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 <matthias.treydte at meetwise.com> 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