/*
* $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.fat;
import java.io.IOException;
import org.jnode.driver.block.BlockDeviceAPI;
/**
* @author epr
*/
public class FatFormatter {
public static final int MAX_DIRECTORY = 512;
public static final int FLOPPY_DESC = 0xf0;
public static final int HD_DESC = 0xf8;
public static final int RAMDISK_DESC = 0xfa;
private BootSector bs;
private Fat fat;
private FatDirectory rootDir;
public static FatFormatter fat144FloppyFormatter(int reservedSectors, BootSector bs) {
return new FatFormatter(FLOPPY_DESC, 512, 1, 2880, 18, 2, FatType.FAT32, 2, 0,
reservedSectors, bs);
}
public static FatFormatter HDFormatter(int bps, int nbTotalSectors, int sectorsPerTrack,
int nbHeads, FatType fatSize, int hiddenSectors, int reservedSectors, BootSector bs) {
return new FatFormatter(HD_DESC, bps,
calculateDefaultSectorsPerCluster(bps, nbTotalSectors), nbTotalSectors,
sectorsPerTrack, nbHeads, fatSize, 2, hiddenSectors, reservedSectors, bs);
}
protected FatFormatter(int mediumDescriptor, int bps, int spc, int nbTotalSectors,
int sectorsPerTrack, int nbHeads, FatType fatSize, int nbFats, int hiddenSectors,
int reservedSectors, BootSector bs) {
this.bs = bs;
final float fatEntrySize = fatSize.getEntrySize();
bs.setMediumDescriptor(mediumDescriptor);
bs.setOemName("JNode1.0");
bs.setBytesPerSector(bps);
bs.setNrReservedSectors(reservedSectors);
bs.setNrRootDirEntries(mediumDescriptor == FLOPPY_DESC ? 224
: calculateDefaultRootDirectorySize(bps, nbTotalSectors));
bs.setNrLogicalSectors(nbTotalSectors);
bs.setSectorsPerFat((Math.round(nbTotalSectors / (spc * (bps / fatEntrySize))) + 1));
bs.setSectorsPerCluster(spc);
bs.setNrFats(2);
bs.setSectorsPerTrack(sectorsPerTrack);
bs.setNrHeads(nbHeads);
bs.setNrHiddenSectors(hiddenSectors);
fat = new Fat(fatSize, mediumDescriptor, bs.getSectorsPerFat(), bs.getBytesPerSector());
fat.setMediumDescriptor(bs.getMediumDescriptor());
rootDir = new FatLfnDirectory(null, bs.getNrRootDirEntries());
}
private static int calculateDefaultSectorsPerCluster(int bps, int nbTotalSectors) {
// Apply the default cluster size from MS
long sizeInMB = (nbTotalSectors * bps) / (1024 * 1024);
int spc;
if (sizeInMB < 32) {
spc = 1;
} else if (sizeInMB < 64) {
spc = 2;
} else if (sizeInMB < 128) {
spc = 4;
} else if (sizeInMB < 256) {
spc = 8;
} else if (sizeInMB < 1024) {
spc = 32;
} else if (sizeInMB < 2048) {
spc = 64;
} else if (sizeInMB < 4096) {
spc = 128;
} else if (sizeInMB < 8192) {
spc = 256;
} else if (sizeInMB < 16384) {
spc = 512;
} else
throw new IllegalArgumentException("Disk too large to be formatted in FAT16");
return spc;
}
private static int calculateDefaultRootDirectorySize(int bps, int nbTotalSectors) {
int totalSize = bps * nbTotalSectors;
// take a default 1/5 of the size for root max
if (totalSize >= MAX_DIRECTORY * 5 * 32) { // ok take the max
return MAX_DIRECTORY;
} else {
return totalSize / (5 * 32);
}
}
/**
* Set the label
*
* @param label
*/
public void setLabel(String label) throws IOException {
rootDir.setLabel(label);
}
/**
* Format the given device, according to my settings
*
* @param api
* @throws IOException
*/
public void format(BlockDeviceAPI api) throws IOException {
bs.write(api);
for (int i = 0; i < bs.getNrFats(); i++) {
fat.write(api, FatUtils.getFatOffset(bs, i));
}
rootDir.write(api, FatUtils.getRootDirOffset(bs));
api.flush();
}
/**
* Returns the bs.
*
* @return BootSector
*/
public BootSector getBootSector() {
return bs;
}
}