1 /*
2  * Copyright (C) 2009,2010 Matthias Treydte <mt@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 FAT32 File System Information Sector.
26  *
27  * @author Matthias Treydte &lt;waldheinz at gmail.com&gt;
28  * @see http://en.wikipedia.org/wiki/File_Allocation_Table#FS_Information_Sector
29  */
30 final class FsInfoSector extends Sector {
31 
32     /**
33      * The offset to the free cluster count value in the FS info sector.
34      */
35     public static final int FREE_CLUSTERS_OFFSET = 0x1e8;
36 
37     /**
38      * The offset to the "last allocated cluster" value in this sector.
39      */
40     public static final int LAST_ALLOCATED_OFFSET = 0x1ec;
41 
42     /**
43      * The offset to the signature of this sector.
44      */
45     public static final int SIGNATURE_OFFSET = 0x1fe;
46 
FsInfoSector(BlockDevice device, long offset)47     private FsInfoSector(BlockDevice device, long offset) {
48         super(device, offset, BootSector.SIZE);
49     }
50 
51     /**
52      * Reads a {@code FsInfoSector} as specified by the given
53      * {@code Fat32BootSector}.
54      *
55      * @param bs the boot sector that specifies where the FS info sector is
56      *      stored
57      * @return the FS info sector that was read
58      * @throws IOException on read error
59      * @see Fat32BootSector#getFsInfoSectorNr()
60      */
read(Fat32BootSector bs)61     public static FsInfoSector read(Fat32BootSector bs) throws IOException {
62         final FsInfoSector result =
63                 new FsInfoSector(bs.getDevice(), offset(bs));
64 
65         result.read();
66         result.verify();
67         return result;
68     }
69 
70     /**
71      * Creates an new {@code FsInfoSector} where the specified
72      * {@code Fat32BootSector} indicates it should be.
73      *
74      * @param bs the boot sector specifying the FS info sector storage
75      * @return the FS info sector instance that was created
76      * @throws IOException on write error
77      * @see Fat32BootSector#getFsInfoSectorNr()
78      */
create(Fat32BootSector bs)79     public static FsInfoSector create(Fat32BootSector bs) throws IOException {
80         final int offset = offset(bs);
81 
82         if (offset == 0) throw new IOException(
83                 "creating a FS info sector at offset 0 is strange");
84 
85         final FsInfoSector result =
86                 new FsInfoSector(bs.getDevice(), offset(bs));
87 
88         result.init();
89         result.write();
90         return result;
91     }
92 
offset(Fat32BootSector bs)93     private static int offset(Fat32BootSector bs) {
94         return bs.getFsInfoSectorNr() * bs.getBytesPerSector();
95     }
96 
97     /**
98      * Sets the number of free clusters on the file system stored at
99      * {@link #FREE_CLUSTERS_OFFSET}.
100      *
101      * @param value the new free cluster count
102      * @see Fat#getFreeClusterCount()
103      */
setFreeClusterCount(long value)104     public void setFreeClusterCount(long value) {
105         if (getFreeClusterCount() == value) return;
106 
107         set32(FREE_CLUSTERS_OFFSET, value);
108     }
109 
110     /**
111      * Returns the number of free clusters on the file system as sepcified by
112      * the 32-bit value at {@link #FREE_CLUSTERS_OFFSET}.
113      *
114      * @return the number of free clusters
115      * @see Fat#getFreeClusterCount()
116      */
getFreeClusterCount()117     public long getFreeClusterCount() {
118         return get32(FREE_CLUSTERS_OFFSET);
119     }
120 
121     /**
122      * Sets the last allocated cluster that was used in the {@link Fat}.
123      *
124      * @param value the FAT's last allocated cluster number
125      * @see Fat#getLastAllocatedCluster()
126      */
setLastAllocatedCluster(long value)127     public void setLastAllocatedCluster(long value) {
128         if (getLastAllocatedCluster() == value) return;
129 
130         super.set32(LAST_ALLOCATED_OFFSET, value);
131     }
132 
133     /**
134      * Returns the last allocated cluster number of the {@link Fat} of the
135      * file system this FS info sector is part of.
136      *
137      * @return the last allocated cluster number
138      * @see Fat#getLastAllocatedCluster()
139      */
getLastAllocatedCluster()140     public long getLastAllocatedCluster() {
141         return super.get32(LAST_ALLOCATED_OFFSET);
142     }
143 
init()144     private void init() {
145         buffer.position(0x00);
146         buffer.put((byte) 0x52);
147         buffer.put((byte) 0x52);
148         buffer.put((byte) 0x61);
149         buffer.put((byte) 0x41);
150 
151         /* 480 reserved bytes */
152 
153         buffer.position(0x1e4);
154         buffer.put((byte) 0x72);
155         buffer.put((byte) 0x72);
156         buffer.put((byte) 0x41);
157         buffer.put((byte) 0x61);
158 
159         setFreeClusterCount(-1);
160         setLastAllocatedCluster(Fat.FIRST_CLUSTER);
161 
162         buffer.position(SIGNATURE_OFFSET);
163         buffer.put((byte) 0x55);
164         buffer.put((byte) 0xaa);
165 
166         markDirty();
167     }
168 
verify()169     private void verify() throws IOException {
170         if (!(get8(SIGNATURE_OFFSET) == 0x55) ||
171                 !(get8(SIGNATURE_OFFSET + 1) == 0xaa)) {
172 
173             throw new IOException("invalid FS info sector signature");
174         }
175     }
176 
177     @Override
toString()178     public String toString() {
179         return FsInfoSector.class.getSimpleName() +
180                 " [freeClusterCount=" + getFreeClusterCount() + //NOI18N
181                 ", lastAllocatedCluster=" + getLastAllocatedCluster() + //NOI18N
182                 ", offset=" + getOffset() + //NOI18N
183                 ", dirty=" + isDirty() + //NOI18N
184                 "]"; //NOI18N
185     }
186 
187 }
188