1 /* 2 * Copyright (C) 2003-2009 JNode.org 3 * 2009,2010 Matthias Treydte <mt@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 <epr at jnode.org> 31 * @author Matthias Treydte <waldheinz at gmail.com> 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 BootSector(BlockDevice device)69 protected BootSector(BlockDevice device) { 70 super(device, 0, SIZE); 71 markDirty(); 72 } 73 read(BlockDevice device)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 getFatType()121 public abstract FatType getFatType(); 122 123 /** 124 * Gets the number of sectors per FAT. 125 * 126 * @return the sectors per FAT 127 */ getSectorsPerFat()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 */ setSectorsPerFat(long v)135 public abstract void setSectorsPerFat(long v); 136 setSectorCount(long count)137 public abstract void setSectorCount(long count); 138 getRootDirEntryCount()139 public abstract int getRootDirEntryCount(); 140 getSectorCount()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 */ getFileSystemTypeLabelOffset()149 public abstract int getFileSystemTypeLabelOffset(); 150 getExtendedBootSignatureOffset()151 public abstract int getExtendedBootSignatureOffset(); 152 init()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 */ getFileSystemTypeLabel()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 */ setFileSystemTypeLabel(String fsType)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 */ getDataClusterCount()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 */ getDataSize()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 */ getOemName()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 */ setOemName(String name)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 */ getBytesPerSector()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 */ setBytesPerSector(int v)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 isPowerOfTwo(int n)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 */ getBytesPerCluster()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 */ getSectorsPerCluster()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 */ setSectorsPerCluster(int v)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 */ getNrReservedSectors()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 */ setNrReservedSectors(int v)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 */ getNrFats()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 */ setNrFats(int v)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 */ getNrLogicalSectors()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 */ setNrLogicalSectors(int v)387 protected void setNrLogicalSectors(int v) { 388 if (v == getNrLogicalSectors()) return; 389 390 set16(TOTAL_SECTORS_16_OFFSET, v); 391 } 392 setNrTotalSectors(long v)393 protected void setNrTotalSectors(long v) { 394 set32(TOTAL_SECTORS_32_OFFSET, v); 395 } 396 getNrTotalSectors()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 */ getMediumDescriptor()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 */ setMediumDescriptor(int v)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 */ getSectorsPerTrack()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 */ setSectorsPerTrack(int v)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 */ getNrHeads()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 */ setNrHeads(int v)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 */ getNrHiddenSectors()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 */ setNrHiddenSectors(long v)473 public void setNrHiddenSectors(long v) { 474 if (v == getNrHiddenSectors()) return; 475 476 set32(0x1c, v); 477 } 478 479 @Override toString()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