Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2009,2010 Matthias Treydte <mt (at) 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.util;
     20 
     21 import de.waldheinz.fs.BlockDevice;
     22 import de.waldheinz.fs.ReadOnlyException;
     23 import java.io.File;
     24 import java.io.FileNotFoundException;
     25 import java.io.IOException;
     26 import java.io.RandomAccessFile;
     27 import java.nio.ByteBuffer;
     28 import java.nio.channels.FileChannel;
     29 
     30 /**
     31  * This is a {@code BlockDevice} that uses a {@link File} as it's backing store.
     32  *
     33  * @author Matthias Treydte &lt;matthias.treydte at meetwise.com&gt;
     34  */
     35 public final class FileDisk implements BlockDevice {
     36 
     37     /**
     38      * The number of bytes per sector for all {@code FileDisk} instances.
     39      */
     40     public final static int BYTES_PER_SECTOR = 512;
     41 
     42     private final RandomAccessFile raf;
     43     private final FileChannel fc;
     44     private final boolean readOnly;
     45     private boolean closed;
     46 
     47     /**
     48      * Creates a new instance of {@code FileDisk} for the specified
     49      * {@code File}.
     50      *
     51      * @param file the file that holds the disk contents
     52      * @param readOnly if the file should be opened in read-only mode, which
     53      *      will result in a read-only {@code FileDisk} instance
     54      * @throws FileNotFoundException if the specified file does not exist
     55      * @see #isReadOnly()
     56      */
     57     public FileDisk(File file, boolean readOnly) throws FileNotFoundException {
     58         if (!file.exists()) throw new FileNotFoundException();
     59 
     60         this.readOnly = readOnly;
     61         this.closed = false;
     62         final String modeString = readOnly ? "r" : "rw"; //NOI18N
     63         this.raf = new RandomAccessFile(file, modeString);
     64         this.fc = raf.getChannel();
     65     }
     66 
     67     public FileDisk(RandomAccessFile raf, FileChannel fc, boolean readOnly) {
     68         this.closed = false;
     69         this.raf = raf;
     70         this.fc = fc;
     71         this.readOnly = readOnly;
     72     }
     73 
     74     private FileDisk(RandomAccessFile raf, boolean readOnly) {
     75         this.closed = false;
     76         this.raf = raf;
     77         this.fc = raf.getChannel();
     78         this.readOnly = readOnly;
     79     }
     80 
     81     /**
     82      * Creates a new {@code FileDisk} of the specified size. The
     83      * {@code FileDisk} returned by this method will be writable.
     84      *
     85      * @param file the file to hold the {@code FileDisk} contents
     86      * @param size the size of the new {@code FileDisk}
     87      * @return the created {@code FileDisk} instance
     88      * @throws IOException on error creating the {@code FileDisk}
     89      */
     90     public static FileDisk create(File file, long size) throws IOException {
     91         try {
     92             final RandomAccessFile raf =
     93                     new RandomAccessFile(file, "rw"); //NOI18N
     94             raf.setLength(size);
     95 
     96             return new FileDisk(raf, false);
     97         } catch (FileNotFoundException ex) {
     98             throw new IOException(ex);
     99         }
    100     }
    101 
    102     @Override
    103     public long getSize() throws IOException {
    104         checkClosed();
    105 
    106         return raf.length();
    107     }
    108 
    109     @Override
    110     public void read(long devOffset, ByteBuffer dest) throws IOException {
    111         checkClosed();
    112 
    113         int toRead = dest.remaining();
    114         if ((devOffset + toRead) > getSize()) throw new IOException(
    115                 "reading past end of device");
    116 
    117         while (toRead > 0) {
    118             final int read = fc.read(dest, devOffset);
    119             if (read < 0) throw new IOException();
    120             toRead -= read;
    121             devOffset += read;
    122         }
    123     }
    124 
    125     @Override
    126     public void write(long devOffset, ByteBuffer src) throws IOException {
    127         checkClosed();
    128 
    129         if (this.readOnly) throw new ReadOnlyException();
    130 
    131         int toWrite = src.remaining();
    132 
    133         if ((devOffset + toWrite) > getSize()) throw new IOException(
    134                 "writing past end of file");
    135 
    136         while (toWrite > 0) {
    137             final int written = fc.write(src, devOffset);
    138             if (written < 0) throw new IOException();
    139             toWrite -= written;
    140             devOffset += written;
    141         }
    142     }
    143 
    144     @Override
    145     public void flush() throws IOException {
    146         checkClosed();
    147     }
    148 
    149     @Override
    150     public int getSectorSize() {
    151         checkClosed();
    152 
    153         return BYTES_PER_SECTOR;
    154     }
    155 
    156     @Override
    157     public void close() throws IOException {
    158         if (isClosed()) return;
    159 
    160         this.closed = true;
    161         this.fc.close();
    162         this.raf.close();
    163     }
    164 
    165     @Override
    166     public boolean isClosed() {
    167         return this.closed;
    168     }
    169 
    170     private void checkClosed() {
    171         if (closed) throw new IllegalStateException("device already closed");
    172     }
    173 
    174     @Override
    175     public boolean isReadOnly() {
    176         checkClosed();
    177 
    178         return this.readOnly;
    179     }
    180 
    181 }
    182