/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* 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 2.1 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.fs.jfat;
/*
* $Id: FatFormatter.java 2007-07-26 +0100 (s,26 JULY 2007) Tanmoy Deb $
*
* JNode.org
* Copyright (C) 2003-2006 JNode.org
*
* 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 2.1 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import static java.lang.Integer.toHexString;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Date;
import org.apache.log4j.Logger;
import org.jnode.driver.block.BlockDeviceAPI;
import org.jnode.util.LittleEndian;
/**
* <p>
* According to the FAT32 Documents.
* <ul>
* <li>FAT32 disk structure:</li>
* <li>Sector 0 Boot Sector</li>
* <li>Sector 1 FSInfo</li>
* <li>Sector 2 More boot code</li>
* <li>Sector 3 unused</li>
* <li>Sector 4 unused</li>
* <li>Sector 5 unused</li>
* <li>Sector 6 Backup boot sector</li>
* <li>Sector 7 Backup FSInfo sector</li>
* <li>Sector 8 Backup 'more boot code</li>
* <li>Reserved sectors up to the FAT at sector 32</li>
* </ul>
*
* @author tango
*
*/
public class FatFormatter {
private static final Logger log = Logger.getLogger(FatFormatter.class);
/** The Device Identifier for Floppy Device */
public static final int FLOPPY_DESC = 0xf0;
/** The Device Identifier for Hard Disk Device */
public static final int HD_DESC = 0xf8;
/** The Device Identifier for RAM Disk Device */
public static final int RAMDISK_DESC = 0xfa;
/** The Size of Fat. */
private int FatSize;
/** The Number of the Fat in system. */
public final int NumOfFATs = 2;
/** The boot sector backing up. */
public final int BackupBootSector = 6;
/** The number of reserved sectors. */
public final int ReservedSectorCount = 32;
/** The Tracks Per cylinder. */
private static final int NB_HEADS = 255;
/** The sector per track. */
private static final int SECTOR_PER_TRACK = 63;
/** The volume label. */
private static final String VOL_LABEL = "NO NAME ";
/** The FAT version label. */
private static final String FAT_LABEL = "FAT32 ";
/** The Identifier of the Boot Sector */
public final byte[] BS_Identifier = {(byte) 0x55, (byte) 0xAA};
/** The First 3 Bytes of the BootSector */
public final byte[] BS_jmpBoot = {(byte) 0xEB, (byte) 0x5A, (byte) 0x90};
/**
* This lead signature is used to validate that this is in fact an FSInfo
* sector.
*/
private final int FSI_LeadSig = 0x41615252;
/**
* The signature that is more localized in the sector to the location of the
* fields that are used.
*/
private final int FSI_StrucSig = 0x61417272;
/**
* 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.
*/
private int FSI_FreeCount = 0xffffffff;
/**
* This is a hint for the FAT driver. It indicates the cluster number at
* which the driver should start looking for free clusters.
*/
private final int FSI_Nxt_Free = 3; // A confusion here but OK now for
// setting the Info at 3rd sec
/**
* This trail signature is used to validate that this is in fact an FSInfo
* sector
*/
private final int FSI_TrailSig = 0xaa550000;
/** The media ID at the reserved cluster one. */
private final int ReservedSector_0 = 0x0ffffff8;
/** The End Of Chain ID at the reserved sector second. */
private final int ReservedSector_1 = 0xffffffff;
/** The End of cluster chain of the root directory. */
private final int ReservedSector_2 = 0x0fffffff;
/** The array for the reserved sector. */
private byte[] reservedSector;
/**
* The Hard Disk's formatting logic implementation by JFAT. This Version
* only support to the Hard Disks.
*
* @throws IOException
*/
public static FatFormatter HDDFormatter(int sectorSize, int nbTotalSectors,
ClusterSize clusterSize, int hiddenSectors, BlockDeviceAPI api) throws IOException {
return new FatFormatter(HD_DESC, sectorSize, nbTotalSectors, SECTOR_PER_TRACK, NB_HEADS,
clusterSize, hiddenSectors, api);
}
/**
* The Constructor for the HDD devices in the JNode system.
*
* @throws IOException
*
*/
public FatFormatter(int mediumDescriptor, int sectorSize, int nbTotalSectors,
int sectorsPerTrack, int nbHeads, ClusterSize ClusterSize, int hiddenSectors,
BlockDeviceAPI api) throws IOException {
FatFsInfo fsInfo = new FatFsInfo(sectorSize);
BootSector bs = new BootSector(sectorSize);
api.flush();
int DiskSize = getDiskSize(nbTotalSectors, sectorSize);
int SectorPerCluster = get_spc(ClusterSize, sectorSize);
int UserAreaSize =
getUserAreaSize(nbTotalSectors, ReservedSectorCount, NumOfFATs, ClusterSize);
this.FatSize =
getFATSizeSectors(nbTotalSectors, ReservedSectorCount, SectorPerCluster, NumOfFATs,
sectorSize);
// fill out the boot sector and fs info
bs.setBS_JmpBoot(this.BS_jmpBoot);
bs.setBS_OemName("MSWIN4.1");
bs.setBPB_BytesPerSector(sectorSize);
bs.setBPB_SecPerCluster(SectorPerCluster); // look FATformat source
bs.setBPB_RsvSecCount(this.ReservedSectorCount);
bs.setBPB_NoFATs(NumOfFATs);
bs.setBPB_RootEntCnt(0);
bs.setBPB_TotSec16(0);
bs.setBPB_MediumDescriptor(mediumDescriptor);
bs.setBPB_FATSz16(0);
bs.setBPB_SecPerTrk(SECTOR_PER_TRACK);
bs.setBPB_NumHeads(NB_HEADS);
bs.setBPB_HiddSec(hiddenSectors);
bs.setBPB_TotSec32(nbTotalSectors);
// For FAT16/32 only
bs.setBPB_FATSz32(FatSize);
bs.setBPB_ExtFlags(0);
bs.setBPB_FSVer(0);
bs.setBPB_RootClus(2);
bs.setBPB_FSInfo(1);
bs.setBPB_BkBootSec(BackupBootSector);
bs.setBS_DrvNum(0x80);
bs.setBS_Reserved1(0);
bs.setBS_BootSig(0x29); // 0x29 if the next three bits are OK.
int VolumeID = getDriveSerialNumber();
bs.setBS_VolID(VolumeID);
bs.setBS_VolLab(VOL_LABEL);
bs.setBS_FilSysType(FAT_LABEL);
bs.setBS_Identifier(BS_Identifier);
// Keeping the FSInfos
fsInfo.setFsInfo_LeadSig(FSI_LeadSig);
fsInfo.setFsInfo_Reserved1();
fsInfo.setFsInfo_StrucSig(FSI_StrucSig);
FSI_FreeCount = (UserAreaSize / SectorPerCluster) - 1;
fsInfo.setFsInfo_FreeCount(FSI_FreeCount);
fsInfo.setFsInfo_NextFree(FSI_Nxt_Free);
fsInfo.setReserve2();
fsInfo.setFsInfo_TrailSig(FSI_TrailSig);
/**
* TODO:This portion need modofication for the Disk Size Handlings Not
* So mandatory now .WIll look into it. Work out the Cluster Count
*/
long FatNeeded = UserAreaSize / SectorPerCluster;
/**
* check for a cluster count of >2^28, since the upper 4 bits of the
* cluster values in the FAT are reserved
*/
if (FatNeeded > Math.pow(2, 28)) {
log
.error("This drive has more than 2^28 clusters, try to specify a larger cluster size\n");
}
/**
* Once zero_sectors has run, any data on the drive is basically
* lost.... First zero out ReservedSect + FatSize * NumFats +
* SectorsPerCluster
*
*/
int SystemAreaSize = (ReservedSectorCount + (NumOfFATs * FatSize) + SectorPerCluster);
log.info("Clearing out " + SystemAreaSize +
" sectors for Reserved sectors, fats and root cluster...\n");
/** Disk Freeing */
try {
setQuickSectorFree(SystemAreaSize, api);
} catch (IOException e1) {
log.info("Error ocured during Disk Free.");
}
/** calling the Format method */
try {
log.info("Initialising reserved sectors and FATs....");
format(api, bs, fsInfo, SectorPerCluster, nbTotalSectors);
} catch (IOException e) {
log.error("Problems in Disk Formatting.");
}
/**
* The mkjfat informations group
*/
int DiskSizeGB = DiskSize / (1000 * 1000 * 1000);
log.info("Size(Bytes): " + DiskSize + "\tSize(GB): " + DiskSizeGB + "\tSectors :" +
nbTotalSectors);
log.info("Volume ID is :" + toHexString(VolumeID >> 16) + ":" +
toHexString(VolumeID & 0xffff));
log.info(sectorSize + " Bytes Per Sector , Cluster Size " + ClusterSize + " bytes.");
log.info(ReservedSectorCount + " Reserved Sectors ," + (FatSize) + " Sectors Per FAT ," +
NumOfFATs + " FATs.");
log.info("Total Clusters :" + (UserAreaSize / SectorPerCluster));
log.info("Free Clusters: " + FSI_FreeCount);
// Flushing the DeviceAPI
api.flush();
}
/**
* Format the given device, according to my settings
*
* @param api
* @throws IOException
*/
public void format(BlockDeviceAPI api, BootSector bs, FatFsInfo fsInfo, int sectorPerCluster,
int nbTotalSectors) throws IOException {
log.info("The Formatting...\n");
/**
* Now we should write the boot sector and fsinfo twice, once at 0 and
* once at the backup boot sect position
*/
for (int i = 0; i < 2; i++) {
int SectorStart = (i == 0) ? 0 : (BackupBootSector * 512);
bs.write(api, (long) SectorStart); // Write the BootSector
fsInfo.write(api, (long) SectorStart + 512);
}
/** Write the first fat sector in the right places */
for (int i = 0; i < NumOfFATs; i++) {
int SectorStart = (ReservedSectorCount + (i * FatSize)) * 512;
this.reservedSector = new byte[12];
LittleEndian.setInt32(this.reservedSector, 0, ReservedSector_0);
LittleEndian.setInt32(this.reservedSector, 4, ReservedSector_1);
LittleEndian.setInt32(this.reservedSector, 8, ReservedSector_2);
api.write(SectorStart, ByteBuffer.wrap(this.reservedSector));
}
}
/**
* The Method for Disk Free Operations.
*
* @param systemAreaSize
* @param api
* @throws IOException
*/
private void setQuickSectorFree(int systemAreaSize, BlockDeviceAPI api) throws IOException {
byte[] reserveArray = new byte[512];
for (int i = 0; i < systemAreaSize; i++) {
api.write(i * 512, ByteBuffer.wrap(reserveArray));
}
}
/**
* This is for Quick Disk Free Operation TODO:Need to test with it.
*
* @param TotalSectors
* @param api
* @throws IOException
*/
@SuppressWarnings("unused")
private void setQuickDiskFree(int TotalSectors, BlockDeviceAPI api) throws IOException {
// TODO:For Total Disk Formatting
}
/**
* @param clusterSize
* @param BytesPerSector
* @return
*/
private static int get_spc(ClusterSize clusterSize, int BytesPerSector) {
return (clusterSize.getSize() / BytesPerSector);
}
/**
* The method for getting the Serial Number of the Drive.
*/
public int getDriveSerialNumber() {
Date date = new Date();
int year = FatUtils.getYear(date.getTime());
int month = FatUtils.getMonth((int) date.getTime());
int day = FatUtils.getDay((int) date.getTime());
int hour = FatUtils.getHours((int) date.getTime());
int minute = FatUtils.getMinutes((int) date.getTime());
int second = FatUtils.getSeconds((int) date.getTime());
int milliSecond = (int) FatUtils.getMilliSeconds(date.getTime());
int low = day + (month << 8);
int tmp = (milliSecond / 10) + (second << 8);
low += tmp;
int high = minute + (hour << 8);
high += year;
int serialNo = low + (high << 16);
return serialNo;
}
/**
* Get the FAT size sectors.
*/
public int getFATSizeSectors(int TotalSectorNumber, int ReservedSecCnt, int SecPerClus,
int NumFATs, int BytesPerSect) {
long Numerator, Denominator;
long FATElementSize = 4;
long FATsz;
Numerator = FATElementSize * (TotalSectorNumber - ReservedSecCnt);
Denominator = (SecPerClus * BytesPerSect) + (FATElementSize * NumFATs);
FATsz = Numerator / Denominator;
// round up
FATsz += 1;
return ((int) FATsz);
}
/**
* @param TotalSectors
* @param ReservedSectorCount
* @param NumFATs
* @param FATSize
* @return
*/
private int getUserAreaSize(int TotalSectors, int ReservedSectorCount, int NumFATs,
ClusterSize FATSize) {
int UserAreaSize = TotalSectors - ReservedSectorCount - (NumFATs * FATSize.getSize());
return (UserAreaSize);
}
/**
* The Total Disk Size
* @param TotalSectors
* @param sectorSize
* @return
*/
private int getDiskSize(int TotalSectors, int sectorSize) {
return (TotalSectors * sectorSize);
}
}