/*
*
* 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.windows.vfs;
import static net.decasdev.dokan.WinError.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.FileAlreadyExistsException;
import java.util.Hashtable;
import org.apache.log4j.Logger;
import org.panbox.desktop.common.ex.PanboxCreateFailedException;
import org.panbox.desktop.common.ex.PanboxDeleteFailedException;
import org.panbox.desktop.common.ex.PanboxEncryptionException;
import org.panbox.desktop.common.vfs.AbstractFileInfo;
import org.panbox.desktop.common.vfs.PanboxFS;
import org.panbox.desktop.common.vfs.PanboxFSAdapter;
import net.decasdev.dokan.ByHandleFileInformation;
import net.decasdev.dokan.Dokan;
import net.decasdev.dokan.DokanDiskFreeSpace;
import net.decasdev.dokan.DokanFileInfo;
import net.decasdev.dokan.DokanOperationException;
import net.decasdev.dokan.DokanOperations;
import net.decasdev.dokan.DokanOptions;
import net.decasdev.dokan.DokanVolumeInformation;
import net.decasdev.dokan.FileFlag;
import net.decasdev.dokan.FileShareMode;
import net.decasdev.dokan.Win32FindData;
public class DokanUserFS implements PanboxFSAdapter, DokanOperations {
private static final Logger logger = Logger
.getLogger("org.panbox.desktop.common");
private final Hashtable<Long, Boolean> isDeletableTable = new Hashtable<Long, Boolean>();
private PanboxFSWindows panboxFS = null;
private String mountPoint = null;
// ==== PanboxFSAdapter methods ====
@Override
public boolean userfs_mount(PanboxFS panboxFS, File mountPoint) {
if (!(panboxFS instanceof PanboxFSWindows)) {
logger.fatal(getClass().getName()
+ " : Dokan implementation requires an instance of PanboxFSWindows!");
return false;
} else {
this.panboxFS = (PanboxFSWindows) panboxFS;
this.mountPoint = mountPoint.getAbsolutePath();
try {
Dokan.removeMountPoint(this.mountPoint);
} catch (final Throwable ex) {
// If unmount of existing failed exit with error!
logger.fatal(getClass().getName()
+ " : userfs_mount : Exception: " + ex.getMessage());
return false;
}
final int result = Dokan.mount(new DokanOptions(this.mountPoint, 0,
0), this);
if (result != Dokan.DOKAN_SUCCESS) {
logger.fatal(getClass().getName() + " : Dokan mount failed: "
+ Dokan.getErrorString(result));
}
return result == Dokan.DOKAN_SUCCESS;
}
}
@Override
public boolean userfs_unmount(File mountPoint) {
// Flushes and closes all files
panboxFS.beforeUnmount(mountPoint);
boolean unmount = Dokan.unmount(this.mountPoint.charAt(0));
boolean remove = Dokan.removeMountPoint(mountPoint.getAbsolutePath());
logger.info(getClass().getName()
+ " : userfs_unmount, Unmount successful: " + unmount
+ ", Remove successful: " + remove);
return (unmount && remove);
}
@Override
public AbstractFileInfo createFileInfo(String fileName,
boolean isDirectory, long fileSize, long creationTime,
long lastAccessTime, long lastWriteTime, long attr,
boolean isSymbolic) {
return new FileInfo(fileName, isDirectory, fileSize, creationTime,
lastAccessTime, lastWriteTime);
}
// ==== DokanOperations Windows specific methods ====
@Override
public void onCleanup(String fileName, DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onCleanup (fileName: "
+ fileName + ", fileInfo: " + fileInfo + ")");
try {
panboxFS.cleanup(fileName, fileInfo);
} catch (IOException e) {
logger.error(getClass().getName() + " : onCleanup : Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onCleanup : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public void onCloseFile(String fileName, DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onCloseFile (fileName: "
+ fileName + ", fileInfo: " + fileInfo + ")");
try {
boolean isDeletable = (isDeletableTable.get(fileInfo.handle) != null)
&& (isDeletableTable.get(fileInfo.handle));
panboxFS.closeFile(fileName, fileInfo, isDeletable);
isDeletableTable.remove(fileInfo.handle);
} catch (IOException e) {
logger.error(getClass().getName() + " : onCloseFile : Exception: ",
e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onCloseFile : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public void onCreateDirectory(String fileName, DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onCreateDirectory (fileName: "
+ fileName + ", fileInfo: " + fileInfo + ")");
try {
panboxFS.createDirectory(fileName, fileInfo);
} catch (FileNotFoundException | PanboxCreateFailedException e) {
logger.error(getClass().getName()
+ " : onCreateDirectory : Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (FilenameExceededRangeException e) {
logger.debug(getClass().getName()
+ " : onCreateFile : Path including directoryname was too long on backend. Will return max filename length exceeded error. " + e.getMessage());
throw new DokanOperationException(ERROR_FILENAME_EXCED_RANGE);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onCreateDirectory : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public long onCreateFile(String fileName, int desiredAccess, int shareMode,
int creationDisposition, int flagsAndAttributes,
DokanFileInfo fileInfo) throws DokanOperationException {
logger.debug(getClass().getName() + " : onCreateFile (fileName: "
+ fileName + ", desiredAccess: " + desiredAccess
+ ", shareMode: " + shareMode + ", creationDisposition: "
+ creationDisposition + ", flagsAndAttributes: "
+ flagsAndAttributes + ", fileInfo: " + fileInfo + ")");
try {
// boolean readonly = ((desiredAccess & FileAccess.GENERIC_WRITE) ==
// 0);
// Some applications open files event with FILE_SHARE_DELETE in case
// they don't want to delete it at all!
// Example: Windows Media Player
boolean isDeletable = ((shareMode & FileShareMode.FILE_SHARE_DELETE) != 0
&& (shareMode & FileShareMode.FILE_SHARE_READ) == 0 && (shareMode & FileShareMode.FILE_SHARE_WRITE) == 0);
// Some applications create temp files with deleteOnClose file
// attribute. They will be removed once all handles are closed!
boolean deleteOnClose = ((flagsAndAttributes & FileFlag.FILE_FLAG_DELETE_ON_CLOSE) != 0);
if (!panboxFS.createFile(fileName, creationDisposition,
flagsAndAttributes, isDeletable, deleteOnClose, fileInfo)) {
// exception will be thrown at the end of this method!
} else {
isDeletableTable.put(fileInfo.handle, isDeletable);
return fileInfo.handle;
}
} catch (PathNotFoundException e) {
logger.debug(getClass().getName()
+ " : onCreateFile : Path was not available for the requested file.");
throw new DokanOperationException(ERROR_PATH_NOT_FOUND);
} catch (PanboxCreateFailedException e) {
// ignore non-existing files in log (otherwise log might be full of
// spam!)
if (!e.getMessage().contains(
"Failed to get VirtualFile for filename")) {
logger.error(getClass().getName()
+ " : onCreateFile : Exception: ", e);
}
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (FilenameExceededRangeException e) {
logger.debug(getClass().getName()
+ " : onCreateFile : Path including filename was too long on backend. Will return max filename length exceeded error. " + e.getMessage());
throw new DokanOperationException(ERROR_FILENAME_EXCED_RANGE);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onCreateFile : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
logger.info(getClass().getName()
+ " : onCreateFile : File could not be found. This might be just a is-file-existing-check.");
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
@Override
public void onDeleteDirectory(String fileName, DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onDeleteDirectory (fileName: "
+ fileName + ", fileInfo: " + fileInfo + ")");
try {
panboxFS.deleteDirectory(fileName, fileInfo);
} catch (IOException e) {
logger.error(getClass().getName()
+ " : onDeleteDirectory : Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (PanboxDeleteFailedException e) {
logger.error(getClass().getName()
+ " : onDeleteDirectory : Exception: ", e);
throw new DokanOperationException(ERROR_BUSY);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onDeleteDirectory : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public void onDeleteFile(String fileName, DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onDeleteFile (fileName: "
+ fileName + ", fileInfo: " + fileInfo + ")");
try {
panboxFS.deleteFile(fileName, fileInfo);
} catch (FileNotFoundException e) {
logger.error(
getClass().getName() + " : onDeleteFile : Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (PanboxDeleteFailedException e) {
logger.error(
getClass().getName() + " : onDeleteFile : Exception: ", e);
throw new DokanOperationException(ERROR_BUSY);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onDeleteFile : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public Win32FindData[] onFindFiles(String pathName, DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onFindFiles (pathName: "
+ pathName + ", fileInfo: " + fileInfo + ")");
try {
return panboxFS.findFiles(pathName, null, fileInfo);
} catch (FileNotFoundException e) {
logger.error(getClass().getName() + " : onFindFiles : Exception: ",
e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onFindFiles : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public Win32FindData[] onFindFilesWithPattern(String pathName,
String searchPattern, DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName()
+ " : onFindFilesWithPattern (pathName: " + pathName
+ ", searchPattern: " + searchPattern + ", fileInfo: "
+ fileInfo + ")");
try {
return panboxFS.findFiles(pathName, searchPattern, fileInfo);
} catch (FileNotFoundException e) {
logger.error(getClass().getName()
+ " : onFindFilesWithPattern : Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onFindFilesWithPattern : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public void onFlushFileBuffers(String fileName, DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onFlushFileBuffers (fileName: "
+ fileName + ", fileInfo: " + fileInfo + ")");
try {
panboxFS.flushFileBuffers(fileName, fileInfo);
} catch (IOException e) {
logger.error(getClass().getName()
+ " : onFlushFileBuffers : Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onFlushFileBuffers : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_INVALID_PARAMETER);
}
}
@Override
public DokanDiskFreeSpace onGetDiskFreeSpace(DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onGetDiskFreeSpace (fileInfo: "
+ fileInfo + ")");
return panboxFS.getDiskFreeSpace(fileInfo);
}
@Override
public ByHandleFileInformation onGetFileInformation(String fileName,
DokanFileInfo fileInfo) throws DokanOperationException {
logger.debug(getClass().getName()
+ " : onGetFileInformation (fileName: " + fileName
+ ", fileInfo: " + fileInfo + ")");
try {
return panboxFS
.getFileInformation(fileName, false, false, fileInfo)
.toByhandleFileInformation(0);
} catch (IOException e) {
logger.error(getClass().getName()
+ " : onGetFileInformation : Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onGetFileInformation : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public DokanVolumeInformation onGetVolumeInformation(String volumeName,
DokanFileInfo fileInfo) throws DokanOperationException {
logger.debug(getClass().getName()
+ " : onGetVolumeInformation (volumeName: " + volumeName
+ ", fileInfo: " + fileInfo + ")");
DokanVolumeInformation volInfo = panboxFS.getVolumeInformation(
volumeName, fileInfo);
logger.debug(getClass().getName()
+ " : onGetVolumeInformation returned: " + volInfo);
return volInfo;
}
@Override
public void onLockFile(String fileName, long byteOffset, long length,
DokanFileInfo fileInfo) throws DokanOperationException {
logger.debug(getClass().getName() + " : onLockFile (fileName: "
+ fileName + ", byteOffset: " + byteOffset + ", length: "
+ length + ", fileInfo: " + fileInfo + ")");
panboxFS.lockFile(fileName, byteOffset, length, fileInfo);
}
@Override
public void onMoveFile(String existingFileName, String newFileName,
boolean replaceExisiting, DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onMoveFile (existingFileName: "
+ existingFileName + ", newFileName: " + newFileName
+ ", replaceExisiting: " + replaceExisiting + ", fileInfo: "
+ fileInfo + ")");
try {
panboxFS.moveFile(existingFileName, newFileName, replaceExisiting,
fileInfo);
} catch (FileAlreadyExistsException | FileNotFoundException e) {
logger.error(getClass().getName() + " : onMoveFile : Exception: ",
e);
throw new DokanOperationException(ERROR_FILE_EXISTS);
} catch (IOException e) {
logger.error(getClass().getName() + " : onMoveFile : Exception: ",
e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onMoveFile : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public long onOpenDirectory(String fileName, DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onOpenDirectory (fileName: "
+ fileName + ", fileInfo: " + fileInfo + ")");
return panboxFS.openDirectory(fileName, fileInfo);
}
@Override
public int onReadFile(String fileName, ByteBuffer buffer, long offset,
DokanFileInfo fileInfo) throws DokanOperationException {
logger.debug(getClass().getName() + " : onReadFile (fileName: "
+ fileName + ", offset: " + offset + ", fileInfo: " + fileInfo
+ ")");
try {
return panboxFS.readFile(fileName, buffer, offset, fileInfo);
} catch (IOException | PanboxEncryptionException e) {
logger.error(getClass().getName() + " : onReadFile : Exception: ",
e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onReadFile : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public void onSetEndOfFile(String fileName, long length,
DokanFileInfo fileInfo) throws DokanOperationException {
logger.debug(getClass().getName() + " : onSetEndOfFile (fileName: "
+ fileName + ", length: " + length + ", fileInfo: " + fileInfo
+ ")");
try {
panboxFS.setEndOfFile(fileName, length, fileInfo);
} catch (IOException | PanboxEncryptionException e) {
logger.error(getClass().getName()
+ " : onSetEndOfFile : Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onSetEndOfFile : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public void onSetFileAttributes(String fileName, int fileAttributes,
DokanFileInfo fileInfo) throws DokanOperationException {
logger.debug(getClass().getName()
+ " : onSetFileAttributes (fileName: " + fileName
+ ", fileAttributes: " + fileAttributes + ", fileInfo: "
+ fileInfo + ")");
panboxFS.setFileAttributes(fileName, fileAttributes, fileInfo);
}
@Override
public void onSetFileTime(String fileName, long creationTime,
long lastAccessTime, long lastWriteTime, DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onSetFileTime (fileName: "
+ fileName + ", creationTime: " + creationTime
+ ", lastAccessTime: " + lastAccessTime + ", lastWriteTime: "
+ lastWriteTime + ", fileInfo: " + fileInfo + ")");
try {
panboxFS.setFileTime(fileName, creationTime, lastAccessTime,
lastWriteTime, fileInfo);
} catch (FileNotFoundException e) {
logger.error(getClass().getName()
+ " : onSetFileTime : Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public void onUnlockFile(String fileName, long byteOffset, long length,
DokanFileInfo fileInfo) throws DokanOperationException {
logger.debug(getClass().getName() + " : onUnlockFile (fileName: "
+ fileName + ", byteOffset: " + byteOffset + ", length: "
+ length + ", fileInfo: " + fileInfo + ")");
panboxFS.unlockFile(fileName, byteOffset, length, fileInfo);
}
@Override
public void onUnmount(DokanFileInfo fileInfo)
throws DokanOperationException {
logger.debug(getClass().getName() + " : onUnmount (fileInfo: "
+ fileInfo + ")");
panboxFS.unmount(fileInfo);
}
@Override
public int onWriteFile(String fileName, ByteBuffer buffer, long offset,
DokanFileInfo fileInfo) throws DokanOperationException {
logger.debug(getClass().getName() + " : onWriteFile (fileName: "
+ fileName + ", offset: " + offset + ", fileInfo: " + fileInfo
+ ")");
try {
return panboxFS.writeFile(fileName, buffer, offset, fileInfo);
} catch (IOException | PanboxEncryptionException e) {
logger.error(getClass().getName() + " : onWriteFile : Exception: ",
e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
} catch (Exception e) {
logger.error(getClass().getName()
+ " : onWriteFile : Unknown Exception: ", e);
throw new DokanOperationException(ERROR_FILE_NOT_FOUND);
}
}
@Override
public String getPathToBackendFile(String fileName)
throws DokanOperationException {
return null;
}
}