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 java.io.IOException;
23 import java.nio.ByteBuffer;
24 
25 /**
26  * A directory that is stored in a cluster chain.
27  *
28  * @author Ewout Prangsma &lt;epr at jnode.org&gt;
29  * @author Matthias Treydte &lt;waldheinz at gmail.com&gt;
30  */
31 class ClusterChainDirectory extends AbstractDirectory {
32 
33     /**
34      * According to the FAT specification, this is the maximum size a FAT
35      * directory may occupy on disk. The {@code ClusterChainDirectory} takes
36      * care not to grow beyond this limit.
37      *
38      * @see #changeSize(int)
39      */
40     public final static int MAX_SIZE = 65536 * 32;
41 
42     /**
43      * The {@code ClusterChain} that stores this directory. Package-visible
44      * for testing.
45      */
46     final ClusterChain chain;
47 
ClusterChainDirectory(ClusterChain chain, boolean isRoot)48     protected ClusterChainDirectory(ClusterChain chain, boolean isRoot) {
49 
50         super((int)(chain.getLengthOnDisk() / FatDirectoryEntry.SIZE),
51                 chain.isReadOnly(), isRoot);
52 
53         this.chain = chain;
54     }
55 
readRoot( ClusterChain chain)56     public static ClusterChainDirectory readRoot(
57             ClusterChain chain) throws IOException {
58 
59         final ClusterChainDirectory result =
60                 new ClusterChainDirectory(chain, true);
61 
62         result.read();
63         return result;
64     }
65 
createRoot(Fat fat)66     public static ClusterChainDirectory createRoot(Fat fat) throws IOException {
67 
68         if (fat.getFatType() != FatType.FAT32) {
69             throw new IllegalArgumentException(
70                     "only FAT32 stores root directory in a cluster chain");
71         }
72 
73         final Fat32BootSector bs = (Fat32BootSector) fat.getBootSector();
74         final ClusterChain cc = new ClusterChain(fat, false);
75         cc.setChainLength(1);
76 
77         bs.setRootDirFirstCluster(cc.getStartCluster());
78 
79         final ClusterChainDirectory result =
80                 new ClusterChainDirectory(cc, true);
81 
82         result.flush();
83         return result;
84     }
85 
86     @Override
read(ByteBuffer data)87     protected final void read(ByteBuffer data) throws IOException {
88         this.chain.readData(0, data);
89     }
90 
91     @Override
write(ByteBuffer data)92     protected final void write(ByteBuffer data) throws IOException {
93         final int toWrite = data.remaining();
94         chain.writeData(0, data);
95         final long trueSize = chain.getLengthOnDisk();
96 
97         /* TODO: check if the code below is really needed */
98         if (trueSize > toWrite) {
99             final int rest = (int) (trueSize - toWrite);
100             final ByteBuffer fill = ByteBuffer.allocate(rest);
101             chain.writeData(toWrite, fill);
102         }
103     }
104 
105     /**
106      * Returns the first cluster of the chain that stores this directory for
107      * non-root instances or 0 if this is the root directory.
108      *
109      * @return the first storage cluster of this directory
110      * @see #isRoot()
111      */
112     @Override
getStorageCluster()113     protected final long getStorageCluster() {
114         return isRoot() ? 0 : chain.getStartCluster();
115     }
116 
delete()117     public final void delete() throws IOException {
118         chain.setChainLength(0);
119     }
120 
121     @Override
changeSize(int entryCount)122     protected final void changeSize(int entryCount)
123             throws IOException, IllegalArgumentException {
124 
125         assert (entryCount >= 0);
126 
127         final int size = entryCount * FatDirectoryEntry.SIZE;
128 
129         if (size > MAX_SIZE) throw new DirectoryFullException(
130                 "directory would grow beyond " + MAX_SIZE + " bytes",
131                 getCapacity(), entryCount);
132 
133         sizeChanged(chain.setSize(Math.max(size, chain.getClusterSize())));
134     }
135 
136 }
137