/* 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.InvalidValue;
import xxl.core.io.fat.errors.NotFSISector;
import xxl.core.io.fat.util.ByteArrayConversionsLittleEndian;
/**
* This class implements the FSI (File System Info) Sector Structure and Backup Boot Sector.
* On a FAT32 volume, the FAT can be a large data structure, unlike on FAT16 where it is limited
* to a maximum of 128K woth of sectors and FAT12 where it is limited to a maximim of 6K worth
* of sectors. For this reason, a provision is made to store the 'last known' free cluster count
* on the FAT32 volume so that it does not have to be computed as soon as an API call is made to
* ask how much free space there is on the volume. The FSInfo sector number is the value in the
* BPB.FSInfo filed; for Microsoft operating systems it is always set to 1.
*/
public class FSI
{
/**
* The FSInfo sector as byte array.
*/
private byte[] fsInfo = new byte[512];
/**
* Value 0x41615252. This lead signature is used to validate that this is in
* fact an FSInfo sector.
*/
protected final static long LEAD_SIG = 0x41615252;
/**
* Another signature that is more localized in the sector to the location of the
* fields that are used.
*/
protected final static long STRUC_SIG = 0x61417272;
/**
* Value 0xAA550000. This trail signature is used to validate that this is
* in fact an FSInfo sector. Note that the high 2 bytes of this value -
* which go into the bytes at offset 510 and 511 - match the signature
* bytes used at the same offset in sector 0.
*/
protected final static long TRAIL_SIG = 0xAA550000;
/**
* Indicates if the free count value is unknown or not.
*/
protected final static long UNKNOWN_FREE_COUNT = 0xFFFFFFFF;
/**
* Contains the last known free cluster count on the volume. If the value is 0xFFFFFFFF,
* then the free count is unknown and must be computed. Any other value can be used, but
* is not necessarily correct. It should be range checked at least to make sure it is
* <= volume cluster count.
*/
protected long freeCount = UNKNOWN_FREE_COUNT;
/**
* This is a hint for the FAT driver. It indicates the cluster number at
* which the driver should start looking for free clusters. Because a
* FAT32 FAT is large, it can be rather time consuming if there are a
* lot of allocated clusters at the start of the FAT and the driver starts
* looking for free cluster starting at cluster 2. Typically this value is
* set to the last cluster number that the driver allocated. If the value
* is 0xFFFFFFFF, then there is no hint and the driver should start looking
* at cluster 2. Any other value can be used, but should be checked first
* to make sure it is a valid cluster number for the volume.
*/
protected long nextFree = UNKNOWN_FREE_COUNT;
/**
* Object of ByteArrayConversionsLittleEndian. It supports transformations from a
* different number of byte entries to short, int, long and vice versa.
* This transformations can either be for little endian systems or
* for big endian systems.
*/
//protected ByteArrayConversionsLittleEndian ByteArrayConversionsLittleEndian = new ByteArrayConversionsLittleEndianLittleEndian();
/**
* Create an instance of this object.
*/
public FSI()
{
} //end constructor
/**
* Use this method only by an initialization of the file system, i.e.
* when you format your disk.
* @return byte array which represents the FSInfo sector.
*/
public byte[] getFSI()
{
//the value should be 0x41615252
//LeadSig
fsInfo[0] = (byte)0x52;
fsInfo[1] = (byte)0x52;
fsInfo[2] = (byte)0x61;
fsInfo[3] = (byte)0x41;
//reserved for future expansion of FAT -> 0
//Reserved1
for (int i=4; i < 480; i++)
fsInfo[i] = (byte)0;
//StrucSig 0x61417272
fsInfo[484] = (byte)0x72;
fsInfo[485] = (byte)0x72;
fsInfo[486] = (byte)0x41;
fsInfo[487] = (byte)0x61;
//FreeCount, contains the last known free cluster count on the volume.
//If the value is 0xFFFFFFFF, then the free count is unknown and must
//be computed. Any other value can be used, but is not necessarily correct.
//It should be range checked at least to make sure it is <= volume cluster
//count.
fsInfo[488] = (byte)0xFF;
fsInfo[489] = (byte)0xFF;
fsInfo[490] = (byte)0xFF;
fsInfo[491] = (byte)0xFF;
//NxtFree. This is a hint for the FAT driver. It indicates the cluster
//number at which the driver should start looking for free clusters.
//Because a FAT32 FAT is large, it can be rather time consuming if
//there are a lot of allocated clusters at the start of the FAT and the
//driver starts looking for a free cluster starting at cluster 2.
//Typically this value is set to the last cluster number that the driver
//allocated. If the value is 0xFFFFFFFF, then there is no hint and the
//driver should start looking at cluster 2. Any other value can be used,
//but should be checked first to make sure it is a valid cluster number
//for the volume.
nextFree = 2;
fsInfo[492] = (byte)(nextFree); //(byte)0xFF;
fsInfo[493] = (byte)(nextFree >> 8); //(byte)0xFF;
fsInfo[494] = (byte)(nextFree >> 16); //(byte)0xFF;
fsInfo[495] = (byte)(nextFree >> 32); //(byte)0xFF;
//reserved for future expansion of FAT -> 0
//Reserved2
for (int i=496; i < 496+12; i++)
fsInfo[i] = (byte)0;
//TrailSig. Value 0xAA550000. This trail signature is used to validate that
//this is in fact an FSInfo sector. Note that the high 2 bytes of this
//value -- which go into the bytes at offsets 510 and 511 -- match the
//signature bytes used at the same offsets in sector 0.
fsInfo[508] = (byte)0x00;
fsInfo[509] = (byte)0x00;
fsInfo[510] = (byte)0x55;
fsInfo[511] = (byte)0xAA;
return fsInfo;
} //end getFSI()
/**
* Set the fsInfo byte array. If the given fsiSector doesn't contain
* the right signatures or if the length is not equal 512 an exception
* is thrown.
* @param fsiSector the FSInfo Sector.
* @throws NotFSISector in case the three signatures or the length of the array are wrong.
*/
public void initializeFSI(byte[] fsiSector) throws NotFSISector
{
long leadSig = ByteArrayConversionsLittleEndian.convLong(fsiSector[0], fsiSector[1], fsiSector[2], fsiSector[3]);
long strucSig = ByteArrayConversionsLittleEndian.convLong(fsiSector[484], fsiSector[485], fsiSector[486], fsiSector[487]);
long trailSig = ByteArrayConversionsLittleEndian.convLong(fsiSector[508], fsiSector[509], fsiSector[510], fsiSector[511]);
if (leadSig != LEAD_SIG || strucSig != STRUC_SIG || trailSig != trailSig || fsiSector.length != 512)
throw new NotFSISector(fsiSector);
System.arraycopy(fsiSector, 0, fsInfo, 0, fsiSector.length);
freeCount = ByteArrayConversionsLittleEndian.convLong(fsiSector[488], fsiSector[489], fsiSector[490], fsiSector[491]);
nextFree = ByteArrayConversionsLittleEndian.convLong(fsiSector[492], fsiSector[493], fsiSector[494], fsiSector[495]);
} //end initializeFSI(byte[] fsiSector)
/**
* Return the number free clusters.
* @return the number of free clusters.
* @throws InvalidValue if the number of free clusters is unknown.
*/
public long getFreeCount() throws InvalidValue
{
if (freeCount == 0xFFFFFFFF)
throw new InvalidValue("The number of free clusters is unknown.", freeCount);
return freeCount;
} //end getFreeCount()
/**
* Set the number of free clusters to the given value newFreeCount.
* @param newFreeCount is the new free count value.
*/
protected void setFreeCount(long newFreeCount)
{
freeCount = newFreeCount;
} //end setFreeCount(freeCount)
/**
* Decrease the number of free clusters by the given value decFreeCount.
* @param decFreeCount is the value that is substract from freeCount.
*/
protected void decFreeCount(long decFreeCount)
{
freeCount -= decFreeCount;
} //end decFreeCount(freeCount)
/**
* Increase the number of free clusters by the given value incFreeCount.
* @param incFreeCount is the value that is add to freeCount.
*/
protected void incFreeCount(long incFreeCount)
{
freeCount += incFreeCount;
} //end incFreeCount(freeCount)
/**
* Return the next free cluster number.
*
* @return the next free cluster number.
*/
public long getNextFree()
{
//if nextFree eqauls 0xFFFFFFFF the nextFree value is not valid
//and a search has to start at fat-index 2
if (nextFree == 0xFFFFFFFF)
return 2;
return nextFree;
} //end getNextFree()
/**
* Set the next free variable to the given newValue. The next free
* value indicates the file system where to start the search for
* free clusters in the FAT.
* @param newValue the new next free value.
*/
protected void setNextFree(long newValue)
{
nextFree = newValue;
} //end setNextFree(long newValue)
/**
* Return the updated FSI-sector. Use this method to save the changes
* of the FSI-sector after work with the file system.
* @return byte array that represents the FSI.
*/
public byte[] getUsedFSI()
{
//update the fsi with the changed values
//free count
fsInfo[492] = (byte)(freeCount & 0x00000000000000FF);
fsInfo[493] = (byte)((freeCount & 0x000000000000FF00) >> 8);
fsInfo[494] = (byte)((freeCount & 0x0000000000FF0000) >> 16);
fsInfo[495] = (byte)((freeCount & 0x00000000FF000000) >> 24);
//nextFree
fsInfo[492] = (byte)(nextFree);
fsInfo[493] = (byte)(nextFree >> 8);
fsInfo[494] = (byte)(nextFree >> 16);
fsInfo[495] = (byte)(nextFree >> 32);
return fsInfo;
} //end getUsedFSI()
} //end class FSI