/* * EncFS Java Library * Copyright (C) 2011 Mark R. Pariente * * This program 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 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 Lesser General Public License for more details. */ package org.mrpdaemon.sec.encfs; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Object representing a file in an EncFS volume. * <p/> * Useful for decryption of file names as well as performing file operations * such as copying, moving, deletion etc. */ public class EncFSFile { /** * Size in bytes of file header when file IV's are used (uniqueIV) */ public static final int HEADER_SIZE = 8; // Volume hosting this file private final EncFSVolume volume; // Information about the plaintext file (decoded names etc.) private final EncFSFileInfo plainFileInfo; // Information about the ciphertext file (encrypted names etc.) private final EncFSFileInfo cipherFileInfo; /** * Create a new object representing a file in an EncFS volume * * @param volume * The volume that contains the file * @param plainFileInfo * EncFSFileInfo representing the cleartext file (decoded) * @param cipherFileInfo * EncFSFileInfo representing the ciphertext file (encoded) */ public EncFSFile(EncFSVolume volume, EncFSFileInfo plainFileInfo, EncFSFileInfo cipherFileInfo) { this.volume = volume; this.plainFileInfo = plainFileInfo; this.cipherFileInfo = cipherFileInfo; } /** * Returns the volume path of this file * * @return Absolute volume path of the file */ public String getPath() { return plainFileInfo.getPath(); } /** * Returns the encrypted volume path of this file * * @return Encrypted volume path of the file */ public String getEncryptedPath() { return cipherFileInfo.getPath(); } /** * Returns the volume path of the parent directory hosting this file * <p/> * This is the plaintext path starting from the volume's root up until the * parent directory hosting the current file * * @return Volume path of this file's parent directory */ public String getParentPath() { return plainFileInfo.getParentPath(); } /** * Returns the encrypted volume path of the parent directory hosting this * file * * @return Encrypted volume path of this file's parent directory */ public String getEncryptedParentPath() { return cipherFileInfo.getParentPath(); } /** * Returns the plaintext name of this file * * @return decrypted name of this EncFS file */ public String getName() { return plainFileInfo.getName(); } /** * Returns the ciphertext name of this file * * @return encrypted name of this EncFS file */ public String getEncrytedName() { return cipherFileInfo.getName(); } /** * Returns the length of decrypted contents for this file * * @return Length of decrypted file contents */ public long getLength() { return plainFileInfo.getSize(); } /** * Returns the last modification time for this file * * @return last modification time for the file */ public long getLastModified() { return plainFileInfo.getLastModified(); } /** * Returns the volume containing this file * * @return Volume containing the EncFS file */ public EncFSVolume getVolume() { return volume; } /** * Checks whether this EncFSFile represents a directory * * @return true if directory, false otherwise */ public boolean isDirectory() { return plainFileInfo.isDirectory(); } /** * Checks whether the file is readable * * @return true if the file is readable, false otherwise */ public boolean isReadable() { return plainFileInfo.isReadable(); } /** * Checks whether the file is writable * * @return true if the file is writable, false otherwise */ public boolean isWritable() { return plainFileInfo.isWritable(); } /** * Checks whether the file is executable * * @return true if the file is executable, false otherwise */ public boolean isExecutable() { return plainFileInfo.isExecutable(); } /** * List files/directory names contained by the directory represented by this * EncFSFile object. */ public String[] list() throws IOException { EncFSFile[] files = listFiles(); String[] fileNames = null; if (files != null) { fileNames = new String[files.length]; for (int i = 0; i < files.length; i++) { EncFSFile file = files[i]; fileNames[i] = file.getName(); } } return fileNames; } /** * Get list of EncFSFile's under this directory * * @return list of EncFSFile under the given directory */ public EncFSFile[] listFiles() throws IOException { if (!isDirectory()) { return null; } String encDirName; if (this == volume.getRootDir()) { encDirName = EncFSVolume.ROOT_PATH; } else { encDirName = getEncryptedPath(); } String dirName = getPath(); List<EncFSFileInfo> fileInfos = volume.getFileProvider().listFiles( encDirName); List<EncFSFile> result = new ArrayList<EncFSFile>(fileInfos.size()); for (EncFSFileInfo fileInfo : fileInfos) { String decodedFileName; try { decodedFileName = EncFSCrypto.decodeName(volume, fileInfo.getName(), dirName); } catch (EncFSCorruptDataException e) { decodedFileName = null; } catch (EncFSChecksumException e) { decodedFileName = null; } if (decodedFileName != null) { EncFSFileInfo decEncFileInfo = EncFSFileInfo .getDecodedFileInfo(volume, dirName, decodedFileName, fileInfo); result.add(new EncFSFile(volume, decEncFileInfo, fileInfo)); } } return result.toArray(new EncFSFile[result.size()]); } /** * Delete this file * * @return true if deletion succeeds, false otherwise */ public boolean delete() throws IOException { return volume.getFileProvider().delete(getEncryptedPath()); } /** * Opens the file as an EncFSInputStream that decodes the file contents * automatically * * @return EncFSInputStream that decodes file contents */ public EncFSInputStream openInputStream() throws EncFSCorruptDataException, EncFSUnsupportedException, IOException { return new EncFSInputStream(volume, volume.getFileProvider() .openInputStream(getEncryptedPath()), getPath()); } /** * Opens the file as an EncFSOutputStream that encrypts the file contents * automatically * * @param inputLength * Length of the input file that will be written to this output * stream. Note that this parameter is optional if using * EncFSLocalFileProvider, but some network based storage API's * require knowing the file length in advance. * @return EncFSOutputStream that encrypts file contents */ public EncFSOutputStream openOutputStream(long inputLength) throws EncFSUnsupportedException, EncFSCorruptDataException, IOException { return new EncFSOutputStream(volume, volume.getFileProvider() .openOutputStream(getEncryptedPath(), volume.getEncryptedFileLength(inputLength)), getPath()); } /** * Copies this file/dir to a target file or directory * * @param dstPath * EncFSFile representing the target file or directory * @return true if copy succeeds, false otherwise */ public boolean copy(EncFSFile dstPath) throws IOException { if (isDirectory()) { // Recursive copy of this directory to the target path try { return volume.copyPath(getPath(), dstPath.getPath()); } catch (EncFSCorruptDataException e) { throw new IOException(e); } } else if (dstPath.isDirectory()) { /* * Trying to copy a file to a directory, copy it UNDER that * directory instead */ EncFSFile realDstPath; try { realDstPath = volume.createFile(dstPath.getPath(), getName()); } catch (EncFSCorruptDataException e) { throw new IOException(e); } return copy(realDstPath); } else { // Trying to copy a file into a file if (volume.getConfig().isSupportedExternalIVChaining()) { /* * Need to re-encrypt the file since external IV chaining is * used. */ try { EncFSUtil.copyWholeStreamAndClose(openInputStream(), dstPath.openOutputStream(getLength())); } catch (EncFSException e) { throw new IOException(e); } return true; } else { // Simply copy the file using the file provider return volume.getFileProvider().copy(getEncryptedPath(), dstPath.getEncryptedPath()); } } } }