org.panbox.desktop.common.vfs.backend.generic.GenericVirtualFileImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.panbox.desktop.common.vfs.backend.generic.GenericVirtualFileImpl.java

Source

/*
 * 
 *               Panbox - encryption for cloud storage 
 *      Copyright (C) 2014-2015 by Fraunhofer SIT and Sirrix AG 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Additonally, third party code may be provided with notices and open source
 * licenses from communities and third parties that govern the use of those
 * portions, and any licenses granted hereunder do not alter any rights and
 * obligations you may have under such open source licenses, however, the
 * disclaimer of warranty and limitation of liability provisions of the GPLv3 
 * will apply to all the product.
 * 
 */
package org.panbox.desktop.common.vfs.backend.generic;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.Hashtable;

import javax.crypto.SecretKey;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.panbox.core.crypto.io.AESGCMRandomAccessFileCompat;
import org.panbox.core.exception.FileEncryptionException;
import org.panbox.core.exception.FileIntegrityException;
import org.panbox.core.vfs.backend.VirtualFile;
import org.panbox.core.vfs.backend.VirtualVolume;
import org.panbox.desktop.common.ex.PanboxEncryptionException;
import org.panbox.desktop.common.ex.PanboxIntegrityException;
import org.panbox.desktop.common.vfs.backend.VirtualRandomAccessFile;

public class GenericVirtualFileImpl extends VirtualRandomAccessFile {

    protected File file;
    protected AESGCMRandomAccessFileCompat aesRandomAccessFile;

    protected boolean deleteFileOnClose = false;

    private final static Logger logger = Logger.getLogger(GenericVirtualFileImpl.class);

    /**
     * stores number of open file handles for each opened file
     */
    final static Hashtable<AESGCMRandomAccessFileCompat, Integer> filehandleCtrMap = new Hashtable<AESGCMRandomAccessFileCompat, Integer>();

    protected GenericVirtualFileImpl(String fileName, VirtualVolume volume) {
        super(fileName, volume);
        file = new File(fileName);
        try {
            if (!isDirectory()) {
                if (exists()) {
                    aesRandomAccessFile = file.canWrite() ? AESGCMRandomAccessFileCompat.getInstance(file, true)
                            : AESGCMRandomAccessFileCompat.getInstance(file, false);
                    // System.err.println("getInstance(" + file.getName() + ","
                    // + aesRandomAccessFile.writable + ") -> "
                    // + aesRandomAccessFile.toString());
                } else {
                    aesRandomAccessFile = file.getParentFile().canWrite()
                            ? AESGCMRandomAccessFileCompat.getInstance(file, true)
                            : AESGCMRandomAccessFileCompat.getInstance(file, false);
                    // System.err.println("getInstance(" + file.getName() + ","
                    // + aesRandomAccessFile.writable + ") -> "
                    // + aesRandomAccessFile.toString());
                }
            }
        } catch (IOException | FileEncryptionException e) {
            logger.error("Unable to initialize AES backend", e);
        }
    }

    @Override
    public boolean isDirectory() {
        return file.isDirectory();
    }

    @Override
    public boolean delete() {
        if (file.isDirectory()) {
            try {
                FileUtils.deleteDirectory(file);
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        } else {
            return file.delete();
        }
    }

    @Override
    public boolean createNewDirectory() {
        return file.mkdirs();
    }

    @Override
    public boolean exists() {
        return file.exists();
    }

    @Override
    public long length() throws IOException {
        // return aesRandomAccessFile.length();
        return AESGCMRandomAccessFileCompat.realToVirtualFileSize(file.length());
    }

    @Override
    public VirtualFile[] list() {
        String[] files = file.list();

        GenericVirtualFileImpl[] fileList = new GenericVirtualFileImpl[files.length];

        for (int i = 0; i < files.length; ++i) {
            fileList[i] = new GenericVirtualFileImpl(new File(file, files[i]).getPath(), volume);
        }

        return fileList;
    }

    @Override
    public String getPath() {
        return file.getPath();
    }

    @Override
    public String getRelativePath() {
        return volume.getRelativePathForFile(this);
    }

    @Override
    public boolean renameTo(VirtualFile newFile) {
        if (isDirectory()) {
            return file.renameTo(newFile.getFile());
        } else {
            // use backend rename function
            GenericVirtualFileImpl newDFile = (GenericVirtualFileImpl) newFile;
            if (aesRandomAccessFile.renameTo(newDFile.getFile())) {
                this.file = newDFile.getFile();
                return true;
            } else {
                return false;
            }
        }
    }

    @Override
    public File getFile() {
        return file;
    }

    @Override
    public String toString() {
        return file.getName() + " at " + file.getAbsoluteFile();
    }

    @Override
    public long getCreationTime() {
        try {
            BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class,
                    LinkOption.NOFOLLOW_LINKS);
            return attr.creationTime().toMillis();
        } catch (IOException e) {
            logger.error("Error in getCreationTime()", e);
            return 0;
        }
    }

    @Override
    public long getLastAccessTime() {
        try {
            BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class,
                    LinkOption.NOFOLLOW_LINKS);
            return attr.lastAccessTime().toMillis();
        } catch (IOException e) {
            logger.error("Error in getLastAccessTime()", e);
            return 0;
        }
    }

    @Override
    public long getLastWriteTime() {
        // return file.lastModified();
        try {
            BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class,
                    LinkOption.NOFOLLOW_LINKS);
            return attr.lastModifiedTime().toMillis();
        } catch (IOException e) {
            logger.error("Error in getLastWriteTime()", e);
            return 0;
        }
    }

    @Override
    public void setLastAccessTime(long atime) throws IOException {
        if (atime > 0) {
            FileTime fileTime = FileTime.fromMillis(atime);
            Files.setAttribute(file.toPath(), "lastAccessTime", fileTime);
        }
    }

    @Override
    public void setModifiedTime(long mtime) throws IOException {
        if (mtime > 0) {
            FileTime fileTime = FileTime.fromMillis(mtime);
            Files.setLastModifiedTime(file.toPath(), fileTime);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#seek(long)
     */
    @Override
    public void seek(long pos) throws IOException, PanboxEncryptionException {
        try {
            aesRandomAccessFile.seek(pos);
        } catch (IOException e) {
            throw new IOException(e.getMessage(), e);
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#getFilePointer()
     */
    @Override
    public long getFilePointer() throws IOException {
        try {
            return aesRandomAccessFile.getFilePointer();
        } catch (IOException e) {
            throw new IOException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#skipBytes(int)
     */
    @Override
    public int skipBytes(int n) throws IOException, PanboxEncryptionException {
        try {
            return aesRandomAccessFile.skipBytes(n);
        } catch (IOException e) {
            throw new IOException(e.getMessage(), e);
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#write(byte[])
     */
    @Override
    public void write(byte[] b) throws IOException, PanboxEncryptionException {
        try {
            aesRandomAccessFile.write(b);
        } catch (IOException e) {
            throw new IOException(e.getMessage(), e);
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        } catch (FileIntegrityException e) {
            throw new PanboxIntegrityException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#write(long,
     * java.nio.ByteBuffer)
     */
    @Override
    public int write(long seekpos, ByteBuffer b) throws IOException, PanboxEncryptionException {
        try {
            aesRandomAccessFile.seek(seekpos);
            byte[] buf;
            if (b.hasArray()) {
                buf = b.array();
                this.write(buf, 0, buf.length);
            } else {
                buf = new byte[b.remaining()];
                b.get(buf);
                this.write(buf, 0, buf.length);
            }
            return buf.length;
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#write(long,
     * java.nio.ByteBuffer)
     */
    @Override
    public int write(long seekpos, byte[] b) throws IOException, PanboxEncryptionException {
        try {
            aesRandomAccessFile.seek(seekpos);
            this.write(b, 0, b.length);
            return b.length;
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#write(byte[], int,
     * int)
     */
    @Override
    public void write(byte[] b, int off, int len) throws IOException, PanboxEncryptionException {
        try {
            aesRandomAccessFile.write(b, off, len);
        } catch (IOException e) {
            throw new IOException(e.getMessage(), e);
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        } catch (FileIntegrityException e) {
            throw new PanboxIntegrityException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#write(int)
     */
    @Override
    public void write(int b) throws IOException, PanboxEncryptionException {
        try {
            aesRandomAccessFile.write(b);
        } catch (IOException e) {
            throw new IOException(e.getMessage(), e);
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        } catch (FileIntegrityException e) {
            throw new PanboxIntegrityException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#read(byte[], int,
     * int)
     */
    @Override
    public int read(byte[] b, int off, int len) throws IOException, PanboxEncryptionException {
        try {
            return aesRandomAccessFile.read(b, off, len);
        } catch (IOException e) {
            throw new IOException(e.getMessage(), e);
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        } catch (FileIntegrityException e) {
            throw new PanboxIntegrityException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#read(byte[])
     */
    @Override
    public int read(byte[] b) throws IOException, PanboxEncryptionException {
        try {
            return aesRandomAccessFile.read(b);
        } catch (IOException e) {
            throw new IOException(e.getMessage(), e);
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        } catch (FileIntegrityException e) {
            throw new PanboxIntegrityException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#read(long,
     * java.nio.ByteBuffer)
     */
    @Override
    public int read(long seekpos, ByteBuffer b) throws IOException, PanboxEncryptionException {
        byte[] buf = new byte[b.remaining()]; // raf.array() does not
        // work, because backend
        // byte[] is missing!
        try {
            aesRandomAccessFile.seek(seekpos);
            int reallyRead = aesRandomAccessFile.read(buf, 0, buf.length);
            if (reallyRead != -1) {
                // Trim NUL-Bytes from file
                b.put(buf, 0, reallyRead);
                return reallyRead;
            } else {
                // EOF is indicated by -1; returning -1 will be misinterpreted
                // as an ERRNO (i.e. EPERM)
                return 0;
            }
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        } catch (FileIntegrityException e) {
            throw new PanboxIntegrityException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#read(long,
     * java.nio.ByteBuffer)
     */
    @Override
    public int read(long seekpos, byte[] b) throws IOException, PanboxEncryptionException {
        try {
            aesRandomAccessFile.seek(seekpos);
            int reallyRead = aesRandomAccessFile.read(b);
            if (reallyRead == -1) {
                // EOF is indicated by -1; returning -1 will be misinterpreted
                // as an ERRNO (i.e. EPERM)
                return 0;
            } else {
                return reallyRead;
            }
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        } catch (FileIntegrityException e) {
            throw new PanboxIntegrityException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#read()
     */
    @Override
    public int read() throws IOException, PanboxEncryptionException {
        try {
            return aesRandomAccessFile.read();
        } catch (IOException e) {
            throw new IOException(e.getMessage(), e);
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        } catch (FileIntegrityException e) {
            throw new PanboxIntegrityException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#open(java.io.File,
     * boolean)
     */
    @Override
    public void open() throws IOException, PanboxEncryptionException {
        try {
            // windows verifies file existance by calling create(OPEN_EXISTING)
            // - thus check if file exists at an earlypoint in time
            if (!file.exists()) {
                throw new IOException("File " + file + "does not exist!");
            } else {

                // TODO: support read-only mode, use fileinfo-objects
                // check file handle counter maps and create/increase values
                synchronized (filehandleCtrMap) {
                    Integer fhctr = filehandleCtrMap.get(aesRandomAccessFile);
                    // System.err.println("open(" + file.getName() + ","
                    // + aesRandomAccessFile.writable + ", ctr=" + fhctr
                    // + ") -> " + aesRandomAccessFile.toString());
                    if (fhctr != null) {
                        // file has already been opened - only increase counter
                        filehandleCtrMap.put(aesRandomAccessFile, ++fhctr);
                    } else {
                        aesRandomAccessFile.open();
                        filehandleCtrMap.put(aesRandomAccessFile, 1);
                    }
                }
            }
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#create(int,
     * javax.crypto.SecretKey, java.io.File)
     */
    @Override
    public void create(int shareKeyVersion, SecretKey shareKey) throws IOException, PanboxEncryptionException {
        try {
            // don't check if file has been opened - multiple create() calls on
            // the same file should cause an exception
            // TODO: check error handling + mapping to error codes
            aesRandomAccessFile.create(shareKeyVersion, shareKey);
            // System.err.println("create(" + file.getName() + ","
            // + aesRandomAccessFile.writable + ") -> "
            // + aesRandomAccessFile.toString());

            // check file handle counter maps and create/increase values
            synchronized (filehandleCtrMap) {
                Integer fhctr = filehandleCtrMap.get(aesRandomAccessFile);
                if (fhctr == null) {
                    filehandleCtrMap.put(aesRandomAccessFile, 1);
                }
            }

        } catch (IOException e) {
            throw new IOException(e.getMessage(), e);
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#flush()
     */
    @Override
    public void flush() throws IOException {
        aesRandomAccessFile.flush();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#close()
     */
    @Override
    public void close() throws IOException {
        if (!file.exists()) {
            throw new IOException("File " + file + "does not exist!");
        } else {
            synchronized (filehandleCtrMap) {
                Integer fhctr = filehandleCtrMap.get(aesRandomAccessFile);
                if ((fhctr == null) || (fhctr < 1)) {
                    throw new IOException("Invalid value of file handle counter!");
                } else {
                    if (fhctr == 1) {
                        // last reference, close file and remove entry
                        aesRandomAccessFile.close();
                        // System.err.println("close(" + file.getName() + ","
                        // + aesRandomAccessFile.writable + ") -> "
                        // + aesRandomAccessFile.toString());
                        filehandleCtrMap.remove(aesRandomAccessFile);

                        if (deleteFileOnClose) {
                            delete();
                        }
                    } else if (fhctr > 1) {
                        // decrease counter value, but don't close file
                        filehandleCtrMap.put(aesRandomAccessFile, --fhctr);
                    }
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#setDeleteOnClose()
     */
    @Override
    public void setDeleteOnClose() {
        deleteFileOnClose = true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.panbox.desktop.common.vfs.backend.VirtualFile#getShareKeyVersion()
     */
    @Override
    public int getShareKeyVersion() throws PanboxEncryptionException, IOException {
        try {
            return aesRandomAccessFile.getShareKeyVersion();
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.panbox.desktop.common.vfs.backend.VirtualFile#initWithShareKey(javax
     * .crypto .SecretKey)
     */
    @Override
    public void initWithShareKey(SecretKey shareKey) throws PanboxEncryptionException, IOException {
        try {
            aesRandomAccessFile.initWithShareKey(shareKey);
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        } catch (FileIntegrityException e) {
            throw new PanboxIntegrityException(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.panbox.desktop.common.vfs.backend.VirtualFile#setLength(long)
     */
    @Override
    public void setLength(long newLength) throws IOException, PanboxEncryptionException {
        try {
            aesRandomAccessFile.setLength(newLength);
        } catch (FileEncryptionException e) {
            throw new PanboxEncryptionException(e.getMessage(), e);
        } catch (FileIntegrityException e) {
            throw new PanboxIntegrityException(e.getMessage(), e);
        }
    }

    @Override
    public boolean isOpened() {
        return aesRandomAccessFile.isOpen();
    }

    @Override
    public boolean isSymbolic() {
        return Files.isSymbolicLink(getFile().toPath());
    }

    @Override
    public void setAttr(long attr) throws IOException {
        super.setAttr(attr);
    }

    @Override
    public boolean canExecute() {
        return file.canExecute();
    }

    @Override
    public boolean canRead() {
        return file.canRead();
    }

    @Override
    public boolean canWrite() {
        return file.canWrite();
    }
}