/* * Copyright (C) 2010-2015, Martin Goellnitz * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301, USA */ package jfs.sync.encfs; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import jfs.conf.JFSConfig; import jfs.sync.JFSFile; import jfs.sync.JFSFileProducer; import jfs.sync.base.AbstractJFSFileProducerFactory; import jfs.sync.encryption.FileInfo; import org.mrpdaemon.sec.encfs.EncFSFile; import org.mrpdaemon.sec.encfs.EncFSVolume; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Represents a file that is stored as EncFS file locally. * * @author Martin Goellnitz * */ public class JFSEncfsFile extends JFSFile { private static final Logger LOG = LoggerFactory.getLogger(JFSEncfsFile.class); /** * The retrieved file information object from the server. */ private final FileInfo info; private EncFSFile file = null; /** * The list of included files. */ private JFSFile[] list = null; private EncFSVolume access; private OutputStream output = null; InputStream input = null; /** * Creates a new external file for a certain path using a specific file producer. * * @param access * The server access object to use. * @param fileProducer * The assigned file producer. * @param path * The path to create the external file for. */ public JFSEncfsFile(EncFSVolume access, JFSFileProducer fileProducer, String path, boolean isDirectory) { super(fileProducer, path); path = path.replace(File.separatorChar, '/'); this.access = access; info = new FileInfo(); try { String[] pathAndName = AbstractJFSFileProducerFactory.getPathAndName(path, "/"); info.setPath(pathAndName[0]); info.setName(pathAndName[1]); info.setDirectory(isDirectory); try { file = access.getFile(path.length()==0 ? "/" : path); info.setCanRead(file.isReadable()); info.setCanWrite(file.isWritable()); info.setPath(file.getParentPath()); info.setName(path.length()==0 ? "" : file.getName()); info.setDirectory(file.isDirectory()); info.setExists(file.isReadable()); info.setModificationDate(file.getLastModified()); info.setSize(file.getLength()); } catch (IllegalArgumentException iae) { LOG.warn("()", iae); info.setExists(false); } // try/catch } catch (Exception e) { LOG.error("()", e); } // try/catch if (LOG.isInfoEnabled()) { LOG.info("() "+(info.isDirectory() ? "d" : "-")+(info.isExists() ? "e" : "-")+" | "+info.getPath()+"/"+info.getName()); } // if } // JFSEncfsFile() /** * Creates a new external root file and reads the structure from server. * * @param access * The server access object to use. * @param fileProducer * The assigned file producer. */ public JFSEncfsFile(EncFSVolume access, JFSFileProducer fileProducer) { this(access, fileProducer, "", true); } // JFSWebDavFile() /** * @see JFSFile#canRead() */ @Override public boolean canRead() { return info.isCanRead(); } /** * @see JFSFile#canWrite() */ @Override public boolean canWrite() { return info.isCanWrite(); } /** * @see JFSFile#getInputStream() */ @Override protected InputStream getInputStream() { LOG.debug("getInputStream() file {}", file.getPath()); try { input = file.openInputStream(); } catch (Exception e) { LOG.error("getInputStream()", e); } // try/catch return input; } // getInputStream() /** * @see JFSFile#getOutputStream() */ @Override protected OutputStream getOutputStream() { LOG.debug("getOutputStream()"); try { if (!info.isExists()) { file = access.createFile(getPath()); info.setCanRead(file.isReadable()); info.setCanWrite(file.isWritable()); info.setExists(true); info.setPath(file.getParentPath()); info.setName(file.getName()); info.setDirectory(file.isDirectory()); info.setSize(file.isDirectory() ? 0 : file.getLength()); info.setModificationDate(file.getLastModified()); } // if output = file.openOutputStream(-1); } catch (Exception e) { LOG.error("getOutputStream()", e); } // try/catch return output; } // getOutputStream() /** * @see JFSFile#closeInputStream() */ @Override protected void closeInputStream() { if (input!=null) { try { input.close(); } catch (IOException e) { LOG.error("closeInputStream()", e); } // try/catch input = null; } // if } // closeInputStream() /** * @see JFSFile#closeOutputStream() */ @Override protected void closeOutputStream() { LOG.debug("getOutputStream()"); if (output!=null) { try { output.close(); } catch (IOException e) { LOG.error("closeOutputStream()", e); } // try/catch output = null; } // if } // closeOutputStream() /** * @see JFSFile#delete() */ @Override public boolean delete() { LOG.debug("delete() deleting {}", file.getPath()); boolean result = false; try { result = file.delete(); } catch (IOException e) { LOG.error("delete()", e); } // try/catch return result; } // delete() /** * @see JFSFile#exists() */ @Override public boolean exists() { return info.isExists(); } /** * @see JFSFile#getLastModified() */ @Override public long getLastModified() { return info.getModificationDate(); } /** * @see JFSFile#getLength() */ @Override public long getLength() { return info.getSize(); } /** * @see JFSFile#getList() */ @Override public JFSFile[] getList() { LOG.debug("getList() listing {}", file.getPath()); if (list==null) { list = new JFSEncfsFile[0]; if (isDirectory()) { try { EncFSFile[] listing = file.listFiles(); list = new JFSEncfsFile[listing.length]; int i = 0; for (EncFSFile f : listing) { if (LOG.isDebugEnabled()) { LOG.debug("getList("+i+") listing "+f.getPath()); } // if list[i] = new JFSEncfsFile(access, fileProducer, f.getPath(), f.isDirectory()); i++; } // for } catch (Exception e) { LOG.error("getList()", e); } // try/catch } // if } // getList() return list; } // getList() /** * @see JFSFile#getName() */ @Override public String getName() { return info.getName(); } /** * @see JFSFile#getPath() */ @Override public String getPath() { return (info.getPath().length()>1 ? info.getPath() : "")+"/"+info.getName(); } /** * @see JFSFile#isDirectory() */ @Override public boolean isDirectory() { return info.isDirectory(); } /** * @see JFSFile#mkdir() */ @Override public boolean mkdir() { boolean result = false; try { String path = info.getPath()+"/"+info.getName(); LOG.debug("mkdir() creating {}", path); result = access.makeDir(path); if (result) { file = access.getFile(path); } // if } catch (Exception e) { LOG.error("mkdir()", e); } // try/catch return result; } // mkdir() /** * @see JFSFile#setLastModified(long) */ @Override public boolean setLastModified(long time) { LOG.debug("setLastModified() {}/{}", info.getPath(), info.getName()); boolean success = false; info.setModificationDate(time); if (LOG.isDebugEnabled()) { LOG.debug("setLastModified() "+file.getParentPath()+":"+file.getName()+" - "+file.getPath()); } // if // TODO: Maybe fix this somewhere else... String encryptedPath = file.getEncryptedPath(); int idx = encryptedPath.startsWith("//") ? 1 : 0; String encPath = getFileProducer().getRootPath()+encryptedPath.substring(idx); File encFile = new File(encPath); LOG.debug("setLastModified({}) {}", encPath, encFile.exists()); if (encFile.exists()) { encFile.setLastModified(info.getModificationDate()); } // if return success; } // setLastModified() /** * @see JFSFile#setReadOnly() */ @Override public boolean setReadOnly() { if (!JFSConfig.getInstance().isSetCanWrite()) { return true; } info.setCanWrite(false); return true; } /** * @see JFSFile#preCopyTgt(JFSFile) */ @Override protected boolean preCopyTgt(JFSFile srcFile) { LOG.debug("preCopyTgt() {}/{}", info.getPath(), info.getName()); info.setModificationDate(srcFile.getLastModified()); // Set last modified and read-only only when file is no directory: if (!srcFile.isDirectory()) { info.setSize(srcFile.getLength()); if (!srcFile.canWrite()) { info.setCanWrite(false); } } // if return true; } // preCopyTgt() /** * @see JFSFile#preCopySrc(JFSFile) */ @Override protected boolean preCopySrc(JFSFile tgtFile) { return true; } /** * @see JFSFile#postCopyTgt(JFSFile) */ @Override protected boolean postCopyTgt(JFSFile srcFile) { LOG.debug("postCopyTgt() {}", srcFile.getPath()); // Update information object after copy. This method is only // called if all operations were performed successfully: info.setDirectory(srcFile.isDirectory()); info.setExists(srcFile.exists()); info.setSize(srcFile.getLength()); setLastModified(srcFile.getLastModified()); return true; } // postCopyTgt() /** * @see JFSFile#postCopySrc(JFSFile) */ @Override protected boolean postCopySrc(JFSFile tgtFile) { return true; } /** * @see JFSFile#flush() */ @Override public boolean flush() { return true; } } // JFSEncfsFile