diff options
Diffstat (limited to 'src/main/java/de/waldheinz/fs/fat/FatFileSystem.java')
-rw-r--r-- | src/main/java/de/waldheinz/fs/fat/FatFileSystem.java | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/src/main/java/de/waldheinz/fs/fat/FatFileSystem.java b/src/main/java/de/waldheinz/fs/fat/FatFileSystem.java new file mode 100644 index 0000000..db4d3e2 --- /dev/null +++ b/src/main/java/de/waldheinz/fs/fat/FatFileSystem.java @@ -0,0 +1,283 @@ +/* + * 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 de.waldheinz.fs.AbstractFileSystem; +import de.waldheinz.fs.BlockDevice; +import java.io.IOException; +import de.waldheinz.fs.ReadOnlyException; + +/** + * <p> + * Implements the {@code FileSystem} interface for the FAT family of file + * systems. This class always uses the "long file name" specification when + * writing directory entries. + * </p><p> + * For creating (aka "formatting") FAT file systems please refer to the + * {@link SuperFloppyFormatter} class. + * </p> + * + * @author Ewout Prangsma <epr at jnode.org> + * @author Matthias Treydte <waldheinz at gmail.com> + */ +public final class FatFileSystem extends AbstractFileSystem { + + private final Fat fat; + private final FsInfoSector fsiSector; + private final BootSector bs; + private final FatLfnDirectory rootDir; + private final AbstractDirectory rootDirStore; + private final FatType fatType; + private final long filesOffset; + + FatFileSystem(BlockDevice api, boolean readOnly) throws IOException { + + this(api, readOnly, false); + } + + /** + * Constructor for FatFileSystem in specified readOnly mode + * + * @param device the {@code BlockDevice} holding the file system + * @param readOnly if this FS should be read-lonly + * @param ignoreFatDifferences + * @throws IOException on read error + */ + private FatFileSystem(BlockDevice device, boolean readOnly, + boolean ignoreFatDifferences) + throws IOException { + + super(readOnly); + + this.bs = BootSector.read(device); + + if (bs.getNrFats() <= 0) throw new IOException( + "boot sector says there are no FATs"); + + this.filesOffset = FatUtils.getFilesOffset(bs); + this.fatType = bs.getFatType(); + this.fat = Fat.read(bs, 0); + + if (!ignoreFatDifferences) { + for (int i=1; i < bs.getNrFats(); i++) { + final Fat tmpFat = Fat.read(bs, i); + if (!fat.equals(tmpFat)) { + throw new IOException("FAT " + i + " differs from FAT 0"); + } + } + } + + if (fatType == FatType.FAT32) { + final Fat32BootSector f32bs = (Fat32BootSector) bs; + ClusterChain rootDirFile = new ClusterChain(fat, + f32bs.getRootDirFirstCluster(), isReadOnly()); + this.rootDirStore = ClusterChainDirectory.readRoot(rootDirFile); + this.fsiSector = FsInfoSector.read(f32bs); + + if (fsiSector.getFreeClusterCount() != fat.getFreeClusterCount()) { + throw new IOException("free cluster count mismatch - fat: " + + fat.getFreeClusterCount() + " - fsinfo: " + + fsiSector.getFreeClusterCount()); + } + } else { + this.rootDirStore = + Fat16RootDirectory.read((Fat16BootSector) bs,readOnly); + this.fsiSector = null; + } + + this.rootDir = new FatLfnDirectory(rootDirStore, fat, isReadOnly()); + + } + + /** + * Reads the file system structure from the specified {@code BlockDevice} + * and returns a fresh {@code FatFileSystem} instance to read or modify + * it. + * + * @param device the {@code BlockDevice} holding the file system + * @param readOnly if the {@code FatFileSystem} should be in read-only mode + * @return the {@code FatFileSystem} instance for the device + * @throws IOException on read error or if the file system structure could + * not be parsed + */ + public static FatFileSystem read(BlockDevice device, boolean readOnly) + throws IOException { + + return new FatFileSystem(device, readOnly); + } + + long getFilesOffset() { + checkClosed(); + + return filesOffset; + } + + /** + * Returns the size of the FAT entries of this {@code FatFileSystem}. + * + * @return the exact type of the FAT used by this file system + */ + public FatType getFatType() { + checkClosed(); + + return this.fatType; + } + + /** + * Returns the volume label of this file system. + * + * @return the volume label + */ + public String getVolumeLabel() { + checkClosed(); + + final String fromDir = rootDirStore.getLabel(); + + if (fromDir == null && fatType != FatType.FAT32) { + return ((Fat16BootSector)bs).getVolumeLabel(); + } else { + return fromDir; + } + } + + /** + * Sets the volume label for this file system. + * + * @param label the new volume label, may be {@code null} + * @throws ReadOnlyException if the file system is read-only + * @throws IOException on write error + */ + public void setVolumeLabel(String label) + throws ReadOnlyException, IOException { + + checkClosed(); + checkReadOnly(); + + rootDirStore.setLabel(label); + + if (fatType != FatType.FAT32) { + ((Fat16BootSector)bs).setVolumeLabel(label); + } + } + + AbstractDirectory getRootDirStore() { + checkClosed(); + + return rootDirStore; + } + + /** + * Flush all changed structures to the device. + * + * @throws IOException on write error + */ + @Override + public void flush() throws IOException { + checkClosed(); + + if (bs.isDirty()) { + bs.write(); + } + + for (int i = 0; i < bs.getNrFats(); i++) { + fat.writeCopy(FatUtils.getFatOffset(bs, i)); + } + + rootDir.flush(); + + if (fsiSector != null) { + fsiSector.setFreeClusterCount(fat.getFreeClusterCount()); + fsiSector.setLastAllocatedCluster(fat.getLastAllocatedCluster()); + fsiSector.write(); + } + } + + @Override + public FatLfnDirectory getRoot() { + checkClosed(); + + return rootDir; + } + + /** + * Returns the fat. + * + * @return Fat + */ + public Fat getFat() { + return fat; + } + + /** + * Returns the bootsector. + * + * @return BootSector + */ + public BootSector getBootSector() { + checkClosed(); + + return bs; + } + + /** + * <p> + * {@inheritDoc} + * </p><p> + * This method shows the free space in terms of available clusters. + * </p> + * + * @return always -1 + */ + @Override + public long getFreeSpace() { + return this.fat.getFreeClusterCount() * this.bs.getBytesPerCluster(); + } + + /** + * <p> + * {@inheritDoc} + * </p><p> + * This method is currently not implemented for {@code FatFileSystem} and + * always returns -1. + * </p> + * + * @return always -1 + */ + @Override + public long getTotalSpace() { + return this.bs.getDataClusterCount() * this.bs.getBytesPerCluster(); + } + + /** + * <p> + * {@inheritDoc} + * </p><p> + * This method is currently not implemented for {@code FatFileSystem} and + * always returns -1. + * </p> + * + * @return always -1 + */ + @Override + public long getUsableSpace() { + // TODO implement me + return -1; + } +} |