// Path: src/main/java/de/waldheinz/fs/AbstractFileSystem.java
// public abstract class AbstractFileSystem implements FileSystem {
//     private final boolean readOnly;
//     private boolean closed;
//     
//     /**
//      * Creates a new {@code AbstractFileSystem}.
//      * 
//      * @param readOnly if the file system should be read-only
//      */
//     public AbstractFileSystem(boolean readOnly) {
//         this.closed = false;
//         this.readOnly = readOnly;
//     }
//     
//     @Override
//     public void close() throws IOException {
//         if (!isClosed()) {
//             if (!isReadOnly()) {
//                 flush();
//             }
//             
//             closed = true;
//         }
//     }
//     
//     @Override
//     public final boolean isClosed() {
//         return closed;
//     }
//     
//     @Override
//     public final boolean isReadOnly() {
//         return readOnly;
//     }
// 
//     /**
//      * Checks if this {@code FileSystem} was already closed, and throws an
//      * exception if it was.
//      *
//      * @throws IllegalStateException if this {@code FileSystem} was
//      *      already closed
//      * @see #isClosed()
//      * @see #close() 
//      */
//     protected final void checkClosed() throws IllegalStateException {
//         if (isClosed()) {
//             throw new IllegalStateException("file system was already closed");
//         }
//     }
//     
//     /**
//      * Checks if this {@code FileSystem} is read-only, and throws an
//      * exception if it is.
//      *
//      * @throws ReadOnlyException if this {@code FileSystem} is read-only
//      * @see #isReadOnly() 
//      */
//     protected final void checkReadOnly() throws ReadOnlyException {
//         if (isReadOnly()) {
//             throw new ReadOnlyException();
//         }
//     }
// }
// 
// Path: src/main/java/de/waldheinz/fs/BlockDevice.java
// public interface BlockDevice {
// 
//     /**
//      * Gets the total length of this device in bytes.
//      *
//      * @return the total number of bytes on this device
//      * @throws IOException on error getting the size of this device
//      */
//     public abstract long getSize() throws IOException;
// 
//     /**
//      * Read a block of data from this device.
//      *
//      * @param devOffset the byte offset where to read the data from
//      * @param dest the destination buffer where to store the data read
//      * @throws IOException on read error
//      */
//     public abstract void read(long devOffset, ByteBuffer dest)
//             throws IOException;
// 
//     /**
//      * Writes a block of data to this device.
//      *
//      * @param devOffset the byte offset where to store the data
//      * @param src the source {@code ByteBuffer} to write to the device
//      * @throws ReadOnlyException if this {@code BlockDevice} is read-only
//      * @throws IOException on write error
//      * @throws IllegalArgumentException if the {@code devOffset} is negative
//      *      or the write would go beyond the end of the device
//      * @see #isReadOnly()
//      */
//     public abstract void write(long devOffset, ByteBuffer src)
//             throws ReadOnlyException, IOException,
//             IllegalArgumentException;
//             
//     /**
//      * Flushes data in caches to the actual storage.
//      *
//      * @throws IOException on write error
//      */
//     public abstract void flush() throws IOException;
// 
//     /**
//      * Returns the size of a sector on this device.
//      *
//      * @return the sector size in bytes
//      * @throws IOException on error determining the sector size
//      */
//     public int getSectorSize() throws IOException;
// 
//     /**
//      * Closes this {@code BlockDevice}. No methods of this device may be
//      * accesses after this method was called.
//      *
//      * @throws IOException on error closing this device
//      * @see #isClosed() 
//      */
//     public void close() throws IOException;
// 
//     /**
//      * Checks if this device was already closed. No methods may be called
//      * on a closed device (except this method).
//      *
//      * @return if this device is closed
//      */
//     public boolean isClosed();
// 
//     /**
//      * Checks if this {@code BlockDevice} is read-only.
//      *
//      * @return if this {@code BlockDevice} is read-only
//      */
//     public boolean isReadOnly();
//     
// }
// 
// Path: src/main/java/de/waldheinz/fs/ReadOnlyException.java
// public final class ReadOnlyException extends RuntimeException {
// 
//     private final static long serialVersionUID = 1;
//     
//     /**
//      * Creates a new instance of {@code ReadOnlyException}.
//      *
//      */
//     public ReadOnlyException() {
//         super("read-only");
//     }
// }


import de.waldheinz.fs.ReadOnlyException;
import de.waldheinz.fs.AbstractFileSystem;
import de.waldheinz.fs.BlockDevice;
import java.io.IOException;
                    f32bs.getRootDirFirstCluster(), isReadOnly());
            this.rootDirStore = ClusterChainDirectory.readRoot(rootChain);
            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
     */
// Path: src/main/java/de/waldheinz/fs/AbstractFileSystem.java
// public abstract class AbstractFileSystem implements FileSystem {
//     private final boolean readOnly;
//     private boolean closed;
//     
//     /**
//      * Creates a new {@code AbstractFileSystem}.
//      * 
//      * @param readOnly if the file system should be read-only
//      */
//     public AbstractFileSystem(boolean readOnly) {
//         this.closed = false;
//         this.readOnly = readOnly;
//     }
//     
//     @Override
//     public void close() throws IOException {
//         if (!isClosed()) {
//             if (!isReadOnly()) {
//                 flush();
//             }
//             
//             closed = true;
//         }
//     }
//     
//     @Override
//     public final boolean isClosed() {
//         return closed;
//     }
//     
//     @Override
//     public final boolean isReadOnly() {
//         return readOnly;
//     }
// 
//     /**
//      * Checks if this {@code FileSystem} was already closed, and throws an
//      * exception if it was.
//      *
//      * @throws IllegalStateException if this {@code FileSystem} was
//      *      already closed
//      * @see #isClosed()
//      * @see #close() 
//      */
//     protected final void checkClosed() throws IllegalStateException {
//         if (isClosed()) {
//             throw new IllegalStateException("file system was already closed");
//         }
//     }
//     
//     /**
//      * Checks if this {@code FileSystem} is read-only, and throws an
//      * exception if it is.
//      *
//      * @throws ReadOnlyException if this {@code FileSystem} is read-only
//      * @see #isReadOnly() 
//      */
//     protected final void checkReadOnly() throws ReadOnlyException {
//         if (isReadOnly()) {
//             throw new ReadOnlyException();
//         }
//     }
// }
// 
// Path: src/main/java/de/waldheinz/fs/BlockDevice.java
// public interface BlockDevice {
// 
//     /**
//      * Gets the total length of this device in bytes.
//      *
//      * @return the total number of bytes on this device
//      * @throws IOException on error getting the size of this device
//      */
//     public abstract long getSize() throws IOException;
// 
//     /**
//      * Read a block of data from this device.
//      *
//      * @param devOffset the byte offset where to read the data from
//      * @param dest the destination buffer where to store the data read
//      * @throws IOException on read error
//      */
//     public abstract void read(long devOffset, ByteBuffer dest)
//             throws IOException;
// 
//     /**
//      * Writes a block of data to this device.
//      *
//      * @param devOffset the byte offset where to store the data
//      * @param src the source {@code ByteBuffer} to write to the device
//      * @throws ReadOnlyException if this {@code BlockDevice} is read-only
//      * @throws IOException on write error
//      * @throws IllegalArgumentException if the {@code devOffset} is negative
//      *      or the write would go beyond the end of the device
//      * @see #isReadOnly()
//      */
//     public abstract void write(long devOffset, ByteBuffer src)
//             throws ReadOnlyException, IOException,
//             IllegalArgumentException;
//             
//     /**
//      * Flushes data in caches to the actual storage.
//      *
//      * @throws IOException on write error
//      */
//     public abstract void flush() throws IOException;
// 
//     /**
//      * Returns the size of a sector on this device.
//      *
//      * @return the sector size in bytes
//      * @throws IOException on error determining the sector size
//      */
//     public int getSectorSize() throws IOException;
// 
//     /**
//      * Closes this {@code BlockDevice}. No methods of this device may be
//      * accesses after this method was called.
//      *
//      * @throws IOException on error closing this device
//      * @see #isClosed() 
//      */
//     public void close() throws IOException;
// 
//     /**
//      * Checks if this device was already closed. No methods may be called
//      * on a closed device (except this method).
//      *
//      * @return if this device is closed
//      */
//     public boolean isClosed();
// 
//     /**
//      * Checks if this {@code BlockDevice} is read-only.
//      *
//      * @return if this {@code BlockDevice} is read-only
//      */
//     public boolean isReadOnly();
//     
// }
// 
// Path: src/main/java/de/waldheinz/fs/ReadOnlyException.java
// public final class ReadOnlyException extends RuntimeException {
// 
//     private final static long serialVersionUID = 1;
//     
//     /**
//      * Creates a new instance of {@code ReadOnlyException}.
//      *
//      */
//     public ReadOnlyException() {
//         super("read-only");
//     }
// }


// Path: src/main/java/de/waldheinz/fs/fat/FatFileSystem.java
import de.waldheinz.fs.ReadOnlyException;
import de.waldheinz.fs.AbstractFileSystem;
import de.waldheinz.fs.BlockDevice;
import java.io.IOException;

                    f32bs.getRootDirFirstCluster(), isReadOnly());
            this.rootDirStore = ClusterChainDirectory.readRoot(rootChain);
            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)