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 &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 
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