/*
* $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;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.log4j.Logger;
import org.jnode.driver.block.BlockDeviceAPI;
import org.jnode.partitions.ibm.IBMPartitionTable;
import org.jnode.util.LittleEndian;
import org.jnode.util.NumberUtils;
/**
* @author gvt
* @author Tango
*/
public class BootSector {
@SuppressWarnings("unused")
private static final Logger log = Logger.getLogger(BootSector.class);
private static final int IFAT12 = 12;
private static final int IFAT16 = 16;
private static final int IFAT32 = 32;
private static final long SZFAT12 = 4085;
private static final long SZFAT16 = 65525;
private static final String SFAT12 = "FAT12";
private static final String SFAT16 = "FAT16";
private static final String SFAT32 = "FAT32";
private byte[] sector;
private int type;
private boolean dirty;
/*
* Common fields, common offsets
*/
private byte[] BS_jmpBoot;
private String BS_OEMName;
private int BPB_BytsPerSec;
private int BPB_SecPerClus;
private int BPB_RsvdSecCnt;
private int BPB_NumFATs;
private int BPB_RootEntCnt;
private int BPB_TotSec16;
private int BPB_Media;
private int BPB_FATSz16;
private int BPB_SecPerTrk;
private int BPB_NumHeads;
private long BPB_HiddSec;
private long BPB_TotSec32;
/*
* FAT32 only fields;
*/
private long BPB_FATSz32;
private int BPB_ExtFlags;
private int BPB_FSVer;
private long BPB_RootClus;
private int BPB_FSInfo;
private int BPB_BkBootSec;
private byte[] BPB_Reserved;
/*
* Common fields, different offsets
*/
private int BS_DrvNum;
private int BS_Reserved1;
private int BS_BootSig;
private long BS_VolID;
private String BS_VolLab;
private String BS_FilSysType;
/*
* computed fields
*/
private long RootDirSectors;
private long FATSz;
private long FirstDataSector;
private long TotSec;
private long DataSec;
private long CountOfClusters;
public BootSector(int size) {
sector = new byte[size];
dirty = false;
}
public BootSector(byte[] sector) {
this.sector = new byte[sector.length];
System.arraycopy(sector, 0, this.sector, 0, sector.length);
decode();
dirty = false;
}
public boolean isaValidBootSector() {
return IBMPartitionTable.containsPartitionTable(sector);
}
public synchronized void read(BlockDeviceAPI device) throws IOException {
device.read(0, ByteBuffer.wrap(sector));
decode();
dirty = false;
}
public synchronized void write(BlockDeviceAPI device, long offset) throws IOException {
//encode();//TODO: Notice here once (Changed Now)
device.write(offset, ByteBuffer.wrap(sector));
dirty = false;
}
private void compute() {
RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec - 1)) / BPB_BytsPerSec;
if (BPB_FATSz16 != 0)
FATSz = BPB_FATSz16;
else
FATSz = BPB_FATSz32;
if (isFat32()) {
FirstDataSector = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors;
} else {
FirstDataSector = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz);
}
if (BPB_TotSec16 != 0)
TotSec = BPB_TotSec16;
else
TotSec = BPB_TotSec32;
DataSec = TotSec - (BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors);
CountOfClusters = DataSec / BPB_SecPerClus;
if (CountOfClusters < SZFAT12)
type = IFAT12;
else if (CountOfClusters < SZFAT16)
type = IFAT16;
else
type = IFAT32;
}
private void decode() {
BS_jmpBoot = getBytes(0, 3);
BS_OEMName = getString(3, 8);
BPB_BytsPerSec = get16(11);
BPB_SecPerClus = get8(13);
BPB_RsvdSecCnt = get16(14);
BPB_NumFATs = get8(16);
BPB_RootEntCnt = get16(17);
BPB_TotSec16 = get16(19);
BPB_Media = get8(21);
BPB_FATSz16 = get16(22);
BPB_SecPerTrk = get16(24);
BPB_NumHeads = get16(26);
BPB_HiddSec = get32(28);
BPB_TotSec32 = get32(32);
if (BPB_FATSz16 == 0)
BPB_FATSz32 = get32(36);
compute();
if (!isFat32()) {
BS_DrvNum = get8(36);
BS_Reserved1 = get8(37);
BS_BootSig = get8(38);
BS_VolID = get32(39);
BS_VolLab = getString(43, 11);
BS_FilSysType = getString(54, 8);
} else {
BPB_ExtFlags = get16(40);
BPB_FSVer = get16(42);
BPB_RootClus = get32(44);
BPB_FSInfo = get16(48);
BPB_BkBootSec = get16(50);
BPB_Reserved = getBytes(52, 12);
BS_DrvNum = get8(64);
BS_Reserved1 = get8(65);
BS_BootSig = get8(66);
BS_VolID = get32(67);
BS_VolLab = getString(71, 11);
BS_FilSysType = getString(82, 8);
}
}
@SuppressWarnings("unused")
private void encode() {
setBytes(0, 3, BS_jmpBoot);
setString(3, 8, BS_OEMName);
set16(11, BPB_BytsPerSec);
set8(13, BPB_SecPerClus);
set16(14, BPB_RsvdSecCnt);
set8(16, BPB_NumFATs);
set16(17, BPB_RootEntCnt);
set16(19, BPB_TotSec16);
set8(21, BPB_Media);
set16(22, BPB_FATSz16);
set16(24, BPB_SecPerTrk);
set16(26, BPB_NumHeads);
set32(28, BPB_HiddSec);
set32(32, BPB_TotSec32);
if (!isFat32()) {
set8(36, BS_DrvNum);
set8(37, BS_Reserved1);
set8(38, BS_BootSig);
set32(39, BS_VolID);
setString(43, 11, BS_VolLab);
setString(54, 8, BS_FilSysType);
} else {
set32(36, BPB_FATSz32);
set16(40, BPB_ExtFlags);
set16(42, BPB_FSVer);
set32(44, BPB_RootClus);
set16(48, BPB_FSInfo);
set16(50, BPB_BkBootSec);
setBytes(52, 12, BPB_Reserved);
set8(64, BS_DrvNum);
set8(65, BS_Reserved1);
set8(66, BS_BootSig);
set32(67, BS_VolID);
setString(71, 11, BS_VolLab);
setString(82, 8, BS_FilSysType);
}
}
protected int get8(int offset) {
return LittleEndian.getUInt8(sector, offset);
}
protected void set8(int offset, int value) {
LittleEndian.setInt8(sector, offset, value);
dirty = true;
}
protected int get16(int offset) {
return LittleEndian.getUInt16(sector, offset);
}
protected void set16(int offset, int value) {
LittleEndian.setInt16(sector, offset, value);
dirty = true;
}
protected long get32(int offset) {
return LittleEndian.getUInt32(sector, offset);
}
protected void set32(int offset, long value) {
LittleEndian.setInt32(sector, offset, (int) value);
dirty = true;
}
protected String getString(int offset, int len) {
StringBuilder b = new StringBuilder(len);
for (int i = 0; i < len; i++) {
int v = sector[offset + i];
b.append((char) v);
}
return b.toString();
}
protected void setString(int offset, int len, String value) {
for (int i = 0; i < len; i++) {
char ch;
if (i < value.length())
ch = value.charAt(i);
else
ch = (char) 0;
LittleEndian.setInt8(sector, offset + i, ch);
}
dirty = true;
}
protected byte[] getBytes(int offset, int len) {
byte[] v = new byte[len];
System.arraycopy(sector, offset, v, 0, len);
return v;
}
protected void setBytes(int offset, int len, byte[] value) {
System.arraycopy(value, 0, sector, offset, len);
dirty = true;
}
public String fatType() {
switch (type) {
case IFAT12:
return SFAT12;
case IFAT16:
return SFAT16;
case IFAT32:
return SFAT32;
default:
return "";
}
}
public boolean isDirty() {
return dirty;
}
public boolean isFat12() {
if (type == IFAT12)
return true;
else
return false;
}
public boolean isFat16() {
if (type == IFAT16)
return true;
else
return false;
}
public boolean isFat32() {
if (type == IFAT32)
return true;
else
return false;
}
/**
* @return the FAT size / type
*/
public int fatSize() {
return type;
}
/**
*
* @return BPB_Media
*/
public int getMediumDescriptor() {
return BPB_Media;
}
public long getSectorsPerFat() {
return FATSz;
}
public int getBytesPerSector() {
return BPB_BytsPerSec;
}
public int getClusterSize() {
return BPB_SecPerClus * BPB_BytsPerSec;
}
public int getSectorsPerCluster() {
return BPB_SecPerClus;
}
public int getNrReservedSectors() {
return BPB_RsvdSecCnt;
}
public int getNrFats() {
return BPB_NumFATs;
}
public long getRootDirectoryStartCluster() {
return BPB_RootClus;
}
public long getCountOfClusters() {
return CountOfClusters;
}
public long getFirstDataSector() {
return FirstDataSector;
}
public long getNrRootDirEntries() {
return BPB_RootEntCnt;
}
public String getVolumeLabel() {
return BS_VolLab;
}
public long getVolumeId() {
return BS_VolID;
}
public String getOEMName() {
return BS_OEMName;
}
/**
* The Setting methods are writing here.
*
*/
public void setBS_JmpBoot(byte[] BS_jmpBoot) {
setBytes(0, 3, BS_jmpBoot);
}
public void setBS_OemName(String BS_OEMName) {
setString(3, 8, BS_OEMName);
}
public void setBPB_BytesPerSector(int BPB_BytsPerSec) {
set16(11, BPB_BytsPerSec);
}
public void setBPB_SecPerCluster(int BPB_SecPerClus) {
set8(13, BPB_SecPerClus);
}
public void setBPB_RsvSecCount(int BPB_RsvdSecCnt) {
set16(14, BPB_RsvdSecCnt);
}
public void setBPB_NoFATs(int BPB_NumFATs) {
set8(16, BPB_NumFATs);
}
public void setBPB_RootEntCnt(int BPB_RootEntCnt) {
set16(17, BPB_RootEntCnt);
}
public void setBPB_TotSec16(int BPB_TotSec16) {
set16(19, BPB_TotSec16);
}
public void setBPB_MediumDescriptor(int BPB_Media) {
set8(21, BPB_Media);
}
public void setBPB_FATSz16(int BPB_FATSz16) {
set16(22, BPB_FATSz16);
}
public void setBPB_SecPerTrk(int BPB_SecPerTrk) {
set16(24, BPB_SecPerTrk);
}
public void setBPB_NumHeads(int BPB_NumHeads) {
set16(26, BPB_NumHeads);
}
public void setBPB_HiddSec(long BPB_HiddSec) {
set32(28, BPB_HiddSec);
}
public void setBPB_TotSec32(long BPB_TotSec32) {
set32(32, BPB_TotSec32);
}
public void setBPB_FATSz32(long BPB_FATSz32) {
set32(36, BPB_FATSz32);
}
public void setBPB_ExtFlags(int BPB_ExtFlags) {
set16(40, BPB_ExtFlags);
}
public void setBPB_FSVer(int BPB_FSVer) {
set16(42, BPB_FSVer);
}
public void setBPB_RootClus(long BPB_RootClus) {
set32(44, BPB_RootClus);
}
public void setBPB_FSInfo(int BPB_FSInfo) {
set16(48, BPB_FSInfo);
}
public void setBPB_BkBootSec(int BPB_BkBootSec) {
set16(50, BPB_BkBootSec);
}
public void setBPB_Reserved(byte[] BPB_Reserved) {
setBytes(52, 12, BPB_Reserved);
}
public void setBS_DrvNum(int BS_DrvNum) {
set8(64, BS_DrvNum);
}
public void setBS_Reserved1(int BS_Reserved1) {
set8(65, BS_Reserved1);
}
public void setBS_BootSig(int BS_BootSig) {
set8(66, BS_BootSig);
}
public void setBS_VolID(long BS_VolID) {
set32(67, BS_VolID);
}
public void setBS_VolLab(String BS_VolLab) {
setString(71, 11, BS_VolLab);
}
public void setBS_FilSysType(String BS_FilSysType) {
setString(82, 8, BS_FilSysType);
}
public void setBS_Identifier(byte[] ident) {
setBytes(510, 2, ident);
}
public String toString() {
StrWriter out = new StrWriter();
out.println("*********************** BootSector *************************");
out.println("fatType\t\t" + fatType());
out.println("isDirty\t\t" + isDirty());
out.println("*************************************************************");
out.println("BS_jmpBoot\t" + NumberUtils.hex(BS_jmpBoot, 0, 3));
out.println("BS_OEMName\t" + BS_OEMName);
out.println("BPB_BytsPerSec\t" + BPB_BytsPerSec);
out.println("BPB_SecPerClus\t" + BPB_SecPerClus);
out.println("BPB_RsvdSecCnt\t" + BPB_RsvdSecCnt);
out.println("BPB_NumFATs\t" + BPB_NumFATs);
out.println("BPB_RootEntCnt\t" + BPB_RootEntCnt);
out.println("BPB_TotSec16\t" + BPB_TotSec16);
out.println("BPB_Media\t" + NumberUtils.hex(BPB_Media, 2));
out.println("BPB_FATSz16\t" + BPB_FATSz16);
out.println("BPB_SecPerTrk\t" + BPB_SecPerTrk);
out.println("BPB_NumHeads\t" + BPB_NumHeads);
out.println("BPB_HiddSec\t" + BPB_HiddSec);
out.println("BPB_TotSec32\t" + BPB_TotSec32);
out.println();
if (isFat32()) {
out.println("BPB_FATSz32\t" + BPB_FATSz32);
out.println("BPB_ExtFlags\t" + NumberUtils.hex(BPB_ExtFlags, 2));
out.println("BPB_FSVer\t" + NumberUtils.hex(BPB_FSVer, 2));
out.println("BPB_RootClus\t" + BPB_RootClus);
out.println("BPB_FSInfo\t" + BPB_FSInfo);
out.println("BPB_BkBootSec\t" + BPB_BkBootSec);
out.println("BPB_Reserved\t" + NumberUtils.hex(BPB_Reserved, 0, 12));
out.println();
}
out.println("BS_DrvNum\t" + NumberUtils.hex(BS_DrvNum, 2));
out.println("BS_Reserved1\t" + NumberUtils.hex(BS_Reserved1, 2));
out.println("BS_BootSig\t" + NumberUtils.hex(BS_BootSig, 2));
out.println("BS_VolID\t" + NumberUtils.hex(BS_VolID, 8));
out.println("BS_VolLab\t" + BS_VolLab);
out.println("BS_FilSysType\t" + BS_FilSysType);
out.println();
out.println("RootDirSectors\t" + RootDirSectors);
out.println("FATSz\t\t" + FATSz);
out.println("FirstDataSector\t" + FirstDataSector);
out.println("TotSec\t\t" + TotSec);
out.println("DataSec\t\t" + DataSec);
out.println("CountOfClusters\t" + CountOfClusters);
out.print("*************************************************************");
return out.toString();
}
}