/* * $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 java.nio.ByteBuffer; import org.jnode.driver.block.BlockDeviceAPI; import org.jnode.driver.block.Geometry; import org.jnode.driver.block.GeometryException; import org.jnode.partitions.ibm.IBMPartitionTable; import org.jnode.partitions.ibm.IBMPartitionTableEntry; import org.jnode.partitions.ibm.IBMPartitionTypes; import org.jnode.util.LittleEndian; import org.jnode.util.NumberUtils; /** * @author epr */ public class BootSector { private byte[] data; private boolean dirty; private final IBMPartitionTableEntry[] partitions; public BootSector(int size) { data = new byte[size]; dirty = false; partitions = new IBMPartitionTableEntry[4]; } public BootSector(byte[] src) { data = new byte[src.length]; System.arraycopy(src, 0, data, 0, src.length); dirty = false; partitions = new IBMPartitionTableEntry[4]; } public boolean isaValidBootSector() { return IBMPartitionTable.containsPartitionTable(data); } /** * Read the contents of this bootsector from the given device. * * @param device */ public synchronized void read(BlockDeviceAPI device) throws IOException { device.read(0, ByteBuffer.wrap(data)); dirty = false; } /** * Write the contents of this bootsector to the given device. * * @param device */ public synchronized void write(BlockDeviceAPI device) throws IOException { device.write(0, ByteBuffer.wrap(data)); dirty = false; } /** * Gets the OEM name * * @return String */ public String getOemName() { StringBuilder b = new StringBuilder(8); for (int i = 0; i < 8; i++) { int v = data[0x3 + i]; b.append((char) v); } return b.toString(); } /** * Sets the OEM name */ public void setOemName(String name) { 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 */ public void setBytesPerSector(int v) { set16(0x0b, v); } /** * Gets the number of sectors/cluster * * @return int */ public int getSectorsPerCluster() { return get8(0x0d); } /** * Sets the number of sectors/cluster */ public void setSectorsPerCluster(int v) { set8(0x0d, v); } /** * Gets the number of reserved (for bootrecord) sectors * * @return int */ public int getNrReservedSectors() { return get16(0xe); } /** * Sets the number of reserved (for bootrecord) sectors */ public void setNrReservedSectors(int v) { set16(0xe, v); } /** * Gets the number of fats * * @return int */ public int getNrFats() { return get8(0x10); } /** * Sets the number of fats */ public void setNrFats(int v) { set8(0x10, v); } /** * Gets the number of entries in the root directory * * @return int */ public int getNrRootDirEntries() { return get16(0x11); } /** * Sets the number of entries in the root directory */ public void setNrRootDirEntries(int v) { set16(0x11, v); } /** * Gets the number of logical sectors * * @return int */ public int getNrLogicalSectors() { return get16(0x13); } /** * Sets the number of logical sectors */ public void setNrLogicalSectors(int v) { set16(0x13, v); } /** * Gets the medium descriptor byte * * @return int */ public int getMediumDescriptor() { return get8(0x15); } /** * Sets the medium descriptor byte */ public void setMediumDescriptor(int v) { set8(0x15, v); } /** * Gets the number of sectors/fat * * @return int */ public int getSectorsPerFat() { return get16(0x16); } /** * Sets the number of sectors/fat */ public void setSectorsPerFat(int v) { set16(0x16, v); } /** * Gets the number of sectors/track * * @return int */ public int getSectorsPerTrack() { return get16(0x18); } /** * Sets the number of sectors/track */ public void setSectorsPerTrack(int v) { set16(0x18, v); } /** * Gets the number of heads * * @return int */ public int getNrHeads() { return get16(0x1a); } /** * Sets the number of heads */ public void setNrHeads(int v) { set16(0x1a, v); } /** * Gets the number of hidden sectors * * @return int */ public int getNrHiddenSectors() { return get16(0x1c); } /** * Sets the number of hidden sectors */ public void setNrHiddenSectors(int v) { set16(0x1c, v); } /** * Gets an unsigned 8-bit byte from a given offset * * @param offset * @return int */ protected int get8(int offset) { return LittleEndian.getUInt8(data, offset); } /** * Sets an unsigned 8-bit byte at a given offset * * @param offset */ protected void set8(int offset, int value) { LittleEndian.setInt8(data, offset, value); dirty = true; } /** * Gets an unsigned 16-bit word from a given offset * * @param offset * @return int */ protected int get16(int offset) { return LittleEndian.getUInt16(data, offset); } /** * Sets an unsigned 16-bit word at a given offset * * @param offset */ protected void set16(int offset, int value) { LittleEndian.setInt16(data, offset, value); dirty = true; } /** * Gets an unsigned 32-bit word from a given offset * * @param offset * @return int */ protected long get32(int offset) { return LittleEndian.getUInt32(data, offset); } /** * Sets an unsigned 32-bit word at a given offset * * @param offset */ protected void set32(int offset, long value) { LittleEndian.setInt32(data, offset, (int) value); dirty = true; } /** * Returns the dirty. * * @return boolean */ public boolean isDirty() { return dirty; } public int getNbPartitions() { return partitions.length; } public IBMPartitionTableEntry initPartitions(Geometry geom, IBMPartitionTypes firstPartitionType) throws GeometryException { getPartition(0).clear(); getPartition(1).clear(); getPartition(2).clear(); getPartition(3).clear(); IBMPartitionTableEntry entry = getPartition(0); entry.setBootIndicator(true); entry.setStartLba(1); entry.setNrSectors(geom.getTotalSectors() - 1); entry.setSystemIndicator(firstPartitionType); entry.setStartCHS(geom.getCHS(entry.getStartLba())); entry.setEndCHS(geom.getCHS(entry.getStartLba() + entry.getNrSectors() - 1)); return entry; } public synchronized IBMPartitionTableEntry getPartition(int partNr) { if (partitions[partNr] == null) { partitions[partNr] = new IBMPartitionTableEntry(null, data, partNr); } return partitions[partNr]; } 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("Sectors per fat = "); res.append(getSectorsPerFat()); 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'); res.append("Nr Root Dir Entries = "); res.append(getNrRootDirEntries()); res.append('\n'); for (int i = 0; i < data.length / 16; i++) { res.append(Integer.toHexString(i)); res.append('-'); res.append(NumberUtils.hex(data, i * 16, 16)); res.append('\n'); } return res.toString(); } }