Home | History | Annotate | Download | only in fat
      1 /*
      2  * Copyright (C) 2003-2009 JNode.org
      3  *               2009,2010 Matthias Treydte <mt (at) waldheinz.de>
      4  *
      5  * This library is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU Lesser General Public License as published
      7  * by the Free Software Foundation; either version 2.1 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful, but
     11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     12  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
     13  * License for more details.
     14  *
     15  * You should have received a copy of the GNU Lesser General Public License
     16  * along with this library; If not, write to the Free Software Foundation, Inc.,
     17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     18  */
     19 
     20 package de.waldheinz.fs.fat;
     21 
     22 import de.waldheinz.fs.BlockDevice;
     23 import java.io.IOException;
     24 import java.nio.ByteBuffer;
     25 import java.nio.ByteOrder;
     26 
     27 /**
     28  * The boot sector.
     29  *
     30  * @author Ewout Prangsma &lt;epr at jnode.org&gt;
     31  * @author Matthias Treydte &lt;waldheinz at gmail.com&gt;
     32  */
     33 public abstract class BootSector extends Sector {
     34 
     35     /**
     36      * Offset to the byte specifying the number of FATs.
     37      *
     38      * @see #getNrFats()
     39      * @see #setNrFats(int)
     40      */
     41     public static final int FAT_COUNT_OFFSET = 16;
     42     public static final int RESERVED_SECTORS_OFFSET = 14;
     43 
     44     public static final int TOTAL_SECTORS_16_OFFSET = 19;
     45     public static final int TOTAL_SECTORS_32_OFFSET = 32;
     46 
     47     /**
     48      * The length of the file system type string.
     49      *
     50      * @see #getFileSystemType()
     51      */
     52     public static final int FILE_SYSTEM_TYPE_LENGTH = 8;
     53 
     54     /**
     55      * The offset to the sectors per cluster value stored in a boot sector.
     56      *
     57      * @see #getSectorsPerCluster()
     58      * @see #setSectorsPerCluster(int)
     59      */
     60     public static final int SECTORS_PER_CLUSTER_OFFSET = 0x0d;
     61 
     62     public static final int EXTENDED_BOOT_SIGNATURE = 0x29;
     63 
     64     /**
     65      * The size of a boot sector in bytes.
     66      */
     67     public final static int SIZE = 512;
     68 
     69     protected BootSector(BlockDevice device) {
     70         super(device, 0, SIZE);
     71         markDirty();
     72     }
     73 
     74     public static BootSector read(BlockDevice device) throws IOException {
     75         final ByteBuffer bb = ByteBuffer.allocate(512);
     76         bb.order(ByteOrder.LITTLE_ENDIAN);
     77         device.read(0, bb);
     78 
     79         if ((bb.get(510) & 0xff) != 0x55 ||
     80                 (bb.get(511) & 0xff) != 0xaa) throw new IOException(
     81                 "missing boot sector signature");
     82 
     83         final byte sectorsPerCluster = bb.get(SECTORS_PER_CLUSTER_OFFSET);
     84 
     85         if (sectorsPerCluster <= 0) throw new IOException(
     86                 "suspicious sectors per cluster count " + sectorsPerCluster);
     87 
     88         final int rootDirEntries = bb.getShort(
     89                 Fat16BootSector.ROOT_DIR_ENTRIES_OFFSET);
     90         final int rootDirSectors = ((rootDirEntries * 32) +
     91                 (device.getSectorSize() - 1)) / device.getSectorSize();
     92 
     93         final int total16 =
     94                 bb.getShort(TOTAL_SECTORS_16_OFFSET) & 0xffff;
     95         final long total32 =
     96                 bb.getInt(TOTAL_SECTORS_32_OFFSET) & 0xffffffffl;
     97 
     98         final long totalSectors = total16 == 0 ? total32 : total16;
     99 
    100         final int fatSz16 =
    101                 bb.getShort(Fat16BootSector.SECTORS_PER_FAT_OFFSET)  & 0xffff;
    102         final long fatSz32 =
    103                 bb.getInt(Fat32BootSector.SECTORS_PER_FAT_OFFSET) & 0xffffffffl;
    104 
    105         final long fatSz = fatSz16 == 0 ? fatSz32 : fatSz16;
    106         final int reservedSectors = bb.getShort(RESERVED_SECTORS_OFFSET);
    107         final int fatCount = bb.get(FAT_COUNT_OFFSET);
    108         final long dataSectors = totalSectors - (reservedSectors +
    109                 (fatCount * fatSz) + rootDirSectors);
    110 
    111         final long clusterCount = dataSectors / sectorsPerCluster;
    112 
    113         final BootSector result =
    114                 (clusterCount > Fat16BootSector.MAX_FAT16_CLUSTERS) ?
    115             new Fat32BootSector(device) : new Fat16BootSector(device);
    116 
    117         result.read();
    118         return result;
    119     }
    120 
    121     public abstract FatType getFatType();
    122 
    123     /**
    124      * Gets the number of sectors per FAT.
    125      *
    126      * @return the sectors per FAT
    127      */
    128     public abstract long getSectorsPerFat();
    129 
    130     /**
    131      * Sets the number of sectors/fat
    132      *
    133      * @param v  the new number of sectors per fat
    134      */
    135     public abstract void setSectorsPerFat(long v);
    136 
    137     public abstract void setSectorCount(long count);
    138 
    139     public abstract int getRootDirEntryCount();
    140 
    141     public abstract long getSectorCount();
    142 
    143     /**
    144      * Returns the offset to the file system type label, as this differs
    145      * between FAT12/16 and FAT32.
    146      *
    147      * @return the offset to the file system type label
    148      */
    149     public abstract int getFileSystemTypeLabelOffset();
    150 
    151     public abstract int getExtendedBootSignatureOffset();
    152 
    153     public void init() throws IOException {
    154         setBytesPerSector(getDevice().getSectorSize());
    155         setSectorCount(getDevice().getSize() / getDevice().getSectorSize());
    156         set8(getExtendedBootSignatureOffset(), EXTENDED_BOOT_SIGNATURE);
    157 
    158         /* magic bytes needed by some windows versions to recognize a boot
    159          * sector. these are x86 jump instructions which lead into
    160          * nirvana when executed, but we're currently unable to produce really
    161          * bootable images anyway. So... */
    162         set8(0x00, 0xeb);
    163         set8(0x01, 0x3c);
    164         set8(0x02, 0x90);
    165 
    166         /* the boot sector signature */
    167         set8(0x1fe, 0x55);
    168         set8(0x1ff, 0xaa);
    169     }
    170 
    171     /**
    172      * Returns the file system type label string.
    173      *
    174      * @return the file system type string
    175      * @see #setFileSystemTypeLabel(java.lang.String)
    176      * @see #getFileSystemTypeLabelOffset()
    177      * @see #FILE_SYSTEM_TYPE_LENGTH
    178      */
    179     public String getFileSystemTypeLabel() {
    180         final StringBuilder sb = new StringBuilder(FILE_SYSTEM_TYPE_LENGTH);
    181 
    182         for (int i=0; i < FILE_SYSTEM_TYPE_LENGTH; i++) {
    183             sb.append ((char) get8(getFileSystemTypeLabelOffset() + i));
    184         }
    185 
    186         return sb.toString();
    187     }
    188 
    189     /**
    190      *
    191      *
    192      * @param fsType the
    193      * @throws IllegalArgumentException if the length of the specified string
    194      *      does not equal {@link #FILE_SYSTEM_TYPE_LENGTH}
    195      */
    196     public void setFileSystemTypeLabel(String fsType)
    197             throws IllegalArgumentException {
    198 
    199         if (fsType.length() != FILE_SYSTEM_TYPE_LENGTH) {
    200             throw new IllegalArgumentException();
    201         }
    202 
    203         for (int i=0; i < FILE_SYSTEM_TYPE_LENGTH; i++) {
    204             set8(getFileSystemTypeLabelOffset() + i, fsType.charAt(i));
    205         }
    206     }
    207 
    208     /**
    209      * Returns the number of clusters that are really needed to cover the
    210      * data-caontaining portion of the file system.
    211      *
    212      * @return the number of clusters usable for user data
    213      * @see #getDataSize()
    214      */
    215     public final long getDataClusterCount() {
    216         return getDataSize() / getBytesPerCluster();
    217     }
    218 
    219     /**
    220      * Returns the size of the data-containing portion of the file system.
    221      *
    222      * @return the number of bytes usable for storing user data
    223      */
    224     private long getDataSize() {
    225         return (getSectorCount() * getBytesPerSector()) -
    226                 FatUtils.getFilesOffset(this);
    227     }
    228 
    229     /**
    230      * Gets the OEM name
    231      *
    232      * @return String
    233      */
    234     public String getOemName() {
    235         StringBuilder b = new StringBuilder(8);
    236 
    237         for (int i = 0; i < 8; i++) {
    238             int v = get8(0x3 + i);
    239             if (v == 0) break;
    240             b.append((char) v);
    241         }
    242 
    243         return b.toString();
    244     }
    245 
    246 
    247     /**
    248      * Sets the OEM name, must be at most 8 characters long.
    249      *
    250      * @param name the new OEM name
    251      */
    252     public void setOemName(String name) {
    253         if (name.length() > 8) throw new IllegalArgumentException(
    254                 "only 8 characters are allowed");
    255 
    256         for (int i = 0; i < 8; i++) {
    257             char ch;
    258             if (i < name.length()) {
    259                 ch = name.charAt(i);
    260             } else {
    261                 ch = (char) 0;
    262             }
    263 
    264             set8(0x3 + i, ch);
    265         }
    266     }
    267 
    268     /**
    269      * Gets the number of bytes/sector
    270      *
    271      * @return int
    272      */
    273     public int getBytesPerSector() {
    274         return get16(0x0b);
    275     }
    276 
    277     /**
    278      * Sets the number of bytes/sector
    279      *
    280      * @param v the new value for bytes per sector
    281      */
    282     public void setBytesPerSector(int v) {
    283         if (v == getBytesPerSector()) return;
    284 
    285         switch (v) {
    286             case 512: case 1024: case 2048: case 4096:
    287                 set16(0x0b, v);
    288                 break;
    289 
    290             default:
    291                 throw new IllegalArgumentException();
    292         }
    293     }
    294 
    295     private static boolean isPowerOfTwo(int n) {
    296         return ((n!=0) && (n&(n-1))==0);
    297     }
    298 
    299     /**
    300      * Returns the number of bytes per cluster, which is calculated from the
    301      * {@link #getSectorsPerCluster() sectors per cluster} and the
    302      * {@link #getBytesPerSector() bytes per sector}.
    303      *
    304      * @return the number of bytes per cluster
    305      */
    306     public int getBytesPerCluster() {
    307         return this.getSectorsPerCluster() * this.getBytesPerSector();
    308     }
    309 
    310     /**
    311      * Gets the number of sectors/cluster
    312      *
    313      * @return int
    314      */
    315     public int getSectorsPerCluster() {
    316         return get8(SECTORS_PER_CLUSTER_OFFSET);
    317     }
    318 
    319     /**
    320      * Sets the number of sectors/cluster
    321      *
    322      * @param v the new number of sectors per cluster
    323      */
    324     public void setSectorsPerCluster(int v) {
    325         if (v == getSectorsPerCluster()) return;
    326         if (!isPowerOfTwo(v)) throw new IllegalArgumentException(
    327                 "value must be a power of two");
    328 
    329         set8(SECTORS_PER_CLUSTER_OFFSET, v);
    330     }
    331 
    332     /**
    333      * Gets the number of reserved (for bootrecord) sectors
    334      *
    335      * @return int
    336      */
    337     public int getNrReservedSectors() {
    338         return get16(RESERVED_SECTORS_OFFSET);
    339     }
    340 
    341     /**
    342      * Sets the number of reserved (for bootrecord) sectors
    343      *
    344      * @param v the new number of reserved sectors
    345      */
    346     public void setNrReservedSectors(int v) {
    347         if (v == getNrReservedSectors()) return;
    348         if (v < 1) throw new IllegalArgumentException(
    349                 "there must be >= 1 reserved sectors");
    350         set16(RESERVED_SECTORS_OFFSET, v);
    351     }
    352 
    353     /**
    354      * Gets the number of fats
    355      *
    356      * @return int
    357      */
    358     public final int getNrFats() {
    359         return get8(FAT_COUNT_OFFSET);
    360     }
    361 
    362     /**
    363      * Sets the number of fats
    364      *
    365      * @param v the new number of fats
    366      */
    367     public final void setNrFats(int v) {
    368         if (v == getNrFats()) return;
    369 
    370         set8(FAT_COUNT_OFFSET, v);
    371     }
    372 
    373     /**
    374      * Gets the number of logical sectors
    375      *
    376      * @return int
    377      */
    378     protected int getNrLogicalSectors() {
    379         return get16(TOTAL_SECTORS_16_OFFSET);
    380     }
    381 
    382     /**
    383      * Sets the number of logical sectors
    384      *
    385      * @param v the new number of logical sectors
    386      */
    387     protected void setNrLogicalSectors(int v) {
    388         if (v == getNrLogicalSectors()) return;
    389 
    390         set16(TOTAL_SECTORS_16_OFFSET, v);
    391     }
    392 
    393     protected void setNrTotalSectors(long v) {
    394         set32(TOTAL_SECTORS_32_OFFSET, v);
    395     }
    396 
    397     protected long getNrTotalSectors() {
    398         return get32(TOTAL_SECTORS_32_OFFSET);
    399     }
    400 
    401     /**
    402      * Gets the medium descriptor byte
    403      *
    404      * @return int
    405      */
    406     public int getMediumDescriptor() {
    407         return get8(0x15);
    408     }
    409 
    410     /**
    411      * Sets the medium descriptor byte
    412      *
    413      * @param v the new medium descriptor
    414      */
    415     public void setMediumDescriptor(int v) {
    416         set8(0x15, v);
    417     }
    418 
    419     /**
    420      * Gets the number of sectors/track
    421      *
    422      * @return int
    423      */
    424     public int getSectorsPerTrack() {
    425         return get16(0x18);
    426     }
    427 
    428     /**
    429      * Sets the number of sectors/track
    430      *
    431      * @param v the new number of sectors per track
    432      */
    433     public void setSectorsPerTrack(int v) {
    434         if (v == getSectorsPerTrack()) return;
    435 
    436         set16(0x18, v);
    437     }
    438 
    439     /**
    440      * Gets the number of heads
    441      *
    442      * @return int
    443      */
    444     public int getNrHeads() {
    445         return get16(0x1a);
    446     }
    447 
    448     /**
    449      * Sets the number of heads
    450      *
    451      * @param v the new number of heads
    452      */
    453     public void setNrHeads(int v) {
    454         if (v == getNrHeads()) return;
    455 
    456         set16(0x1a, v);
    457     }
    458 
    459     /**
    460      * Gets the number of hidden sectors
    461      *
    462      * @return int
    463      */
    464     public long getNrHiddenSectors() {
    465         return get32(0x1c);
    466     }
    467 
    468     /**
    469      * Sets the number of hidden sectors
    470      *
    471      * @param v the new number of hidden sectors
    472      */
    473     public void setNrHiddenSectors(long v) {
    474         if (v == getNrHiddenSectors()) return;
    475 
    476         set32(0x1c, v);
    477     }
    478 
    479     @Override
    480     public String toString() {
    481         StringBuilder res = new StringBuilder(1024);
    482         res.append("Bootsector :\n");
    483         res.append("oemName=");
    484         res.append(getOemName());
    485         res.append('\n');
    486         res.append("medium descriptor = ");
    487         res.append(getMediumDescriptor());
    488         res.append('\n');
    489         res.append("Nr heads = ");
    490         res.append(getNrHeads());
    491         res.append('\n');
    492         res.append("Sectors per track = ");
    493         res.append(getSectorsPerTrack());
    494         res.append('\n');
    495         res.append("Sector per cluster = ");
    496         res.append(getSectorsPerCluster());
    497         res.append('\n');
    498         res.append("byte per sector = ");
    499         res.append(getBytesPerSector());
    500         res.append('\n');
    501         res.append("Nr fats = ");
    502         res.append(getNrFats());
    503         res.append('\n');
    504         res.append("Nr hidden sectors = ");
    505         res.append(getNrHiddenSectors());
    506         res.append('\n');
    507         res.append("Nr logical sectors = ");
    508         res.append(getNrLogicalSectors());
    509         res.append('\n');
    510         res.append("Nr reserved sector = ");
    511         res.append(getNrReservedSectors());
    512         res.append('\n');
    513 
    514         return res.toString();
    515     }
    516 
    517 }
    518