Package org.jnode.fs.fat

Source Code of org.jnode.fs.fat.FatFile

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* 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 org.jnode.fs.fat;

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

import org.jnode.driver.block.BlockDeviceAPI;
import org.jnode.fs.FSFile;
import org.jnode.fs.ReadOnlyFileSystemException;

/**
* A File instance is the in-memory representation of a single file (chain of
* clusters).
*
* @author epr
*/
public class FatFile extends FatObject implements FSFile {
    private long startCluster;
    private long length;
    private FatDirectory dir;
    private int clusterSize;
    private boolean isDir;
    private final FatDirEntry myEntry;

    public FatFile(FatFileSystem fs, FatDirEntry myEntry, long startCluster, long length,
            boolean isDir) {
        super(fs);
        this.myEntry = myEntry;
        this.startCluster = startCluster;
        this.length = length;
        this.clusterSize = fs.getClusterSize();
        this.isDir = isDir;
    }

    public synchronized void read(long fileOffset, ByteBuffer destBuf) throws IOException {
        int len = destBuf.remaining();

        final long max = (isDir) ? getLengthOnDisk() : getLength();
        if (fileOffset + len > max) {
            throw new IOException("Cannot read beyond the EOF");
        }

        final FatFileSystem fs = getFatFileSystem();
        final long[] chain = fs.getFat().getChain(startCluster);
        final BlockDeviceAPI api = fs.getApi();

        int chainIdx = (int) (fileOffset / clusterSize);
        if (fileOffset % clusterSize != 0) {
            int clusOfs = (int) (fileOffset % clusterSize);
            int size = Math.min(len, (int) (clusterSize - (fileOffset % clusterSize) - 1));
            destBuf.limit(destBuf.position() + size);
            api.read(getDevOffset(chain[chainIdx], clusOfs), destBuf);
            fileOffset += size;
            len -= size;
            chainIdx++;
        }
        while (len > 0) {
            int size = Math.min(clusterSize, len);
            destBuf.limit(destBuf.position() + size);
            api.read(getDevOffset(chain[chainIdx], 0), destBuf);
            len -= size;
            chainIdx++;
        }
    }

    public synchronized void write(long fileOffset, ByteBuffer srcBuf) throws IOException {
        int len = srcBuf.remaining();

        if (getFileSystem().isReadOnly()) {
            throw new ReadOnlyFileSystemException("write in readonly filesystem");
        }

        final long max = (isDir) ? getLengthOnDisk() : getLength();
        if (fileOffset > max) {
            throw new IOException("Cannot write beyond the EOF");
        }

        if (fileOffset + len > max) { // this is too short increase the size
                                        // of the file
            setLength(fileOffset + len);
        }

        final FatFileSystem fs = getFatFileSystem();
        final long[] chain = fs.getFat().getChain(getStartCluster());
        final BlockDeviceAPI api = fs.getApi();

        int chainIdx = (int) (fileOffset / clusterSize);
        if (fileOffset % clusterSize != 0) {
            int clusOfs = (int) (fileOffset % clusterSize);
            int size = Math.min(len, (int) (clusterSize - (fileOffset % clusterSize) - 1));
            srcBuf.limit(srcBuf.position() + size);
            api.write(getDevOffset(chain[chainIdx], clusOfs), srcBuf);
            fileOffset += size;
            len -= size;
            chainIdx++;
        }
        while (len > 0) {
            int size = Math.min(clusterSize, len);
            srcBuf.limit(srcBuf.position() + size);
            api.write(getDevOffset(chain[chainIdx], 0), srcBuf);
            len -= size;
            chainIdx++;
        }
    }

    /**
     * Sets the length.
     *
     * @param length The length to set
     */
    public synchronized void setLength(long length) throws IOException {

        if (getFileSystem().isReadOnly()) {
            throw new ReadOnlyFileSystemException("setLength in readonly filesystem");
        }

        if (this.length == length) {
            // Do nothing
            return;
        }

        final FatFileSystem fs = getFatFileSystem();
        final Fat fat = fs.getFat();
        final int nrClusters = (int) ((length + clusterSize - 1) / clusterSize);

        if (this.length == 0) {
            final long[] chain = fat.allocNew(nrClusters);
            this.startCluster = chain[0];
            this.myEntry.setStartCluster((int) startCluster);
        } else {
            final long[] chain = fs.getFat().getChain(startCluster);

            if (nrClusters != chain.length) {
                if (nrClusters > chain.length) {
                    // Grow
                    int count = nrClusters - chain.length;
                    while (count > 0) {
                        fat.allocAppend(getStartCluster());
                        count--;
                    }
                } else {
                    // Shrink
                    fat.setEof(chain[nrClusters - 1]);
                    for (int i = nrClusters; i < chain.length; i++) {
                        fat.setFree(chain[i]);
                    }
                }
            }
        }

        this.length = length;
        this.myEntry.updateLength(length);
    }

    /**
     * Returns the length.
     *
     * @return long
     */
    public long getLength() {
        return length;
    }

    /**
     * Gets the size this file occupies on disk
     *
     * @return long
     */
    public long getLengthOnDisk() {
        if (this.length == 0) {
            return 0;
        } else {
            final FatFileSystem fs = getFatFileSystem();
            final long[] chain = fs.getFat().getChain(getStartCluster());
            return ((long) chain.length) * fs.getClusterSize();
        }
    }

    /**
     * Returns the startCluster.
     *
     * @return long
     */
    public long getStartCluster() {
        return startCluster;
    }

    /**
     * Gets the directory contained in this file.
     *
     * @return Directory
     */
    public synchronized FatDirectory getDirectory() throws IOException {
        if (dir == null) {
            final FatFileSystem fs = getFatFileSystem();
            dir = new FatLfnDirectory(fs, this);
        }
        return dir;
    }

    /**
     * Calculates the device offset (0-based) for the given cluster and offset
     * within the cluster.
     * @param cluster
     * @param clusterOffset
     * @return long
     */
    protected long getDevOffset(long cluster, int clusterOffset) {
        final FatFileSystem fs = getFatFileSystem();
        final long filesOffset = FatUtils.getFilesOffset(fs.getBootSector());
        return filesOffset + clusterOffset + ((cluster - FatUtils.FIRST_CLUSTER) * clusterSize);
    }

    /**
     * Flush any changes in this file to persistent storage
     * @throws IOException
     */
    public void flush() throws IOException {
        if (dir != null) {
            dir.flush();
        }
    }
}
TOP

Related Classes of org.jnode.fs.fat.FatFile

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.