diff options
Diffstat (limited to 'src/main/java/de/waldheinz/fs/fat/BootSector.java')
-rw-r--r-- | src/main/java/de/waldheinz/fs/fat/BootSector.java | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/src/main/java/de/waldheinz/fs/fat/BootSector.java b/src/main/java/de/waldheinz/fs/fat/BootSector.java new file mode 100644 index 0000000..d1aa398 --- /dev/null +++ b/src/main/java/de/waldheinz/fs/fat/BootSector.java @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2003-2009 JNode.org + * 2009,2010 Matthias Treydte <mt@waldheinz.de> + * + * 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 de.waldheinz.fs.fat; + +import de.waldheinz.fs.BlockDevice; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * The boot sector. + * + * @author Ewout Prangsma <epr at jnode.org> + * @author Matthias Treydte <waldheinz at gmail.com> + */ +public abstract class BootSector extends Sector { + + /** + * Offset to the byte specifying the number of FATs. + * + * @see #getNrFats() + * @see #setNrFats(int) + */ + public static final int FAT_COUNT_OFFSET = 16; + public static final int RESERVED_SECTORS_OFFSET = 14; + + public static final int TOTAL_SECTORS_16_OFFSET = 19; + public static final int TOTAL_SECTORS_32_OFFSET = 32; + + /** + * The length of the file system type string. + * + * @see #getFileSystemType() + */ + public static final int FILE_SYSTEM_TYPE_LENGTH = 8; + + /** + * The offset to the sectors per cluster value stored in a boot sector. + * + * @see #getSectorsPerCluster() + * @see #setSectorsPerCluster(int) + */ + public static final int SECTORS_PER_CLUSTER_OFFSET = 0x0d; + + public static final int EXTENDED_BOOT_SIGNATURE = 0x29; + + /** + * The size of a boot sector in bytes. + */ + public final static int SIZE = 512; + + protected BootSector(BlockDevice device) { + super(device, 0, SIZE); + markDirty(); + } + + public static BootSector read(BlockDevice device) throws IOException { + final ByteBuffer bb = ByteBuffer.allocate(512); + bb.order(ByteOrder.LITTLE_ENDIAN); + device.read(0, bb); + + if ((bb.get(510) & 0xff) != 0x55 || + (bb.get(511) & 0xff) != 0xaa) throw new IOException( + "missing boot sector signature"); + + final byte sectorsPerCluster = bb.get(SECTORS_PER_CLUSTER_OFFSET); + + if (sectorsPerCluster <= 0) throw new IOException( + "suspicious sectors per cluster count " + sectorsPerCluster); + + final int rootDirEntries = bb.getShort( + Fat16BootSector.ROOT_DIR_ENTRIES_OFFSET); + final int rootDirSectors = ((rootDirEntries * 32) + + (device.getSectorSize() - 1)) / device.getSectorSize(); + + final int total16 = + bb.getShort(TOTAL_SECTORS_16_OFFSET) & 0xffff; + final long total32 = + bb.getInt(TOTAL_SECTORS_32_OFFSET) & 0xffffffffl; + + final long totalSectors = total16 == 0 ? total32 : total16; + + final int fatSz16 = + bb.getShort(Fat16BootSector.SECTORS_PER_FAT_OFFSET) & 0xffff; + final long fatSz32 = + bb.getInt(Fat32BootSector.SECTORS_PER_FAT_OFFSET) & 0xffffffffl; + + final long fatSz = fatSz16 == 0 ? fatSz32 : fatSz16; + final int reservedSectors = bb.getShort(RESERVED_SECTORS_OFFSET); + final int fatCount = bb.get(FAT_COUNT_OFFSET); + final long dataSectors = totalSectors - (reservedSectors + + (fatCount * fatSz) + rootDirSectors); + + final long clusterCount = dataSectors / sectorsPerCluster; + + final BootSector result = + (clusterCount > Fat16BootSector.MAX_FAT16_CLUSTERS) ? + new Fat32BootSector(device) : new Fat16BootSector(device); + + result.read(); + return result; + } + + public abstract FatType getFatType(); + + /** + * Gets the number of sectors per FAT. + * + * @return the sectors per FAT + */ + public abstract long getSectorsPerFat(); + + /** + * Sets the number of sectors/fat + * + * @param v the new number of sectors per fat + */ + public abstract void setSectorsPerFat(long v); + + public abstract void setSectorCount(long count); + + public abstract int getRootDirEntryCount(); + + public abstract long getSectorCount(); + + /** + * Returns the offset to the file system type label, as this differs + * between FAT12/16 and FAT32. + * + * @return the offset to the file system type label + */ + public abstract int getFileSystemTypeLabelOffset(); + + public abstract int getExtendedBootSignatureOffset(); + + public void init() throws IOException { + setBytesPerSector(getDevice().getSectorSize()); + setSectorCount(getDevice().getSize() / getDevice().getSectorSize()); + set8(getExtendedBootSignatureOffset(), EXTENDED_BOOT_SIGNATURE); + + /* magic bytes needed by some windows versions to recognize a boot + * sector. these are x86 jump instructions which lead into + * nirvana when executed, but we're currently unable to produce really + * bootable images anyway. So... */ + set8(0x00, 0xeb); + set8(0x01, 0x3c); + set8(0x02, 0x90); + + /* the boot sector signature */ + set8(0x1fe, 0x55); + set8(0x1ff, 0xaa); + } + + /** + * Returns the file system type label string. + * + * @return the file system type string + * @see #setFileSystemTypeLabel(java.lang.String) + * @see #getFileSystemTypeLabelOffset() + * @see #FILE_SYSTEM_TYPE_LENGTH + */ + public String getFileSystemTypeLabel() { + final StringBuilder sb = new StringBuilder(FILE_SYSTEM_TYPE_LENGTH); + + for (int i=0; i < FILE_SYSTEM_TYPE_LENGTH; i++) { + sb.append ((char) get8(getFileSystemTypeLabelOffset() + i)); + } + + return sb.toString(); + } + + /** + * + * + * @param fsType the + * @throws IllegalArgumentException if the length of the specified string + * does not equal {@link #FILE_SYSTEM_TYPE_LENGTH} + */ + public void setFileSystemTypeLabel(String fsType) + throws IllegalArgumentException { + + if (fsType.length() != FILE_SYSTEM_TYPE_LENGTH) { + throw new IllegalArgumentException(); + } + + for (int i=0; i < FILE_SYSTEM_TYPE_LENGTH; i++) { + set8(getFileSystemTypeLabelOffset() + i, fsType.charAt(i)); + } + } + + /** + * Returns the number of clusters that are really needed to cover the + * data-caontaining portion of the file system. + * + * @return the number of clusters usable for user data + * @see #getDataSize() + */ + public final long getDataClusterCount() { + return getDataSize() / getBytesPerCluster(); + } + + /** + * Returns the size of the data-containing portion of the file system. + * + * @return the number of bytes usable for storing user data + */ + private long getDataSize() { + return (getSectorCount() * getBytesPerSector()) - + FatUtils.getFilesOffset(this); + } + + /** + * Gets the OEM name + * + * @return String + */ + public String getOemName() { + StringBuilder b = new StringBuilder(8); + + for (int i = 0; i < 8; i++) { + int v = get8(0x3 + i); + if (v == 0) break; + b.append((char) v); + } + + return b.toString(); + } + + + /** + * Sets the OEM name, must be at most 8 characters long. + * + * @param name the new OEM name + */ + public void setOemName(String name) { + if (name.length() > 8) throw new IllegalArgumentException( + "only 8 characters are allowed"); + + for (int i = 0; i < 8; i++) { + char ch; + if (i < name.length()) { + ch = name.charAt(i); + } else { + ch = (char) 0; + } + + set8(0x3 + i, ch); + } + } + + /** + * Gets the number of bytes/sector + * + * @return int + */ + public int getBytesPerSector() { + return get16(0x0b); + } + + /** + * Sets the number of bytes/sector + * + * @param v the new value for bytes per sector + */ + public void setBytesPerSector(int v) { + if (v == getBytesPerSector()) return; + + switch (v) { + case 512: case 1024: case 2048: case 4096: + set16(0x0b, v); + break; + + default: + throw new IllegalArgumentException(); + } + } + + private static boolean isPowerOfTwo(int n) { + return ((n!=0) && (n&(n-1))==0); + } + + /** + * Returns the number of bytes per cluster, which is calculated from the + * {@link #getSectorsPerCluster() sectors per cluster} and the + * {@link #getBytesPerSector() bytes per sector}. + * + * @return the number of bytes per cluster + */ + public int getBytesPerCluster() { + return this.getSectorsPerCluster() * this.getBytesPerSector(); + } + + /** + * Gets the number of sectors/cluster + * + * @return int + */ + public int getSectorsPerCluster() { + return get8(SECTORS_PER_CLUSTER_OFFSET); + } + + /** + * Sets the number of sectors/cluster + * + * @param v the new number of sectors per cluster + */ + public void setSectorsPerCluster(int v) { + if (v == getSectorsPerCluster()) return; + if (!isPowerOfTwo(v)) throw new IllegalArgumentException( + "value must be a power of two"); + + set8(SECTORS_PER_CLUSTER_OFFSET, v); + } + + /** + * Gets the number of reserved (for bootrecord) sectors + * + * @return int + */ + public int getNrReservedSectors() { + return get16(RESERVED_SECTORS_OFFSET); + } + + /** + * Sets the number of reserved (for bootrecord) sectors + * + * @param v the new number of reserved sectors + */ + public void setNrReservedSectors(int v) { + if (v == getNrReservedSectors()) return; + if (v < 1) throw new IllegalArgumentException( + "there must be >= 1 reserved sectors"); + set16(RESERVED_SECTORS_OFFSET, v); + } + + /** + * Gets the number of fats + * + * @return int + */ + public final int getNrFats() { + return get8(FAT_COUNT_OFFSET); + } + + /** + * Sets the number of fats + * + * @param v the new number of fats + */ + public final void setNrFats(int v) { + if (v == getNrFats()) return; + + set8(FAT_COUNT_OFFSET, v); + } + + /** + * Gets the number of logical sectors + * + * @return int + */ + protected int getNrLogicalSectors() { + return get16(TOTAL_SECTORS_16_OFFSET); + } + + /** + * Sets the number of logical sectors + * + * @param v the new number of logical sectors + */ + protected void setNrLogicalSectors(int v) { + if (v == getNrLogicalSectors()) return; + + set16(TOTAL_SECTORS_16_OFFSET, v); + } + + protected void setNrTotalSectors(long v) { + set32(TOTAL_SECTORS_32_OFFSET, v); + } + + protected long getNrTotalSectors() { + return get32(TOTAL_SECTORS_32_OFFSET); + } + + /** + * Gets the medium descriptor byte + * + * @return int + */ + public int getMediumDescriptor() { + return get8(0x15); + } + + /** + * Sets the medium descriptor byte + * + * @param v the new medium descriptor + */ + public void setMediumDescriptor(int v) { + set8(0x15, v); + } + + /** + * Gets the number of sectors/track + * + * @return int + */ + public int getSectorsPerTrack() { + return get16(0x18); + } + + /** + * Sets the number of sectors/track + * + * @param v the new number of sectors per track + */ + public void setSectorsPerTrack(int v) { + if (v == getSectorsPerTrack()) return; + + set16(0x18, v); + } + + /** + * Gets the number of heads + * + * @return int + */ + public int getNrHeads() { + return get16(0x1a); + } + + /** + * Sets the number of heads + * + * @param v the new number of heads + */ + public void setNrHeads(int v) { + if (v == getNrHeads()) return; + + set16(0x1a, v); + } + + /** + * Gets the number of hidden sectors + * + * @return int + */ + public long getNrHiddenSectors() { + return get32(0x1c); + } + + /** + * Sets the number of hidden sectors + * + * @param v the new number of hidden sectors + */ + public void setNrHiddenSectors(long v) { + if (v == getNrHiddenSectors()) return; + + set32(0x1c, v); + } + + @Override + public String toString() { + StringBuilder res = new StringBuilder(1024); + res.append("Bootsector :\n"); + res.append("oemName="); + res.append(getOemName()); + res.append('\n'); + res.append("medium descriptor = "); + res.append(getMediumDescriptor()); + res.append('\n'); + res.append("Nr heads = "); + res.append(getNrHeads()); + res.append('\n'); + res.append("Sectors per track = "); + res.append(getSectorsPerTrack()); + res.append('\n'); + res.append("Sector per cluster = "); + res.append(getSectorsPerCluster()); + res.append('\n'); + res.append("byte per sector = "); + res.append(getBytesPerSector()); + res.append('\n'); + res.append("Nr fats = "); + res.append(getNrFats()); + res.append('\n'); + res.append("Nr hidden sectors = "); + res.append(getNrHiddenSectors()); + res.append('\n'); + res.append("Nr logical sectors = "); + res.append(getNrLogicalSectors()); + res.append('\n'); + res.append("Nr reserved sector = "); + res.append(getNrReservedSectors()); + res.append('\n'); + + return res.toString(); + } + +} |