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.AbstractFileSystem; 23 import de.waldheinz.fs.BlockDevice; 24 import java.io.IOException; 25 import de.waldheinz.fs.ReadOnlyException; 26 27 /** 28 * <p> 29 * Implements the {@code FileSystem} interface for the FAT family of file 30 * systems. This class always uses the "long file name" specification when 31 * writing directory entries. 32 * </p><p> 33 * For creating (aka "formatting") FAT file systems please refer to the 34 * {@link SuperFloppyFormatter} class. 35 * </p> 36 * 37 * @author Ewout Prangsma <epr at jnode.org> 38 * @author Matthias Treydte <waldheinz at gmail.com> 39 */ 40 public final class FatFileSystem extends AbstractFileSystem { 41 42 private final Fat fat; 43 private final FsInfoSector fsiSector; 44 private final BootSector bs; 45 private final FatLfnDirectory rootDir; 46 private final AbstractDirectory rootDirStore; 47 private final FatType fatType; 48 private final long filesOffset; 49 FatFileSystem(BlockDevice api, boolean readOnly)50 FatFileSystem(BlockDevice api, boolean readOnly) throws IOException { 51 52 this(api, readOnly, false); 53 } 54 55 /** 56 * Constructor for FatFileSystem in specified readOnly mode 57 * 58 * @param device the {@code BlockDevice} holding the file system 59 * @param readOnly if this FS should be read-lonly 60 * @param ignoreFatDifferences 61 * @throws IOException on read error 62 */ FatFileSystem(BlockDevice device, boolean readOnly, boolean ignoreFatDifferences)63 private FatFileSystem(BlockDevice device, boolean readOnly, 64 boolean ignoreFatDifferences) 65 throws IOException { 66 67 super(readOnly); 68 69 this.bs = BootSector.read(device); 70 71 if (bs.getNrFats() <= 0) throw new IOException( 72 "boot sector says there are no FATs"); 73 74 this.filesOffset = FatUtils.getFilesOffset(bs); 75 this.fatType = bs.getFatType(); 76 this.fat = Fat.read(bs, 0); 77 78 if (!ignoreFatDifferences) { 79 for (int i=1; i < bs.getNrFats(); i++) { 80 final Fat tmpFat = Fat.read(bs, i); 81 if (!fat.equals(tmpFat)) { 82 throw new IOException("FAT " + i + " differs from FAT 0"); 83 } 84 } 85 } 86 87 if (fatType == FatType.FAT32) { 88 final Fat32BootSector f32bs = (Fat32BootSector) bs; 89 ClusterChain rootDirFile = new ClusterChain(fat, 90 f32bs.getRootDirFirstCluster(), isReadOnly()); 91 this.rootDirStore = ClusterChainDirectory.readRoot(rootDirFile); 92 this.fsiSector = FsInfoSector.read(f32bs); 93 94 if (fsiSector.getFreeClusterCount() != fat.getFreeClusterCount()) { 95 throw new IOException("free cluster count mismatch - fat: " + 96 fat.getFreeClusterCount() + " - fsinfo: " + 97 fsiSector.getFreeClusterCount()); 98 } 99 } else { 100 this.rootDirStore = 101 Fat16RootDirectory.read((Fat16BootSector) bs,readOnly); 102 this.fsiSector = null; 103 } 104 105 this.rootDir = new FatLfnDirectory(rootDirStore, fat, isReadOnly()); 106 107 } 108 109 /** 110 * Reads the file system structure from the specified {@code BlockDevice} 111 * and returns a fresh {@code FatFileSystem} instance to read or modify 112 * it. 113 * 114 * @param device the {@code BlockDevice} holding the file system 115 * @param readOnly if the {@code FatFileSystem} should be in read-only mode 116 * @return the {@code FatFileSystem} instance for the device 117 * @throws IOException on read error or if the file system structure could 118 * not be parsed 119 */ read(BlockDevice device, boolean readOnly)120 public static FatFileSystem read(BlockDevice device, boolean readOnly) 121 throws IOException { 122 123 return new FatFileSystem(device, readOnly); 124 } 125 getFilesOffset()126 long getFilesOffset() { 127 checkClosed(); 128 129 return filesOffset; 130 } 131 132 /** 133 * Returns the size of the FAT entries of this {@code FatFileSystem}. 134 * 135 * @return the exact type of the FAT used by this file system 136 */ getFatType()137 public FatType getFatType() { 138 checkClosed(); 139 140 return this.fatType; 141 } 142 143 /** 144 * Returns the volume label of this file system. 145 * 146 * @return the volume label 147 */ getVolumeLabel()148 public String getVolumeLabel() { 149 checkClosed(); 150 151 final String fromDir = rootDirStore.getLabel(); 152 153 if (fromDir == null && fatType != FatType.FAT32) { 154 return ((Fat16BootSector)bs).getVolumeLabel(); 155 } else { 156 return fromDir; 157 } 158 } 159 160 /** 161 * Sets the volume label for this file system. 162 * 163 * @param label the new volume label, may be {@code null} 164 * @throws ReadOnlyException if the file system is read-only 165 * @throws IOException on write error 166 */ setVolumeLabel(String label)167 public void setVolumeLabel(String label) 168 throws ReadOnlyException, IOException { 169 170 checkClosed(); 171 checkReadOnly(); 172 173 rootDirStore.setLabel(label); 174 175 if (fatType != FatType.FAT32) { 176 ((Fat16BootSector)bs).setVolumeLabel(label); 177 } 178 } 179 getRootDirStore()180 AbstractDirectory getRootDirStore() { 181 checkClosed(); 182 183 return rootDirStore; 184 } 185 186 /** 187 * Flush all changed structures to the device. 188 * 189 * @throws IOException on write error 190 */ 191 @Override flush()192 public void flush() throws IOException { 193 checkClosed(); 194 195 if (bs.isDirty()) { 196 bs.write(); 197 } 198 199 for (int i = 0; i < bs.getNrFats(); i++) { 200 fat.writeCopy(FatUtils.getFatOffset(bs, i)); 201 } 202 203 rootDir.flush(); 204 205 if (fsiSector != null) { 206 fsiSector.setFreeClusterCount(fat.getFreeClusterCount()); 207 fsiSector.setLastAllocatedCluster(fat.getLastAllocatedCluster()); 208 fsiSector.write(); 209 } 210 } 211 212 @Override getRoot()213 public FatLfnDirectory getRoot() { 214 checkClosed(); 215 216 return rootDir; 217 } 218 219 /** 220 * Returns the fat. 221 * 222 * @return Fat 223 */ getFat()224 public Fat getFat() { 225 return fat; 226 } 227 228 /** 229 * Returns the bootsector. 230 * 231 * @return BootSector 232 */ getBootSector()233 public BootSector getBootSector() { 234 checkClosed(); 235 236 return bs; 237 } 238 239 /** 240 * <p> 241 * {@inheritDoc} 242 * </p><p> 243 * This method shows the free space in terms of available clusters. 244 * </p> 245 * 246 * @return always -1 247 */ 248 @Override getFreeSpace()249 public long getFreeSpace() { 250 return this.fat.getFreeClusterCount() * this.bs.getBytesPerCluster(); 251 } 252 253 /** 254 * <p> 255 * {@inheritDoc} 256 * </p><p> 257 * This method is currently not implemented for {@code FatFileSystem} and 258 * always returns -1. 259 * </p> 260 * 261 * @return always -1 262 */ 263 @Override getTotalSpace()264 public long getTotalSpace() { 265 return this.bs.getDataClusterCount() * this.bs.getBytesPerCluster(); 266 } 267 268 /** 269 * <p> 270 * {@inheritDoc} 271 * </p><p> 272 * This method is currently not implemented for {@code FatFileSystem} and 273 * always returns -1. 274 * </p> 275 * 276 * @return always -1 277 */ 278 @Override getUsableSpace()279 public long getUsableSpace() { 280 // TODO implement me 281 return -1; 282 } 283 } 284