• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.AbstractFsObject;
22 import java.io.IOException;
23 import de.waldheinz.fs.FsFile;
24 import de.waldheinz.fs.ReadOnlyException;
25 import java.io.EOFException;
26 import java.nio.ByteBuffer;
27 
28 /**
29  * The in-memory representation of a single file (chain of clusters) on a
30  * FAT file system.
31  *
32  * @author Matthias Treydte &lt;waldheinz at gmail.com&gt;
33  * @since 0.6
34  */
35 public final class FatFile extends AbstractFsObject implements FsFile {
36     private final FatDirectoryEntry entry;
37     private final ClusterChain chain;
38 
FatFile(FatDirectoryEntry myEntry, ClusterChain chain)39     private FatFile(FatDirectoryEntry myEntry, ClusterChain chain) {
40         super(myEntry.isReadOnly());
41 
42         this.entry = myEntry;
43         this.chain = chain;
44     }
45 
get(Fat fat, FatDirectoryEntry entry)46     static FatFile get(Fat fat, FatDirectoryEntry entry)
47             throws IOException {
48 
49         if (entry.isDirectory())
50             throw new IllegalArgumentException(entry + " is a directory");
51 
52         final ClusterChain cc = new ClusterChain(
53                 fat, entry.getStartCluster(), entry.isReadonlyFlag());
54 
55         if (entry.getLength() > cc.getLengthOnDisk()) throw new IOException(
56                 "entry is larger than associated cluster chain");
57 
58         return new FatFile(entry, cc);
59     }
60 
61     /**
62      * Returns the length of this file in bytes. This is the length that
63      * is stored in the directory entry that is associated with this file.
64      *
65      * @return long the length that is recorded for this file
66      */
67     @Override
getLength()68     public long getLength() {
69         checkValid();
70 
71         return entry.getLength();
72     }
73 
74     /**
75      * Sets the size (in bytes) of this file. Because
76      * {@link #write(long, java.nio.ByteBuffer) writing} to the file will grow
77      * it automatically if needed, this method is mainly usefull for truncating
78      * a file.
79      *
80      * @param length the new length of the file in bytes
81      * @throws ReadOnlyException if this file is read-only
82      * @throws IOException on error updating the file size
83      */
84     @Override
setLength(long length)85     public void setLength(long length) throws ReadOnlyException, IOException {
86         checkWritable();
87 
88         if (getLength() == length) return;
89 
90         updateTimeStamps(true);
91         chain.setSize(length);
92 
93         this.entry.setStartCluster(chain.getStartCluster());
94         this.entry.setLength(length);
95     }
96 
97     /**
98      * <p>
99      * {@inheritDoc}
100      * </p><p>
101      * Unless this file is {@link #isReadOnly() read-ony}, this method also
102      * updates the "last accessed" field in the directory entry that is
103      * associated with this file.
104      * </p>
105      *
106      * @param offset {@inheritDoc}
107      * @param dest {@inheritDoc}
108      * @see FatDirectoryEntry#setLastAccessed(long)
109      */
110     @Override
read(long offset, ByteBuffer dest)111     public void read(long offset, ByteBuffer dest) throws IOException {
112         checkValid();
113 
114         final int len = dest.remaining();
115 
116         if (len == 0) return;
117 
118         if (offset + len > getLength()) {
119             throw new EOFException();
120         }
121 
122         if (!isReadOnly()) {
123             updateTimeStamps(false);
124         }
125 
126         chain.readData(offset, dest);
127     }
128 
129     /**
130      * <p>
131      * {@inheritDoc}
132      * </p><p>
133      * If the data to be written extends beyond the current
134      * {@link #getLength() length} of this file, an attempt is made to
135      * {@link #setLength(long) grow} the file so that the data will fit.
136      * Additionally, this method updates the "last accessed" and "last modified"
137      * fields on the directory entry that is associated with this file.
138      * </p>
139      *
140      * @param offset {@inheritDoc}
141      * @param srcBuf {@inheritDoc}
142      */
143     @Override
write(long offset, ByteBuffer srcBuf)144     public void write(long offset, ByteBuffer srcBuf)
145             throws ReadOnlyException, IOException {
146 
147         checkWritable();
148 
149         updateTimeStamps(true);
150 
151         final long lastByte = offset + srcBuf.remaining();
152 
153         if (lastByte > getLength()) {
154             setLength(lastByte);
155         }
156 
157         chain.writeData(offset, srcBuf);
158     }
159 
updateTimeStamps(boolean write)160     private void updateTimeStamps(boolean write) {
161         final long now = System.currentTimeMillis();
162         entry.setLastAccessed(now);
163 
164         if (write) {
165             entry.setLastModified(now);
166         }
167     }
168 
169     /**
170      * Has no effect besides possibly throwing an {@code ReadOnlyException}. To
171      * make sure that all data is written out to disk use the
172      * {@link FatFileSystem#flush()} method.
173      *
174      * @throws ReadOnlyException if this {@code FatFile} is read-only
175      */
176     @Override
flush()177     public void flush() throws ReadOnlyException {
178         checkWritable();
179 
180         /* nothing else to do */
181     }
182 
183     /**
184      * Returns the {@code ClusterChain} that holds the contents of
185      * this {@code FatFile}.
186      *
187      * @return the file's {@code ClusterChain}
188      */
getChain()189     ClusterChain getChain() {
190         checkValid();
191 
192         return chain;
193     }
194 
195     /**
196      * Returns a human-readable string representation of this {@code FatFile},
197      * mainly for debugging purposes.
198      *
199      * @return a string describing this {@code FatFile}
200      */
201     @Override
toString()202     public String toString() {
203         return getClass().getSimpleName() + " [length=" + getLength() +
204                 ", first cluster=" + chain.getStartCluster() + "]";
205     }
206 
207 }
208