/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.io.fat;
import xxl.core.io.fat.errors.InitializationException;
import xxl.core.io.fat.errors.WrongFATType;
import xxl.core.io.fat.errors.WrongLength;
import xxl.core.io.fat.util.ByteArrayConversionsLittleEndian;
import xxl.core.io.fat.util.MyMath;
import xxl.core.util.Arrays;
/**
* This class represents the BIOS Parameter Block. Normally it is located in the first
* sector of the volume in the reserved region. It's assumed that all bytes of the bpb
* on the disk are Little Endian encoded. This means the decimal value 65450 = 0xFFAA
* is stored in the following order: 0xAA, 0xFF. Of course this is true only for numbers
* but not for strings or other byte chains that are not interpreted as numbers.
*/
public class BPB
{
//I assume the following table
//byte = 1 byte
//short = 2 byte
//int = 4 byte
//long = 8 byte
//float = 4 byte
//double = 8 byte
/**
* Jump instruction to boot code. This field has two allowed forms:
* jmpBoot[0] = 0xEB, jmpBoot[1] = 0x??, jmpBoot[2] = 0x90
* and
* jmpBoot[0] = 0xE9, jmpBoot[1] = 0x??, jmpBoot[2] = 0x??
* 0x?? indicates that any 8-bit value is allowed in that byte. What this
* forms is a three-byte Intel x86 unconditional branch (jump)
* instruction that jumps to the start of the operating system bootstrap
* code. This code typically occupies the rest of sector 0 of the volume
* following the BPB and possibly other sectors. Either of these forms
* is acceptable. JmpBoot[0] = 0xEB is the more frequently used
* format.
*/
public short[] jmpBoot = new short[3];
/**
* "MSWIN4.1" There are many misconceptions about this field. It is
* only a name string. Microsoft operating systems don't pay any
* attention to this field. Some FAT drivers do. This is the reason that
* the indicated string, "MSWIN4.1", is the recommended setting,
* because it is the setting least likely to cause compatibility problems.
* If you want to put something else in here, that is your option, but
* the result may be that some FAT drivers might not recognize the
* volume. Typically this is some indication of what system formatted
* the volume.
*/
public String OEMName;
/**
* Count of bytes per sector. This value may take on only the following values:
* 512, 1024, 2048, or 4096. If maximum compatibility with old implementatiosn is
* desired, only the value 512 should be used. There is a lot of FAT code in the
* world that is basically "hard wired" to 512 bytes per sector and doesn't
* bother to check this field to make sure it is 512.
* Note: Do not misinterpret these statements about maximum compatibility. If the
* media being recorded has a physical sector size N, you must use N and this must
* still be less than or equal to 4096. Maximum compatibility is achieved by only
* using media with specific sector sizes.
*/
public int BytsPerSec = 1024;
/**
* Number of sectors per allocation unit. This value must be a power of 2 that is
* greater 0. The legal values are 1, 2, 4, 8, 16, 32, 64, and 128. Note however,
* that a value never should be used, if the "bytes per cluster" value results
* (BPB_BytsPerSec * BPB_SecPerClus) greater than 32K (32 * 1024). There is a
* misconception that values greater than this are OK. Values that cause a cluster
* size greater than 32K bytes do not work properly; do not try to define one. Some
* versions of some systems allow 64K bytes per cluster value. Many application
* setup programs will not work correctly on such a FAT volume.
*/
public short SecPerClus = 4;
/**
* Number of reserved sectors in the Reserved region of the volume starting at the
* first sector of the volume. This field must not be 0. For FAT12 and FAT16 volumes,
* this value should never be anything other than 1. For FAT32 volumes, this value
* is typically 32. There is a lot of FAT code in the world "hard wired" to 1
* reserved sector for FAT12 and FAT16 volumes and that doesn't bother to check this
* field to make sure it is 1. Microsoft operating systems will properly support any
* non-zero value in this field.
*/
public int RsvdSecCnt = 32;
/**
* The count of FAT data structures on the volume. This field should always contain
* the value 2 for any FAT volume of any type. Although any value greater than or
* equal to 1 is perfectly valid, many software programs and a few operating systems
* FAT file system drivers may not function properly if the value is something other
* than 2. All Microsoft file system drivers will support a value other than 2, but
* it is still highly recommended that no value other than 2 be used in this field.
* The reason the standard value for this field is 2 is to provide redundancy for the
* FAT data structure so that if a sector goes bad in one of the FATs, that data is not
* lost because it is duplicated in the other FAT. On non-disk-based media, such as
* FLASH memory cards, where such redundancy is a useless feature, a value of 1 may be
* used to save the space that a second copy of the FAT uses, but some FAT file system
* drivers might not recognize such a volume properly.
*/
public short NumFATs = 2;
/**
* For FAT12 and FAT16 volumes, this field contains the count of 32-byte directory
* entries in the root directory. For FAT32 volumes, this field must be set to 0. For
* FAT12 and FAT16 volumes, this value should always specify a count that when multiplied
* by 32 results in an even multiple of BPB_BytsPerSec. For maximum compatibility, FAT16
* volumes should use the value 512.
*/
public int RootEntCnt = 0;
/**
* This field is the old 16-bit total count of sectors on the volume. This count
* includes the count of all sectors in all four regions of the volume. This field can
* be 0; if it is 0, then BPB_TotSec32 must be non-zero. For FAT32 volumes, this field
* must be 0. For FAT12 and FAT16 volumes, this field contains the sector count, and
* BPB_TotSec32 is 0 if the total sector count "fits" (is less than 0x10000).
* If the total sector count doesn't fits the value is stored in BPB_TotSec32.
*/
public int TotSec16 = 0;
/**
* 0xF8 is the standard value for "fixed" (non-removable) media. For removable media,
* 0xF0 is frequently used. The legal values for this field are 0xF0, 0xF8, 0xF9, 0xFA,
* 0xFB, 0xFC, 0xFD, 0xFE, and 0xFF. The only other important point is that whatever value
* is put in here must also be put in the low byte of the FAT[0] entry. This dates back to
* the old MS-DOS 1.x media determination noted earlier and is no longer usually used for
* anything.
*/
public short Media = (byte)0x00F8;
/**
* This field is the FAT12/FAT16 16-bit count of sectors occupied by ONE FAT. On FAT32
* volumes this field must be 0, and BPB_FATSz32 contains the FAT size count.
*/
public int FATSz16 = 0;
/**
* Sectors per track for interrupt 0x13. This field is only relevant for media that have
* a geometry (volume is broken down into tracks by multiple heads and cylinders) and are
* visible on interrupt 0x13. This field contains the "sectors per track" geometry value.
*/
public int SecPerTrk = 0;
/**
* Number of heads for interrupt 0x13. This field is relevant as discussed earlier for
* BPB_SecPerTrk. This field contains the one based "count of heads". For example, on a
* 1.44 MB 3.5-inch floppy drive this value is 2.
*/
public int NumHeads = 0;
/**
* Count of hidden sectors preceding the partition that contains this FAT volume. This
* field is generally only relevant for media visible on interrupt 0x13. This field should
* always be zero on media that are not partitioned. Exactly what value is appropriate is
* operating system specific.
*/
public long HiddSec = 0;
/**
* This field is the new 32-bit total count of sectors on the volume. This count includes
* the count of all sectors in all four regions of the volume. This field can be 0; if it
* is 0, then BPB_TotSec16 must be non-zero. For FAT32 volumes, this field must be non-zero.
* For FAT12/FAT16 volumes, this field contains the sector count if BPB_TotSec16 is 0 (count
* is greater than or equal to 0x10000).
*/
public long TotSec32 = 0;
//////////////////////////
//// FAT12 & FAT16 ////
//////////////////////////
/**
* Int 0x13 drive number (e.g. 0x80). This field supports MS-DOS
* bootstrap and is set to the INT 0x13 drive number of the media
* (0x00 for floppy disks, 0x80 for hard disks).
* NOTE: This field is actually operating system specific.
*/
public short DrvNum = 0x80;
/**
* Reserved (used by Windows NT). Code that formats FAT volumes
* should always set this byte to 0.
*/
public short Reserved1 = 0;
/**
* Extended boot signature (0x29). This is a signature byte that
* indicates that the following three fields in the boot sector are
* present.
*/
public short BootSig = 0x29;
/**
* Volume serial number. This field, together with BS_VolLab,
* supports volume tracking on removable media. These values allow
* FAT file system drivers to detect that the wrong disk is inserted in a
* removable drive. This ID is usually generated by simply combining
* the current date and time into a 32-bit value.
*/
public long VolID;
/**
* Volume label. This field matches the 11-byte volume label
* recorded in the root directory.
* NOTE: FAT file system drivers should make sure that they update
* this field when the volume label file in the root directory has its
* name changed or created. The setting for this field when there is no
* volume label is the string "NO NAME ".
*/
public String VolLab;
/**
* One of the strings "FAT12 ", "FAT16 ", or "FAT ".
* NOTE: Many people think that the string in this field has
* something to do with the determination of what type of FAT-
* FAT12, FAT16, or FAT32-that the volume has. This is not true.
* You will note from its name that this field is not actually part of the
* BPB. This string is informational only and is not used by Microsoft
* file system drivers to determine FAT type, because it is frequently
* not set correctly or is not present. See the FAT Type Determination
* section of this document. This string should be set based on the
* FAT type though, because some non-Microsoft FAT file system
* drivers do look at it.
*/
public String FilSysType;
//////////////////////
//// FAT32 /////
//////////////////////
/**
* This field is only defined for FAT32 media and does not exist on FAT12 and FAT16 media.
* This field is the FAT32 32-bit count of sectors occupied by ONE FAT. BPB_FATSz16 must be 0.
*/
public long FATSz32 = 0;
/**
* This field is only defined for FAT32 media and does not exist on FAT12 and FAT16 media.
* Bits 0-3 -- Zero-based number of active FAT. Only valid if mirroring is disabled.
* Bits 4-6 -- Reserved.
* Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
* -- 1 means only one FAT is active; it is the one referenced in bits 0-3.
* Bits 8-15 -- Reserved.
*/
public int ExtFlags = 0;
/**
* This field is only defined for FAT32 media and does not exist on FAT12 and FAT16 media.
* High byte is major revision number. Low byte is minor revision number. This is the version
* number of the FAT32 volume. This supports the ability to extend the FAT32 media type in
* the future without worrying about old FAT32 drivers mounting the volume. This document
* defines the version to 0:0. If this field is non-zero, back-level Windows versions will
* not mount the volume. NOTE: Disk utilities should respect this field and not operate on
* volumes with a higher major or minor version number than that for which they were designed
* . FAT32 file system drivers must check this field and not mount the volume if it does not
* contain a version number that was defined at the time the driver was written.
*/
public int FSVer = 0;
/**
* This field is only defined for FAT32 media and does not exist on FAT12 and FAT16 media.
* This is set to the cluster number of the first cluster of the root directory, usually 2
* but not required to be 2. NOTE: Disk utilities that change the location of the root
* directory should make every effort to place the first cluster of the root directory in
* the first non-bad cluster on the drive (i.e., in cluster 2, unless it's marked bad).
* This is specified so that disk repair utilities can easily find the root directory if
* this field accidentally gets zeroed.
*/
public long RootClus = 2;
/**
* This field is only defined for FAT32 media and does not exist on FAT12 and FAT16 media.
* Sector number of FSINFO structure in the reserved area of the FAT32 volume. Usually 1.
* NOTE: There will be a copy of the FSINFO structure in BackupBoot, but only the copy
* pointed to by this field will be kept up to date (i.e., both the primary and backup boot
* record will point to the same FSINFO sector).
*/
public int FSInfo = 1;
/**
* This field is only defined for FAT32 media and does not exist on FAT12 and FAT16 media.
* If non-zero, indicates the sector number in the reserved area of the volume of a copy of
* the boot record. Usually 6. No value other than 6 is recommended.
*/
public int BkBootSec = 6;
/**
* This field is only defined for FAT32 media and does not exist on FAT12 and FAT16 media.
* Reserved for future expansion. Code that formats FAT32 volumes should always set all of
* the bytes of this field to 0.
* Important: The space for this field is 12 byte.
*/
public short[] Reserved = new short[12];
/////////////////////////////////////
// end of specific values of BPB //
/////////////////////////////////////
/**
* Instance of FATDevice.
*/
protected FATDevice device;
/**
* Indicates the type of FAT.
*/
private byte fatType = FAT.UNKNOWN_FAT;
/**
* Number of clusters of FAT, see Microsoft paper.
*/
protected long countOfClusters;
//////////////////////////////////////////////
// for initialization of an empty device //
//////////////////////////////////////////////
/**
* This is the table for FAT16 drives. NOTE that this table includes
* entries for disk sizes larger than 512 MB even though typically
* only the entries for disks smaller than 512 MB in size are used.
* The way this table is accessed is to look for the first entry
* in the table for which the disk size is less than or equal
* to the DiskSize field in that table entry. For this table to
* work properly BPB_RsvdSecCnt must be 1, BPB_NumFATs
* must be 2, and BPB_RootEntCnt must be 512. Any of these values
* being different may require the first table entries DiskSize value
* to be changed otherwise the cluster count may be to low for FAT16.
*/
protected static final DiskSizeToSectorPerCluster[] diskTableFAT16 = new DiskSizeToSectorPerCluster[]
{
new DiskSizeToSectorPerCluster(8400L, (byte)0), //disks up to 4.1 MB, the 0 value for secPerClusVal trips an error
new DiskSizeToSectorPerCluster(32680L, (byte)2), //disks up to 16 MB, 1k cluster
new DiskSizeToSectorPerCluster(262144L, (byte)4), //disks up to 128 MB, 2k cluster
new DiskSizeToSectorPerCluster(524288L, (byte)8), //disks up to 256 MB, 4k cluster
new DiskSizeToSectorPerCluster(1048576L, (byte)16), //disks up to 512 MB, 8k cluster
//the entries after this point are not used unless FAT16 is forced
new DiskSizeToSectorPerCluster(2097152L, (byte)32), //disks up to 1 GB, 16k cluster
new DiskSizeToSectorPerCluster(4194304L, (byte)64), //disks up to 2 GB, 32k cluster
new DiskSizeToSectorPerCluster(0xFFFFFFFFL, (byte)0), //any disks greater than 2 GB, 0 value for secPerClusVal trips an error
};
/**
* This is the table for FAT32 drives. NOTE that this table includes
* entries for disk sizes smaller than 512 MB even though typically
* only the entries for disks >= 512 MB in size are used.
* The way this table is accessed is to look for the first entry
* in the table for which the disk size is less than or equal
* to the DiskSize field in that table entry. For this table to
* work properly BPB_RsvdSecCnt must be 32, and BPB_NumFATs
* must be 2. Any of these values being different may require the first
* table entries DiskSize value to be changed otherwise the cluster count
* may be to low for FAT32.
*/
protected static final DiskSizeToSectorPerCluster[] diskTableFAT32 = new DiskSizeToSectorPerCluster[]
{
new DiskSizeToSectorPerCluster(66600L, (byte)0), //disks up to 32.5 MB, the 0 value for secPerClusVal trips an error
new DiskSizeToSectorPerCluster(532480L, (byte)1), //disks up to 260 MB, 0.5k cluster
new DiskSizeToSectorPerCluster(16777216L, (byte)8), //disks up to 8 GB, 4k cluster
new DiskSizeToSectorPerCluster(33554432L, (byte)16), //disks up to 16 GB, 8k cluster
new DiskSizeToSectorPerCluster(67108864L, (byte)32), //disks up to 32 GB, 16k cluster
new DiskSizeToSectorPerCluster(0xFFFFFFFFL, (byte)64) //disks up to 32 GB, 32k cluster
};
/**
* Structure that contains two values: diskSize and secPerClusVal.
* The structure is used by getFATXXBPB methods for the initialisation
* of a FAT-file-system on disk.
*/
public static class DiskSizeToSectorPerCluster
{
/**The disk size*/
public long diskSize;
/**Sectors per cluster*/
public byte secPerClusVal;
/**Constructs a new DiskSizeToSectorPerCluster object
*
* @param diskSize the disk size
* @param secPerClusVal the sectors per cluster value
* */
public DiskSizeToSectorPerCluster(long diskSize, byte secPerClusVal)
{
this.diskSize = diskSize;
this.secPerClusVal = secPerClusVal;
} //end constructor
} //end inner class DiskSizeToSectorPerCluster
/**
* Create an instance of this object. All variables are
* initialized with the given bpbSector. Use this constructor
* by the BOOT process of the file system (device).
* @param device is a pointer to the device where the bpb is stored.
* @param bpbSector is the byte array representation of the BIOS Parameter Block.
* @throws InitializationException in case this object couldn't be initialized.
*/
public BPB(FATDevice device, byte[] bpbSector) throws InitializationException
{
this.device = device;
fatType = determineFatType(bpbSector);
initializeBPB(bpbSector);
if (FileSystem.debug)
{
System.out.println("\n\tBPB\n");
Arrays.printHexArray(bpbSector,System.out);
}
} //end constructor boot
/**
* Creates an instance of this object. All variables (depending on the
* fat type) will be initialized. The constructor creates a fat type
* and diskSize dependend BIOS Parameter Block. Use this constructor
* by the FORMAT process.
* For a FAT12 file system diskSize must be smaller than 4084 blocks.
* For a FAT16 file system diskSize must be smaller than 4194304
* 512-byte-blocks and bigger than 32680 512-byte-blocks.
* For a FAT32 file system diskSize must be smaller than 0xFFFFFFFF bytes
* and bigger than 532480 512-byte-blocks
*
* @param device the instance of FATDevice.
* @param diskSize is the size of the disk in number of 512 blocks.
* @param fatType is the type of fat that should be formatted.
* @throws WrongLength if the diskSize is to big for the given fatType.
* @throws InitializationException in case this object couldn't be initialized.
*/
public BPB(FATDevice device, long diskSize, byte fatType) throws WrongLength, InitializationException
{
this.device = device;
this.fatType = fatType;
//create a byte array that contains the fat type specific
//information of the bpb
byte[] bpbBlock = null;
if (fatType == FAT.FAT12)
{
if (diskSize > 4084)
throw new WrongLength("The maximum length for this FAT type is exceeded.\n Maximum length is 4084 512-byte-blocks", diskSize);
bpbBlock = getFAT12BPB(diskSize); //62 byte
}
else if (fatType == FAT.FAT16)
{
if (diskSize < 32680)
throw new WrongLength("The minimum length for this FAT type is fell short of.\n Minimum length is 32680 512-byte-blocks", diskSize);
if (diskSize > 4194304L)
throw new WrongLength("The maximum length for this FAT type is exceeded\n. Maximum length is 4194304 512-byte-blocks", diskSize);
bpbBlock = getFAT16BPB(diskSize);
}
else
{
if (diskSize < 532480)
throw new WrongLength("The minimum length for this FAT type is fell short of.\n Minimum length is 532480 512-byte-blocks.", diskSize);
bpbBlock = getFAT32BPB(diskSize);
}
//initialize the variables of this object with the parameters
//of the byte array
try
{
initializeBPB(bpbBlock);
}
catch(Exception e)
{
throw new InitializationException("Couldn't initialze BPB, because of: "+e);
}
//it doesn't matter what kind of fat we have the bpb has always 512 Byte
byte[] dataBlock = new byte[512];
System.arraycopy(bpbBlock, 0, dataBlock, 0, bpbBlock.length);
//last two bytes must have the following value
dataBlock[510] = (byte)0x55;
dataBlock[511] = (byte)0xAA;
//write the BPB to disk
device.writeSector(dataBlock, 0);
//this is done to initialize countOfCluster value.
determineFatType(bpbBlock);
} //end constructor format
/**
* Returns a byte array of 62 entries representing the BPB of a FAT12.
* Initialize also the global variables with the same values used for the byte array.
* Use this method only if you create a file system.
*
* @param diskSize is the size of the disk in number of 512 blocks.
* This size sets the right SecPerClus value.
* @return a byte array of 62 entries representing the BPB of a FAT12.
*/
public byte[] getFAT12BPB(long diskSize)
{
byte[] bpb = new byte[62];
//jmpBoot
bpb[0] = (byte)0xEB; //taken from documentation
bpb[1] = (byte)0x3E; //taken from the real FAT-System (MS-WIN 98 SE)
bpb[2] = (byte)0x90; //taken from documentation
jmpBoot[0] = 0xEB;
jmpBoot[1] = 0x3E;
jmpBoot[2] = 0x90;
//OEMName
bpb[3] = (byte)0x4D;
bpb[4] = (byte)0x53;
bpb[5] = (byte)0x57;
bpb[6] = (byte)0x49;
bpb[7] = (byte)0x4E;
bpb[8] = (byte)0x34;
bpb[9] = (byte)0x2E;
bpb[10] = (byte)0x31;
OEMName = "MSWIN4.1";
//BytsPerSec
bpb[11] = (byte)0x00; //0x0200 = 512
bpb[12] = (byte)0x02;
BytsPerSec = 512;
//SecPerClus
bpb[13] = 1; //taken from a real FAT-System
SecPerClus = 1;
//RsvdSecCnt
bpb[14] = (byte)0x01; //0x0001
bpb[15] = (byte)0x00;
RsvdSecCnt = 1; //must be 1 for FAT12 and FAT16
//NumFATs
bpb[16] = (byte)0x02;
NumFATs = 2; //shoulb always be 2 for any FAT volume
//RootEntCnt
bpb[17] = (byte)0xE0; //taken from real FAT-System
bpb[18] = (byte)0x00;
//should be 512 for FAT16 and 0 for FAT32, in general it must
//specify a count when multiplied by 32 results in an even
//multiple of BPB.BytsPerSec.
RootEntCnt = 224;
//TotSec16
bpb[19] = (byte)(diskSize);
bpb[20] = (byte)(diskSize >> 8);
TotSec16 = (int)diskSize; //2880; //for a floppy
//Media
bpb[21] = (byte)0xF0;
Media = 0xF0; //0xF0 for removable media
//FATSz16
FATSz16 = (int)calculateNumberOfFATSectors(
RootEntCnt,
BytsPerSec,
SecPerClus,
diskSize,
RsvdSecCnt,
NumFATs,
FAT.FAT12
);
bpb[22] = (byte)(FATSz16);
bpb[23] = (byte)(FATSz16 >> 8);
FATSz32 = 0;
//SecPerTrk
bpb[24] = (byte)0x09; //0x0009 ////taken from a real FAT-System
bpb[25] = (byte)0x00;
SecPerTrk = 9;
//NumHeads
bpb[26] = (byte)0x02;
bpb[27] = (byte)0x00;
NumHeads = 2; //for a 1.44MB 3.5-inch floppy
//HiddSec
bpb[28] = (byte)0x00;
bpb[29] = (byte)0x00;
bpb[30] = (byte)0x00;
bpb[31] = (byte)0x00;
HiddSec = 0; //zero for media that are not partitoned. Otherwise it is OS specific
//TotSec32
bpb[32] = (byte)0x00;
bpb[33] = (byte)0x00;
bpb[34] = (byte)0x00;
bpb[35] = (byte)0x00;
TotSec32 = 0; //can be zero if TotSec16 is not zero. For FAT32 it must be non-zero
//DrvNum
bpb[36] = (byte)0x00;
DrvNum = 0; //zero for floppy and 0x80 for hard disk. Important: OS specific
//Reserved1
bpb[37] = (byte)0x00;
Reserved1 = 0;
//BootSig
bpb[38] = (byte)0x29;
BootSig = 0x29; //signature byte
//VolID
bpb[39] = (byte)0x00;
bpb[40] = (byte)0x00;
bpb[41] = (byte)0x00;
bpb[42] = (byte)0x00;
VolID = 0; //not that important yet
//VolLab
bpb[43] = (byte)0x4E;
bpb[44] = (byte)0x4F;
bpb[45] = (byte)0x20;
bpb[46] = (byte)0x4E;
bpb[47] = (byte)0x41;
bpb[48] = (byte)0x4D;
bpb[49] = (byte)0x45;
bpb[50] = (byte)0x20;
bpb[51] = (byte)0x20;
bpb[52] = (byte)0x20;
bpb[53] = (byte)0x20;
VolLab = "NO NAME "; //must be the same as the 11-byte volume label in root directory
//FilSysType
bpb[54] = (byte)0x46;
bpb[55] = (byte)0x41;
bpb[56] = (byte)0x54;
bpb[57] = (byte)0x31;
bpb[58] = (byte)0x32;
bpb[59] = (byte)0x20;
bpb[60] = (byte)0x20;
bpb[61] = (byte)0x20;
FilSysType = "FAT12 ";
return bpb;
} //end getFAT12BPB
/**
* Returns a byte array of 62 entries representing the BPB of a FAT16.
* Initialize also the global variables with the same values used for the byte array.
* Use this method only if you create a file system.
* @param diskSize the size of the disk in blocks (512 byte).
* @return byte array representation of the bpb.
*/
public byte[] getFAT16BPB(long diskSize)
{
byte[] bpb = getFAT12BPB(diskSize);
//SecPerClus: get it from the static table
for (int i=0; i < diskTableFAT16.length; i++)
if (diskTableFAT16[i].diskSize >= diskSize)
{
//set sec per clus
bpb[13] = diskTableFAT16[i].secPerClusVal;
SecPerClus = diskTableFAT16[i].secPerClusVal;
break;
}
bpb[17] = (byte)0x00;//0x0200 = 512
bpb[18] = (byte)0x02;
//should be 512 for FAT16 and 0 for FAT32, in general it must
//specify a count when multiplied by 32 results in an even
//multiple of BPB.BytsPerSec.
RootEntCnt = 512;
//if diskSize doesn't fits in TotSec16 it's stored in TotSec32
if (diskSize >= 0x10000)
{
TotSec32 = diskSize;
bpb[19] = bpb[20] = 0;
bpb[32] = (byte)diskSize;
bpb[33] = (byte)(diskSize >> 8);
bpb[34] = (byte)(diskSize >> 16);
bpb[35] = (byte)(diskSize >> 24);
TotSec16 = 0;
}
bpb[21] = (byte)0xF8;
Media = 0xF8; //0xF8 for non removable media
bpb[58] = 0x36; //change FilSysType to "FAT16 ";
FilSysType = "FAT16 ";
//FATSz16
FATSz16 = (int)calculateNumberOfFATSectors(
RootEntCnt,
BytsPerSec,
SecPerClus,
diskSize,
RsvdSecCnt,
NumFATs,
FAT.FAT16
);
FATSz32 = 0;
bpb[22] = (byte)(FATSz16 & 0x000000FF);
bpb[23] = (byte)((FATSz16 & 0x0000FF00) >> 8);
return bpb;
} //end getFAT16BPB
/**
* Returns a byte array of 90 entries representing the BPB of a FAT32.
* Initializes also the global variables with the same values used for the byte array.
* Use this method only if you create a file system.
*
* @param diskSize the number of sectors of the whole disk.
* @return a byte array of 90 entries representing the BPB of a FAT32.
*/
public byte[] getFAT32BPB(long diskSize)
{
byte[] tmp = getFAT16BPB(diskSize);
byte[] bpb = new byte[90];
System.arraycopy(tmp, 0, bpb, 0, tmp.length);
//SecPerClus: get it from the static table
for (int i=0; i < diskTableFAT32.length; i++)
if (diskTableFAT32[i].diskSize >= diskSize)
{
//set sec per clus
bpb[13] = diskTableFAT32[i].secPerClusVal;
SecPerClus = diskTableFAT32[i].secPerClusVal;
break;
}
bpb[14] = (byte)32;
bpb[15] = (byte)0;
RsvdSecCnt = 32;
//should be 512 for FAT16 and 0 for FAT32, in general it must
//specify a count when multiplied by 32 results in an even
//multiple of BPB.BytsPerSec.
RootEntCnt = 0;
bpb[17] = (byte)RootEntCnt;
bpb[18] = (byte)(RootEntCnt >> 8);
TotSec16 = 0;
bpb[19] = (byte)(TotSec16);
bpb[20] = (byte)(TotSec16 >> 8);
bpb[21] = (byte)0xF8;
Media = 0xF8; //0xF8 for non removable media
FATSz16 = 0;
bpb[22] = (byte)FATSz16;
bpb[23] = (byte)(FATSz16 >> 8);
TotSec32 = diskSize;
bpb[32] = (byte)diskSize;
bpb[33] = (byte)(diskSize >> 8);
bpb[34] = (byte)(diskSize >> 16);
bpb[35] = (byte)(diskSize >> 24);
//FATSz32
FATSz32 = calculateNumberOfFATSectors(
RootEntCnt,
BytsPerSec,
SecPerClus,
diskSize,
RsvdSecCnt,
NumFATs,
FAT.FAT32
);
bpb[36] = (byte)FATSz32;
bpb[37] = (byte)(FATSz32 >> 8);
bpb[38] = (byte)(FATSz32 >> 16);
bpb[39] = (byte)(FATSz32 >> 24);
ExtFlags = 0;
ExtFlags |= 1; //only FAT number 1 is active
ExtFlags |= 0x80; //only one FAT is active
bpb[40] = (byte)ExtFlags;
bpb[41] = (byte)(ExtFlags >> 8);
FSVer = 0; //set version to 0:0
bpb[42] = 0;
bpb[43] = 0;
RootClus = 2; //the first cluster number of the first cluster of the root directory. Usually 2
bpb[44] = (byte)RootClus;
bpb[45] = (byte)(RootClus >> 8);
bpb[46] = (byte)(RootClus >> 16);
bpb[47] = (byte)(RootClus >> 24);
FSInfo = 1; //sector number of the FSINFO structure in the reserved area of the volume
bpb[48] = (byte)FSInfo;
bpb[49] = (byte)(FSInfo >> 8);
BkBootSec = 6; //indicates the sector number in the reserved area of the volume of a copy of the boot record
//No value other than 6 is recommended
bpb[50] = (byte)BkBootSec;
bpb[51] = (byte)(BkBootSec >> 8);
Reserved[0] = 0;
for (int i=52; i < 12; i++)
bpb[i] = 0;
//DrvNum is the same as for FAT12 or FAT16, only an other offset in BPB
bpb[64] = (byte)DrvNum;
//Reserved1 is the same as for FAT12 or FAT16, only an other offset in BPB
bpb[65] = (byte)Reserved1;
//BootSig is the same as for FAT12 or FAT16, only an other offset in BPB
bpb[66] = (byte)BootSig;
//VolID is the same as for FAT12 or FAT16, only an other offset in BPB
bpb[67] = (byte)VolID;
bpb[68] = (byte)(VolID >> 8);
bpb[69] = (byte)(VolID >> 16);
bpb[70] = (byte)(VolID >> 24);
//VolLab is the same as for FAT12 or FAT16, only an other offset in BPB
bpb[71] = (byte)0x4E;
bpb[72] = (byte)0x4F;
bpb[73] = (byte)0x20;
bpb[74] = (byte)0x4E;
bpb[75] = (byte)0x41;
bpb[76] = (byte)0x4D;
bpb[77] = (byte)0x45;
bpb[78] = (byte)0x20;
bpb[79] = (byte)0x20;
bpb[80] = (byte)0x20;
bpb[81] = (byte)0x20;
//FilSysType
bpb[85] = 0x33; //change FilSysType to "FAT32 ";
bpb[86] = 0x32; //change FilSysType to "FAT32 ";
FilSysType = "FAT32 ";
return bpb;
} //end getFAT32BPB
/**
* Calculate the number of FAT sectors that are reserved for one FAT.
* @param rootEntCount the number of entries of the root directory.
* @param bytsPerSec the number of bytes for one sector.
* @param secPerClus the number of sectors for one cluster.
* @param diskSize the number of sectors of the whole disk.
* @param reservedSecCount the number of reserved sectors of the disk.
* @param numFats the number of FAT's.
* @param fatType the type of FAT.
* @return the number of FAT sectors for one FAT.
*/
private long calculateNumberOfFATSectors(
int rootEntCount,
int bytsPerSec,
int secPerClus,
long diskSize,
int reservedSecCount,
int numFats,
byte fatType
)
{
//for nore information about the following calculation see the
//Microsoft Paper.
long rootDirSectors = ((rootEntCount * 32) + (bytsPerSec - 1)) / bytsPerSec;
long tmpVal1 = diskSize - (reservedSecCount + rootDirSectors);
//since there are no information about what to do for FAT12
//I developed this part on my own.
if (fatType == FAT.FAT12)
return MyMath.roundUp(((tmpVal1 + 2.0) / (8.0 * bytsPerSec)) * 12.0);
long tmpVal2 = (256 * secPerClus) + numFats;
if (fatType == FAT.FAT32)
tmpVal2 = tmpVal2 / 2;
return (tmpVal1 + (tmpVal2 - 1)) / tmpVal2;
} //end calcualteNumberOfFATSectors(...)
/**
* Initialize the global variables. Depending on the length of the byte array bpb
* the variables will be initialized. If the length is 62 it is a BPB of FAT12 or
* FAT16. If the length is 90 it is a BPB of FAT32. Only the variables of the
* specific BPB are correct all others are undefined.
*
* @param bpb an array of bytes
* @throws InitializationException in case of an initialization error.
*/
protected void initializeBPB(byte[] bpb) throws InitializationException
{
//jmpBoot
jmpBoot[0] = ByteArrayConversionsLittleEndian.convShort(bpb[0]);
jmpBoot[1] = ByteArrayConversionsLittleEndian.convShort(bpb[1]);
jmpBoot[2] = ByteArrayConversionsLittleEndian.convShort(bpb[2]);
OEMName = ByteArrayConversionsLittleEndian.byteToString(bpb, 3, 3+8);
BytsPerSec = ByteArrayConversionsLittleEndian.convInt(bpb[11], bpb[12]);
SecPerClus = ByteArrayConversionsLittleEndian.convShort(bpb[13]);
RsvdSecCnt = ByteArrayConversionsLittleEndian.convInt(bpb[14], bpb[15]);
NumFATs = ByteArrayConversionsLittleEndian.convShort(bpb[16]);
RootEntCnt = ByteArrayConversionsLittleEndian.convInt(bpb[17], bpb[18]);
TotSec16 = ByteArrayConversionsLittleEndian.convInt(bpb[19], bpb[20]);
Media = ByteArrayConversionsLittleEndian.convShort(bpb[21]);
FATSz16 = ByteArrayConversionsLittleEndian.convInt(bpb[22], bpb[23]);
SecPerTrk = ByteArrayConversionsLittleEndian.convInt(bpb[24], bpb[25]);
NumHeads = ByteArrayConversionsLittleEndian.convInt(bpb[26], bpb[27]);
HiddSec = ByteArrayConversionsLittleEndian.convLong(bpb[28], bpb[29], bpb[30], bpb[31]);
TotSec32 = ByteArrayConversionsLittleEndian.convLong(bpb[32], bpb[33], bpb[34], bpb[35]);
if (fatType == FAT.FAT12 || fatType == FAT.FAT16)
{
DrvNum = ByteArrayConversionsLittleEndian.convShort(bpb[36]);
Reserved1 = ByteArrayConversionsLittleEndian.convShort(bpb[37]);
BootSig = ByteArrayConversionsLittleEndian.convShort(bpb[38]);
VolID = ByteArrayConversionsLittleEndian.convLong(bpb[39], bpb[40], bpb[41], bpb[42]);
VolLab = ByteArrayConversionsLittleEndian.byteToString(bpb, 43, 43+11);
FilSysType = ByteArrayConversionsLittleEndian.byteToString(bpb, 54, 54+8);
}
else if (fatType == FAT.FAT32)
{
FATSz32 = ByteArrayConversionsLittleEndian.convLong(bpb[36], bpb[37], bpb[38], bpb[39]);
ExtFlags = ByteArrayConversionsLittleEndian.convInt(bpb[40], bpb[41]);
FSVer = ByteArrayConversionsLittleEndian.convInt(bpb[42], bpb[43]);
RootClus = ByteArrayConversionsLittleEndian.convLong(bpb[44], bpb[45], bpb[46], bpb[47]);
FSInfo = ByteArrayConversionsLittleEndian.convInt(bpb[48], bpb[49]);
BkBootSec = ByteArrayConversionsLittleEndian.convInt(bpb[50], bpb[51]);
for (int i=0; i < 12; i++)
Reserved[i] = ByteArrayConversionsLittleEndian.convShort(bpb[i+52]);
DrvNum = ByteArrayConversionsLittleEndian.convShort(bpb[64]);
Reserved1 = ByteArrayConversionsLittleEndian.convShort(bpb[65]);
BootSig = ByteArrayConversionsLittleEndian.convShort(bpb[66]);
VolID = ByteArrayConversionsLittleEndian.convLong(bpb[67], bpb[68], bpb[69], bpb[70]);
VolLab = ByteArrayConversionsLittleEndian.byteToString(bpb, 71, 71+11);
FilSysType = ByteArrayConversionsLittleEndian.byteToString(bpb, 82, 82+8);
}
else
{
throw new InitializationException("Couldn't initialize BPB, because of: Wrong fat type in initializeBPB().");
}
} //end initializeBPB(byte[] bpb)
/**
* Return the sector number of the FSI structure. This value is
* only valid if the fat type is FAT32. An exception is thrown
* if this method is used by an other FAT type.
* @return the sector number of the FSInfo.
* @throws WrongFATType in case the FAT type is not FAT32.
*/
public int getFSInfoSectorNumber() throws WrongFATType
{
if (fatType == FAT.FAT32)
return FSInfo;
else
throw new WrongFATType(fatType, FAT.FAT32);
} //end getFSInfoSectorNumber()
/**
* Calculate the number of sectors of the root directory.
* @return number of sectors of root directory
*/
public int getRootDirSectors()
{
return ((RootEntCnt * 32) + (BytsPerSec - 1)) / BytsPerSec;
} //end getRootDirSectors()
/**
* Calculate the first sector number which contains the data of the files.
* @return the first data sector number
*/
public long getFirstDataSector()
{
long FATSz = 0;
if (FATSz16 != 0)
FATSz = FATSz16;
else
FATSz = FATSz32;
return RsvdSecCnt + (NumFATs * FATSz) + getRootDirSectors();
} //end getFirstDataSector(int rootDirSectors)
/**
* Return the first sector number of the root directory.
* @return the first root directory sector number.
*/
public long getFirstRootDirSecNum()
{
if (fatType == FAT.FAT12 || fatType == FAT.FAT16)
return RsvdSecCnt + (NumFATs * FATSz16);
else
return RootClus;
} //end getFirstRootDirSecNum(byte fatType)
/**
* Return the number of fat sectors.
* @return the number of fat sectors.
*/
public long getNumFatSectors()
{
if (FATSz16 != 0)
return FATSz16;
else
return FATSz32;
} //end getNumFatSectors()
/**
* Return the number of data sectors.
* @return number of data sectors.
*/
public long getNumDataSectors()
{
long totSec = 0;
long FATSz = 0;
if (FATSz16 != 0)
FATSz = FATSz16;
else
FATSz = FATSz32;
//determine FAT type. This is done by counting the clusters
if (TotSec16 != 0)
totSec = TotSec16;
else
totSec = TotSec32;
return totSec - (RsvdSecCnt + (NumFATs * FATSz) + getRootDirSectors());
} //end getNumDataSectors()
/**
* Return the last possible cluster number that is accessible by the
* file system. It might be that the real last cluster number is bigger
* than the value return from this method but it's not allowed to access
* this cluster. See Microsoft paper for further information.
* It' assumed that BPB is initialized.
* @return last accessible cluster number.
*/
public long getLastFatCluster()
{
return countOfClusters + 1;
} //end getLastFatCluster()
/**
* Determine the fat type which depends on given bpb byte array.
* @param bpb the BIOS Parameter Block.
* @return the fat type.
*/
private byte determineFatType(byte[] bpb)
{
int BytsPerSec = ByteArrayConversionsLittleEndian.convInt(bpb[11], bpb[12]);
short SecPerClus = ByteArrayConversionsLittleEndian.convShort(bpb[13]);
int RsvdSecCnt = ByteArrayConversionsLittleEndian.convInt(bpb[14], bpb[15]);
short NumFATs = ByteArrayConversionsLittleEndian.convShort(bpb[16]);
int RootEntCnt = ByteArrayConversionsLittleEndian.convInt(bpb[17], bpb[18]);
long TotSec16 = ByteArrayConversionsLittleEndian.convInt(bpb[19], bpb[20]);
long TotSec32 = ByteArrayConversionsLittleEndian.convLong(bpb[32], bpb[33], bpb[34], bpb[35]);
long FATSz16 = ByteArrayConversionsLittleEndian.convInt(bpb[22], bpb[23]);
long FATSz32 = ByteArrayConversionsLittleEndian.convLong(bpb[36], bpb[37], bpb[38], bpb[39]);
long rootDirSectors = ((RootEntCnt * 32) + (BytsPerSec - 1)) / BytsPerSec;
long totSec = 0;
long FATSz = 0;
if (FATSz16 != 0)
FATSz = FATSz16;
else
FATSz = FATSz32;
//determine FAT type. This is done by counting the clusters
if (TotSec16 != 0)
totSec = TotSec16;
else
totSec = TotSec32;
long dataSec = totSec - (RsvdSecCnt + (NumFATs * FATSz) + rootDirSectors);
//the value of count of clusters is exactly the count of data clusters starting
//at cluster 2.
countOfClusters = dataSec / SecPerClus; //computation rounds down
byte fatType = 0;
if (countOfClusters < 4085)
{
//Volume is FAT12
fatType = FAT.FAT12;
}
else if (countOfClusters < 65525)
{
//Volume is FAT16
fatType = FAT.FAT16;
}
else
{
//Volume is FAT32
fatType = FAT.FAT32;
}
return fatType;
} //end determineFatType(byte[] bpb)
/**
* Return the fat type.
* @return the fat type.
*/
public byte getFatType()
{
return fatType;
} //end getFatType()
/**
* Return true if the FAT is mirrored at runtime. This method
* should be used only in case of a FAT32 file system.
* @return true if the FAT is mirrored at runtime.
* @throws WrongFATType if this method is used in case of FAT12 or FAT16.
*/
public boolean isFAT32Mirrored() throws WrongFATType
{
if (fatType != FAT.FAT32)
throw new WrongFATType(fatType, FAT.FAT32);
return (ExtFlags & 0x80) == 0;
} //end isFAT32Mirrored()
/**
* Use this method only if mirroring of FAT32 is disabled and
* in case of a FAT32 file system.
* Return the zero-based number of active FAT.
* @return the zero-based number of active FAT.
* @throws WrongFATType if this method is used in case of FAT12 or FAT16.
*/
public int getActiveFAT32Number() throws WrongFATType
{
if (fatType != FAT.FAT32)
throw new WrongFATType(fatType, FAT.FAT32);
return ExtFlags & 0x0F;
} //end getActiveFAT32Number()
/**
* Print the values of the BPB-Structure.
*
* @return the values of the BPB-Structure as a string
*/
@Override
public String toString()
{
String res = "";
res += "FAT shared\n";
res += "==========\n";
res += "OEMName: "+OEMName+"\n";
res += "BytsPerSec: "+BytsPerSec+"\n";
res += "SecPerClus: "+SecPerClus+"\n";
res += "RsvdSecCnt: "+RsvdSecCnt+"\n";
res += "NumFATs: "+NumFATs+"\n";
res += "RootEntCnt: "+RootEntCnt+"\n";
res += "TotSec16: "+TotSec16+"\n";
res += "Media: "+Media+"\n";
res += "FatSz16: "+FATSz16+"\n";
res += "SecPerTrk: "+SecPerTrk+"\n";
res += "NumHeads: "+NumHeads+"\n";
res += "HiddSec: "+HiddSec+"\n";
res += "TotSec32: "+TotSec32+"\n";
res += "\n";
res += "FAT12 / FAT16 specific"+"\n";
res += "======================"+"\n";
res += "DrvNum: "+DrvNum+"\n";
res += "Reserved1: "+Reserved1+"\n";
res += "BootSig: "+BootSig+"\n";
res += "VolID: "+VolID+"\n";
res += "VolLab: "+VolLab+"\n";
res += "FilSysType: "+FilSysType+"\n";
res += "\n";
res += "FAT32"+"\n";
res += "====="+"\n";
res += "FATSz32: "+FATSz32+"\n";
res += "ExtFlags: "+ExtFlags+"\n";
res += "FSVer: "+FSVer+"\n";
res += "RootClus: "+RootClus+"\n";
res += "FSInfo: "+FSInfo+"\n";
res += "BkBootSec: "+BkBootSec+"\n";
res += "Reserved: "+Reserved+"\n";
res += "DrvNum: "+DrvNum+"\n";
res += "Reserved1: "+Reserved1+"\n";
res += "BootSig: "+BootSig+"\n";
res += "VolID: "+VolID+"\n";
res += "VolLab: "+VolLab+"\n";
res += "FilSysType: "+FilSysType+"\n";
res += "\n";
res += "Some other worthy information:"+"\n";
res += "==============================="+"\n";
String fatType;
if (getFatType() == FAT.FAT12)
fatType = "FAT12";
else if (getFatType() == FAT.FAT16)
fatType = "FAT16";
else if (getFatType() == FAT.FAT32)
fatType = "FAT32";
else
fatType = "UNKNOWN";
res += "fatType "+fatType+"\n";
res += "last fat cluster "+getLastFatCluster()+"\n";
res += "num data sectors "+getNumDataSectors()+"\n";
res += "num fat sectors "+getNumFatSectors()+"\n";
res += "first root directory sector number "+getFirstRootDirSecNum(/*getFatType()*/)+"\n";
res += "first data sector "+getFirstDataSector()+"\n";
res += "number of root directory sectors "+getRootDirSectors()+"\n";
return res;
} //end toString()
} //end class BPB