Home | History | Annotate | Download | only in fat
      1 /*
      2  * Copyright (C) 2009,2010 Matthias Treydte <mt (at) waldheinz.de>
      3  *
      4  * This library is free software; you can redistribute it and/or modify it
      5  * under the terms of the GNU Lesser General Public License as published
      6  * by the Free Software Foundation; either version 2.1 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
     12  * License for more details.
     13  *
     14  * You should have received a copy of the GNU Lesser General Public License
     15  * along with this library; If not, write to the Free Software Foundation, Inc.,
     16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     17  */
     18 
     19 package de.waldheinz.fs.fat;
     20 
     21 import de.waldheinz.fs.BlockDevice;
     22 import java.io.IOException;
     23 
     24 /**
     25  * The boot sector layout as used by the FAT12 / FAT16 variants.
     26  *
     27  * @author Matthias Treydte &lt;matthias.treydte at meetwise.com&gt;
     28  */
     29 final class Fat16BootSector extends BootSector {
     30 
     31     /**
     32      * The default number of entries for the root directory.
     33      *
     34      * @see #getRootDirEntryCount()
     35      * @see #setRootDirEntryCount(int)
     36      */
     37     public static final int DEFAULT_ROOT_DIR_ENTRY_COUNT = 512;
     38 
     39     /**
     40      * The default volume label.
     41      */
     42     public static final String DEFAULT_VOLUME_LABEL = "NO NAME"; //NOI18N
     43 
     44     /**
     45      * The maximum number of clusters for a FAT12 file system. This is actually
     46      * the number of clusters where mkdosfs stop complaining about a FAT16
     47      * partition having not enough sectors, so it would be misinterpreted
     48      * as FAT12 without special handling.
     49      *
     50      * @see #getNrLogicalSectors()
     51      */
     52     public static final int MAX_FAT12_CLUSTERS = 4084;
     53 
     54     public static final int MAX_FAT16_CLUSTERS = 65524;
     55 
     56     /**
     57      * The offset to the sectors per FAT value.
     58      */
     59     public static final int SECTORS_PER_FAT_OFFSET = 0x16;
     60 
     61     /**
     62      * The offset to the root directory entry count value.
     63      *
     64      * @see #getRootDirEntryCount()
     65      * @see #setRootDirEntryCount(int)
     66      */
     67     public static final int ROOT_DIR_ENTRIES_OFFSET = 0x11;
     68 
     69     /**
     70      * The offset to the first byte of the volume label.
     71      */
     72     public static final int VOLUME_LABEL_OFFSET = 0x2b;
     73 
     74     /**
     75      * Offset to the FAT file system type string.
     76      *
     77      * @see #getFileSystemType()
     78      */
     79     public static final int FILE_SYSTEM_TYPE_OFFSET = 0x36;
     80 
     81     /**
     82      * The maximum length of the volume label.
     83      */
     84     public static final int MAX_VOLUME_LABEL_LENGTH = 11;
     85 
     86     public static final int EXTENDED_BOOT_SIGNATURE_OFFSET = 0x26;
     87 
     88     /**
     89      * Creates a new {@code Fat16BootSector} for the specified device.
     90      *
     91      * @param device the {@code BlockDevice} holding the boot sector
     92      */
     93     public Fat16BootSector(BlockDevice device) {
     94         super(device);
     95     }
     96 
     97     /**
     98      * Returns the volume label that is stored in this boot sector.
     99      *
    100      * @return the volume label
    101      */
    102     public String getVolumeLabel() {
    103         final StringBuilder sb = new StringBuilder();
    104 
    105         for (int i=0; i < MAX_VOLUME_LABEL_LENGTH; i++) {
    106             final char c = (char) get8(VOLUME_LABEL_OFFSET + i);
    107 
    108             if (c != 0) {
    109                 sb.append(c);
    110             } else {
    111                 break;
    112             }
    113         }
    114 
    115         return sb.toString();
    116     }
    117 
    118     /**
    119      * Sets the volume label that is stored in this boot sector.
    120      *
    121      * @param label the new volume label
    122      * @throws IllegalArgumentException if the specified label is longer
    123      *      than {@link #MAX_VOLUME_LABEL_LENGTH}
    124      */
    125     public void setVolumeLabel(String label) throws IllegalArgumentException {
    126         if (label.length() > MAX_VOLUME_LABEL_LENGTH)
    127             throw new IllegalArgumentException("volume label too long");
    128 
    129         for (int i = 0; i < MAX_VOLUME_LABEL_LENGTH; i++) {
    130             set8(VOLUME_LABEL_OFFSET + i,
    131                     i < label.length() ? label.charAt(i) : 0);
    132         }
    133     }
    134 
    135     /**
    136      * Gets the number of sectors/fat for FAT 12/16.
    137      *
    138      * @return int
    139      */
    140     @Override
    141     public long getSectorsPerFat() {
    142         return get16(SECTORS_PER_FAT_OFFSET);
    143     }
    144 
    145     /**
    146      * Sets the number of sectors/fat
    147      *
    148      * @param v  the new number of sectors per fat
    149      */
    150     @Override
    151     public void setSectorsPerFat(long v) {
    152         if (v == getSectorsPerFat()) return;
    153         if (v > 0x7FFF) throw new IllegalArgumentException(
    154                 "too many sectors for a FAT12/16");
    155 
    156         set16(SECTORS_PER_FAT_OFFSET, (int)v);
    157     }
    158 
    159     @Override
    160     public FatType getFatType() {
    161         final long rootDirSectors = ((getRootDirEntryCount() * 32) +
    162                 (getBytesPerSector() - 1)) / getBytesPerSector();
    163         final long dataSectors = getSectorCount() -
    164                 (getNrReservedSectors() + (getNrFats() * getSectorsPerFat()) +
    165                 rootDirSectors);
    166         final long clusterCount = dataSectors / getSectorsPerCluster();
    167 
    168         if (clusterCount > MAX_FAT16_CLUSTERS) throw new IllegalStateException(
    169                 "too many clusters for FAT12/16: " + clusterCount);
    170 
    171         return clusterCount > MAX_FAT12_CLUSTERS ?
    172             FatType.FAT16 : FatType.FAT12;
    173     }
    174 
    175     @Override
    176     public void setSectorCount(long count) {
    177         if (count > 65535) {
    178             setNrLogicalSectors(0);
    179             setNrTotalSectors(count);
    180         } else {
    181             setNrLogicalSectors((int) count);
    182             setNrTotalSectors(count);
    183         }
    184     }
    185 
    186     @Override
    187     public long getSectorCount() {
    188         if (getNrLogicalSectors() == 0) return getNrTotalSectors();
    189         else return getNrLogicalSectors();
    190     }
    191 
    192     /**
    193      * Gets the number of entries in the root directory.
    194      *
    195      * @return int the root directory entry count
    196      */
    197     @Override
    198     public int getRootDirEntryCount() {
    199         return get16(ROOT_DIR_ENTRIES_OFFSET);
    200     }
    201 
    202     /**
    203      * Sets the number of entries in the root directory
    204      *
    205      * @param v the new number of entries in the root directory
    206      * @throws IllegalArgumentException for negative values
    207      */
    208     public void setRootDirEntryCount(int v) throws IllegalArgumentException {
    209         if (v < 0) throw new IllegalArgumentException();
    210         if (v == getRootDirEntryCount()) return;
    211 
    212         set16(ROOT_DIR_ENTRIES_OFFSET, v);
    213     }
    214 
    215     @Override
    216     public void init() throws IOException {
    217         super.init();
    218 
    219         setRootDirEntryCount(DEFAULT_ROOT_DIR_ENTRY_COUNT);
    220         setVolumeLabel(DEFAULT_VOLUME_LABEL);
    221     }
    222 
    223     @Override
    224     public int getFileSystemTypeLabelOffset() {
    225         return FILE_SYSTEM_TYPE_OFFSET;
    226     }
    227 
    228     @Override
    229     public int getExtendedBootSignatureOffset() {
    230         return EXTENDED_BOOT_SIGNATURE_OFFSET;
    231     }
    232 
    233 }
    234