summaryrefslogtreecommitdiff
path: root/src/main/java/de/waldheinz/fs/fat/ClusterChainDirectory.java
blob: fa3e9dfad41519b14725db1a96921f28065caa1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * Copyright (C) 2003-2009 JNode.org
 *               2009,2010 Matthias Treydte <mt@waldheinz.de>
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; If not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
package de.waldheinz.fs.fat;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * A directory that is stored in a cluster chain.
 *
 * @author Ewout Prangsma &lt;epr at jnode.org&gt;
 * @author Matthias Treydte &lt;waldheinz at gmail.com&gt;
 */
class ClusterChainDirectory extends AbstractDirectory {

    /**
     * According to the FAT specification, this is the maximum size a FAT
     * directory may occupy on disk. The {@code ClusterChainDirectory} takes
     * care not to grow beyond this limit.
     *
     * @see #changeSize(int) 
     */
    public final static int MAX_SIZE = 65536 * 32;

    /**
     * The {@code ClusterChain} that stores this directory. Package-visible
     * for testing.
     */
    final ClusterChain chain;
    
    protected ClusterChainDirectory(ClusterChain chain, boolean isRoot) {
        
        super((int)(chain.getLengthOnDisk() / FatDirectoryEntry.SIZE),
                chain.isReadOnly(), isRoot);
        
        this.chain = chain;   
    }
    
    public static ClusterChainDirectory readRoot(
            ClusterChain chain) throws IOException {
        
        final ClusterChainDirectory result =
                new ClusterChainDirectory(chain, true);
        
        result.read();
        return result;
    }
    
    public static ClusterChainDirectory createRoot(Fat fat) throws IOException {

        if (fat.getFatType() != FatType.FAT32) {
            throw new IllegalArgumentException(
                    "only FAT32 stores root directory in a cluster chain");
        }

        final Fat32BootSector bs = (Fat32BootSector) fat.getBootSector();
        final ClusterChain cc = new ClusterChain(fat, false);
        cc.setChainLength(1);
        
        bs.setRootDirFirstCluster(cc.getStartCluster());
        
        final ClusterChainDirectory result =
                new ClusterChainDirectory(cc, true);
        
        result.flush();
        return result;
    }
    
    @Override
    protected final void read(ByteBuffer data) throws IOException {
        this.chain.readData(0, data);
    }

    @Override
    protected final void write(ByteBuffer data) throws IOException {
        final int toWrite = data.remaining();
        chain.writeData(0, data);
        final long trueSize = chain.getLengthOnDisk();
        
        /* TODO: check if the code below is really needed */
        if (trueSize > toWrite) {
            final int rest = (int) (trueSize - toWrite);
            final ByteBuffer fill = ByteBuffer.allocate(rest);
            chain.writeData(toWrite, fill);
        }
    }

    /**
     * Returns the first cluster of the chain that stores this directory for
     * non-root instances or 0 if this is the root directory.
     *
     * @return the first storage cluster of this directory
     * @see #isRoot() 
     */
    @Override
    protected final long getStorageCluster() {
        return isRoot() ? 0 : chain.getStartCluster();
    }
    
    public final void delete() throws IOException {
        chain.setChainLength(0);
    }
    
    @Override
    protected final void changeSize(int entryCount)
            throws IOException, IllegalArgumentException {

        assert (entryCount >= 0);

        final int size = entryCount * FatDirectoryEntry.SIZE;

        if (size > MAX_SIZE) throw new DirectoryFullException(
                "directory would grow beyond " + MAX_SIZE + " bytes",
                getCapacity(), entryCount);
        
        sizeChanged(chain.setSize(Math.max(size, chain.getClusterSize())));
    }
    
}