/*
* 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.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.Mac;
/**
* Class representing an EncFS volume.
* <p/>
* The volume is defined by a root folder, which contains an EncFS configuration
* file and a hierarchy of encrypted files and subdirectories created by a
* compliant EncFS implementation.
*/
public class EncFSVolume {
/**
* Standard name of the EncFS volume configuration file
*/
public final static String CONFIG_FILE_NAME = ".encfs6.xml";
/**
* Old EncFS config file names
*/
public final static String[] OLD_CONFIG_FILE_NAMES = {".encfs5",
".encfs4", ".encfs3", ".encfs2", ".encfs"};
/**
* String denoting the root path of an EncFS volume
*/
public final static String ROOT_PATH = "/";
/**
* String denoting the path separator for EncFS volumes
*/
public final static String PATH_SEPARATOR = "/";
/**
* Length in bytes of the volume initialization vector (IV)
*/
public final static int IV_LENGTH_IN_BYTES = 16;
private static enum PathOperation {
MOVE, COPY
}
// Volume fields
private EncFSConfig volumeConfig;
private Key volumeKey;
private byte[] volumeIV;
private byte[] derivedKeyData;
private Mac volumeMAC;
private Cipher streamCipher;
private Cipher blockCipher;
private EncFSFile rootDir;
private EncFSFileProvider fileProvider;
/**
* Decrypt volume key and initialize ciphers and other volume fields.
*/
void readConfigAndInitVolume() throws EncFSUnsupportedException, EncFSInvalidConfigException, EncFSCorruptDataException, EncFSInvalidPasswordException, IOException {
byte[] keyData;
try {
keyData = VolumeKey.decryptVolumeKey(volumeConfig, derivedKeyData);
} catch (EncFSChecksumException e) {
throw new EncFSInvalidPasswordException(e);
}
int keyLength = volumeConfig.getVolumeKeySizeInBits()/8;
if (keyData.length<keyLength) {
throw new EncFSInvalidConfigException("Key size too large");
}
volumeKey = EncFSCrypto.newKey(Arrays.copyOfRange(keyData, 0, keyLength));
volumeIV = copyIVdata(keyData, keyLength);
volumeMAC = createVolumeMAC();
streamCipher = StreamCrypto.newStreamCipher();
blockCipher = BlockCrypto.newBlockCipher();
rootDir = getFile(ROOT_PATH);
}
/**
* Copy IV data from the given key data.
*/
private byte[] copyIVdata(byte[] keyData, int keyLength) throws EncFSInvalidConfigException {
int ivLength = keyData.length-keyLength;
if (ivLength!=IV_LENGTH_IN_BYTES) {
throw new EncFSInvalidConfigException("Non-standard IV length");
}
return Arrays.copyOfRange(keyData, keyLength, keyLength+ivLength);
}
// Create the volume MAC
private Mac createVolumeMAC() throws EncFSUnsupportedException,
EncFSInvalidConfigException {
try {
return EncFSCrypto.newMac(volumeKey);
} catch (InvalidKeyException e) {
throw new EncFSInvalidConfigException(e);
}
}
/**
* Combine the given directory and file name into a path string
*
* @param dir
* Directory forming the first path component
* @param fileName
* Filename forming the second path component
*
* @return String representing the combined path
*/
public static String combinePath(EncFSFile dir, String fileName) {
EncFSVolume volume = dir.getVolume();
String result;
if (dir==volume.getRootDir()) {
result = ROOT_PATH+fileName;
} else {
result = dir.getPath()+PATH_SEPARATOR+fileName;
}
return result;
}
/**
* Combine the given directory and file name into a path string
*
* @param dirPath Directory forming the first path component
* @param fileName File forming the second path component
*
* @return String representing the combined path
*/
public static String combinePath(String dirPath, String fileName) {
if (dirPath.equals(ROOT_PATH)) {
return ROOT_PATH+fileName;
} else {
return dirPath+PATH_SEPARATOR+fileName;
}
}
/**
* Combine the given directory and file name into a path string
*
* @param dir Directory path forming the first path component
* @param file File name forming the second path component
*
* @return String representing the combined path
*/
public static String combinePath(EncFSFile dir, EncFSFile file) {
return combinePath(dir, file.getName());
}
/**
* Combine the given directory and file name into a path string
*
* @param dirPath Directory path forming the first path component
* @param file File forming the second path component
*
* @return String representing the combined path
*/
private static String combinePath(String dirPath, EncFSFile file) {
return combinePath(dirPath, file.getName());
}
/**
* Count files and directories under the given file
*
* @param file File to count under
* @return Number of files/directories under the file
*/
public static int countFiles(EncFSFile file) {
if (file.isDirectory()) {
int dirCount = 1;
try {
for (EncFSFile subFile : file.listFiles()) {
dirCount += countFiles(subFile);
}
} catch (Exception e) {
}
return dirCount;
} else {
return 1;
}
}
/**
* Returns the configuration object for this volume
*
* @return Configuration for this EncFS volume
*/
public EncFSConfig getConfig() {
return volumeConfig;
}
/**
* Returns the volume key used for encryption/decryption
*
* @return Volume key for encryption/decryption
*/
public Key getKey() {
return volumeKey;
}
/**
* Returns the volume IV used for encryption/decryption
*
* @return Volume initialization vector (IV) for encryption/decryption
*/
public byte[] getIV() {
return volumeIV;
}
/**
* Returns the password based VolumeCryptKey/IV data for this volume
*
* @return Password-based VolumeCryptKey/IV data for this volume
*/
public byte[] getDerivedKeyData() {
return derivedKeyData;
}
/**
* Returns the MAC object used for checksum verification
*
* @return Volume MAC for checksum verification
*/
public Mac getMAC() {
return volumeMAC;
}
/**
* Returns the stream cipher instance for stream encryption/decryption
*
* @return Stream cipher instance for stream encryption/decryption
*/
public Cipher getStreamCipher() {
return streamCipher;
}
/**
* Returns the block cipher instance for block encryption/decryption
*
* @return Block cipher instance for block encryption/decryption
*/
public Cipher getBlockCipher() {
return blockCipher;
}
/**
* Returns a file object representing the root directory of the volume
*
* @return EncFSFile representing the root directory of this volume
*/
public EncFSFile getRootDir() {
return rootDir;
}
/**
* Returns the file provider used for this volume
*
* @return EncFSFileProvider for this volume
*/
public EncFSFileProvider getFileProvider() {
return fileProvider;
}
/**
* Get an EncFSFile object representing the provided absolute path in the
* volume.
*
* @param filePath Absolute volume path of the file
*
* @return EncFSFile representing the requested file
*
* @throws EncFSCorruptDataException Corrupt data detected (checksum error)
* @throws IOException File provider returned I/O error
*/
public EncFSFile getFile(String filePath) throws EncFSCorruptDataException,
IOException {
validateAbsoluteFileName(filePath, "filePath");
String encryptedPath = EncFSCrypto
.encodePath(this, filePath, ROOT_PATH);
if (!fileProvider.exists(encryptedPath)) {
throw new FileNotFoundException();
}
EncFSFileInfo fileInfo = fileProvider.getFileInfo(encryptedPath);
EncFSFileInfo decodedFileInfo = getDecodedFileInfo(filePath, fileInfo);
return new EncFSFile(this, decodedFileInfo, fileInfo);
}
/**
* Returns the decrypted length a file would have in this volume given its
* encrypted length.
*
* @param encryptedFileLength Length of the encrypted file
* @return Length of the file after decryption
*/
public long getDecryptedFileLength(long encryptedFileLength) {
long size = encryptedFileLength;
if (size==0) {
return 0;
}
// Account for file header
if (volumeConfig.isUseUniqueIV()) {
size -= EncFSFile.HEADER_SIZE;
}
// Account for block headers
long headerLength = volumeConfig.getNumberOfMACBytesForEachFileBlock()
+volumeConfig.getNumberOfRandomBytesInEachMACHeader();
if (headerLength>0) {
long blockLength = volumeConfig.getEncryptedFileBlockSizeInBytes()
+headerLength;
long numBlocks = ((size-1)/blockLength)+1;
size -= numBlocks*headerLength;
}
return size;
}
/**
* Returns the encrypted length a file would have in this volume given its
* decrypted length.
*
* @param decryptedFileLength Length of the decrypted file
* @return Length of the file after encryption
*/
public long getEncryptedFileLength(long decryptedFileLength) {
long size = decryptedFileLength;
if (size==0) {
return 0;
}
// Account for block headers
long headerLength = volumeConfig.getNumberOfMACBytesForEachFileBlock()
+volumeConfig.getNumberOfRandomBytesInEachMACHeader();
if (headerLength>0) {
long blockLength = volumeConfig.getEncryptedFileBlockSizeInBytes()
+headerLength;
long numBlocks = ((size-1)/blockLength)+1;
size += numBlocks*headerLength;
}
// Account for file header
if (volumeConfig.isUseUniqueIV()) {
size += EncFSFile.HEADER_SIZE;
}
return size;
}
/**
* Checks whether the file or directory with the given path exists in the
* volume.
*
* @param path Absolute volume path of the file or directory
*
* @return true if path exists in the volume, false otherwise
*
* @throws EncFSCorruptDataException Filename encoding failed
* @throws IOException File provider returned I/O error
*/
public boolean pathExists(String path) throws EncFSCorruptDataException,
IOException {
validateAbsoluteFileName(path, "fileName");
String encryptedPath = EncFSCrypto.encodePath(this, path, ROOT_PATH);
return fileProvider.exists(encryptedPath);
}
/**
* Tests if the provided path contains EncFS volume.
*
* @param path Path to the presumed EncFS volume
* @return true if the volume is EncFS, false otherwise
* @throws IOException File provider returned I/O error
*/
public static boolean isEncFSVolume(String path) throws IOException {
return isEncFSVolume(new File(path));
}
/**
* Tests if the provided path contains EncFS volume.
*
* @param file File for the presumed EncFS volume
* @return true if the volume is EncFS, false otherwise
* @throws IOException File provider returned I/O error
*/
public static boolean isEncFSVolume(File file) throws IOException {
return isEncFSVolume(new EncFSLocalFileProvider(file));
}
/**
* Tests if the provided path contains EncFS volume.
*
* @param fileProvider File provider for the presumed EncFS volume
* @return true if the volume is EncFS, false otherwise
*/
public static boolean isEncFSVolume(EncFSFileProvider fileProvider)
throws IOException {
return (fileProvider.exists(fileProvider.getFilesystemRootPath()
+EncFSVolume.CONFIG_FILE_NAME));
}
/**
* Creates a new file under the EncFS volume.
*
* @param parentPath Absolute volume path of the parent directory
* @param fileName Name of the file to create
*
* @return EncFSFile handle for the newly created file
*
* @throws EncFSCorruptDataException Filename encoding failed
* @throws IOException File provider returned I/O error
*/
public EncFSFile createFile(String parentPath, String fileName) throws EncFSCorruptDataException, IOException {
validateAbsoluteFileName(parentPath, "volumePath");
return createFile(combinePath(parentPath, fileName));
}
/**
* Creates a new file under the EncFS volume
*
* @param filePath Absolute volume path of the file to create
*
* @return EncFSFile handle for the newly created file
*
* @throws EncFSCorruptDataException Filename encoding failed
* @throws IOException File provider returned I/O error
*/
public EncFSFile createFile(String filePath)
throws EncFSCorruptDataException, IOException {
validateAbsoluteFileName(filePath, "fileName");
String encryptedPath = EncFSCrypto.encodePath(this, filePath, ROOT_PATH);
EncFSFileInfo fileInfo = fileProvider.createFile(encryptedPath);
EncFSFileInfo decodedFileInfo = getDecodedFileInfo(filePath, fileInfo);
return new EncFSFile(this, decodedFileInfo, fileInfo);
}
/**
* Returns the decoded file information for the given file path.
*/
private EncFSFileInfo getDecodedFileInfo(String filePath, EncFSFileInfo fileInfo) {
EncFSFileInfo decodedFileInfo;
if (filePath.equals(ROOT_PATH)) {
decodedFileInfo = EncFSFileInfo.getDecodedFileInfo(this, "", ROOT_PATH, fileInfo);
} else {
int lastIndexOfSeparator = filePath.lastIndexOf(PATH_SEPARATOR);
String decDirName;
String decFilename;
if (filePath.lastIndexOf(PATH_SEPARATOR)==0) {
decDirName = PATH_SEPARATOR;
decFilename = filePath.substring(1);
} else {
decDirName = filePath.substring(0, lastIndexOfSeparator);
decFilename = filePath.substring(lastIndexOfSeparator+1);
}
decodedFileInfo = EncFSFileInfo.getDecodedFileInfo(this, decDirName, decFilename, fileInfo);
}
return decodedFileInfo;
}
/**
* Create a new directory under the EncFS volume.
*
* @param dirPath Absolute volume path of the directory to create
*
* @return true if creation succeeds, false otherwise
*
* @throws EncFSCorruptDataException Filename encoding failed
* @throws IOException File provider returned I/O error
*/
public boolean makeDir(String dirPath) throws EncFSCorruptDataException, IOException {
validateAbsoluteFileName(dirPath, "dirPath");
String encryptedPath = EncFSCrypto.encodePath(this, dirPath, ROOT_PATH);
try {
return fileProvider.mkdir(encryptedPath);
} catch (FileNotFoundException e) {
throw new FileNotFoundException("One or more path element in '"+dirPath+"' doesn't exist!");
}
}
/**
* Create a new directory under the EncFS volume, creating any missing
* directories in the path as well.
*
* @param dirPath Absolute volume path of the directory to create
*/
public boolean makeDirs(String dirPath) throws EncFSCorruptDataException, IOException {
validateAbsoluteFileName(dirPath, "dirPath");
String encryptedPath = EncFSCrypto.encodePath(this, dirPath, ROOT_PATH);
return fileProvider.mkdirs(encryptedPath);
}
/**
* Recursive method to delete a directory tree.
*/
private boolean recursiveDelete(EncFSFile file, EncFSProgressListener progressListener) throws IOException {
boolean result = true;
if (file.isDirectory()) {
for (EncFSFile subFile : file.listFiles()) {
boolean subResult = recursiveDelete(subFile, progressListener);
if (!subResult) {
result = false;
break;
}
}
if (result) {
if (progressListener!=null) {
progressListener.setCurrentFile(file.getPath());
}
result = file.delete();
if (progressListener!=null) {
progressListener.postEvent(EncFSProgressListener.FILE_PROCESS_EVENT);
}
}
} else {
if (progressListener!=null) {
progressListener.setCurrentFile(file.getPath());
}
result = file.delete();
if (progressListener!=null) {
progressListener.postEvent(EncFSProgressListener.FILE_PROCESS_EVENT);
}
}
return result;
}
/**
* Deletes the given file or directory in the EncFS volume.
*
* @param filePath Absolute volume path of the file/directory to delete
* @param recursive Whether to recursively delete directories. Without this option
* deletePath will fail to delete non-empty directories
* @param progressListener Progress listener for getting individual file updates
*/
public boolean deletePath(String filePath, boolean recursive, EncFSProgressListener progressListener)
throws EncFSCorruptDataException, IOException {
EncFSFile file = getFile(filePath);
boolean result;
if (recursive) {
if (progressListener!=null) {
progressListener.setNumFiles(countFiles(file));
}
result = recursiveDelete(file, progressListener);
if (progressListener!=null) {
progressListener.postEvent(EncFSProgressListener.OP_COMPLETE_EVENT);
}
return result;
} else {
if (progressListener!=null) {
progressListener.setNumFiles(1);
progressListener.setCurrentFile(file.getPath());
}
result = file.delete();
if (progressListener!=null) {
progressListener.postEvent(EncFSProgressListener.FILE_PROCESS_EVENT);
progressListener.postEvent(EncFSProgressListener.OP_COMPLETE_EVENT);
}
return result;
}
}
/**
* Deletes the given file or directory in the EncFS volume
*
* @param filePath Absolute volume path of the file/directory to delete
* @param recursive Whether to recursively delete directories. Without this option
* deletePath will fail to delete non-empty directories
*/
public boolean deletePath(String filePath, boolean recursive) throws EncFSCorruptDataException, IOException {
return deletePath(filePath, recursive, null);
}
// Helper function to perform copy/move path operations
private boolean copyOrMovePath(String srcPath, String dstPath, PathOperation op, EncFSProgressListener progressListener)
throws EncFSCorruptDataException, IOException {
validateAbsoluteFileName(srcPath, "srcPath");
validateAbsoluteFileName(dstPath, "dstPath");
if (!pathExists(srcPath)) {
throw new FileNotFoundException("Source path '"+srcPath+"' doesn't exist!");
}
if (srcPath.equals(dstPath)) {
throw new IOException("Can't copy/move onto the same path!");
}
String encSrcPath = EncFSCrypto.encodePath(this, srcPath, ROOT_PATH);
String encDstPath = EncFSCrypto.encodePath(this, dstPath, ROOT_PATH);
if (fileProvider.isDirectory(encSrcPath)&&(getConfig().isChainedNameIV()||op==PathOperation.COPY)) {
/*
* To make this safe (for if we fail halfway through) we need to:
*
* 1) create the new directory 2) Recursively move the sub
* directories / folders 3) Delete the original directory
*
* We can do it as a rename of the parent / original folder or we
* could be left with files we can't read
*/
// Need to copy/move the source dir to the destination
EncFSFile thisDir = getFile(srcPath);
// Update dstPath to point into the new target directory
if (pathExists(dstPath)) {
if (!fileProvider.isDirectory(encDstPath)) {
throw new IOException("Can't copy/move a directory onto a file!");
}
// dstPath is an existing dir, this is a copy/move into it
dstPath = combinePath(dstPath, thisDir);
}
// If dstPath doesn't exist this is a rename, keep dstPath as-is
if (progressListener!=null) {
progressListener.setCurrentFile(dstPath);
}
boolean result = makeDir(dstPath);
if (progressListener!=null) {
progressListener.postEvent(EncFSProgressListener.FILE_PROCESS_EVENT);
}
if (result) {
for (EncFSFile subFile : listFilesForPath(srcPath)) {
boolean subResult = copyOrMovePath(subFile.getPath(), combinePath(dstPath, subFile), op, progressListener);
if (!subResult) {
result = false;
break;
}
}
}
if (result) {
// We only delete source directories for move, not copy
if (op==PathOperation.MOVE) {
result = fileProvider.delete(encSrcPath);
}
} else {
// Attempt failure rollback
fileProvider.delete(encDstPath);
}
return result;
} else { // Simple file operation
EncFSFile srcFile = getFile(srcPath);
/*
* If dstPath is an existing directory we need to copy/move srcPath
* under it
*/
if (pathExists(dstPath)) {
EncFSFile dstFile = getFile(dstPath);
if (dstFile.isDirectory()) {
return copyOrMovePath(srcPath, combinePath(dstPath, srcFile), op, progressListener);
} else {
throw new IOException("Destination file "+dstPath+" exists, can't overwrite!");
}
} else {
// dstPath doesn't exist, perform normal copy/move
boolean result;
if (progressListener!=null) {
progressListener.setCurrentFile(dstPath);
}
if (op==PathOperation.MOVE) {
if (getConfig().isSupportedExternalIVChaining()) {
/*
* Need to re-encrypt the file contents while moving
* since external IV chaining is being used. We'll just
* copy the file over to the destination path and delete
* the original file afterwards.
*/
result = srcFile.copy(createFile(dstPath));
if (result) {
result = srcFile.delete();
}
} else {
// Simply move the file
result = fileProvider.move(encSrcPath, encDstPath);
}
} else {
result = srcFile.copy(createFile(dstPath));
}
if (progressListener!=null) {
progressListener.postEvent(EncFSProgressListener.FILE_PROCESS_EVENT);
}
return result;
}
}
}
// Helper function to post completion event around copyOrMovePath
private boolean copyOrMove(String srcPath, String dstPath, EncFSProgressListener progressListener, PathOperation operation)
throws EncFSCorruptDataException, IOException {
if (progressListener!=null) {
progressListener.setNumFiles(countFiles(getFile(srcPath))+1);
}
boolean result = copyOrMovePath(srcPath, dstPath, operation, progressListener);
if (progressListener!=null) {
progressListener.postEvent(EncFSProgressListener.OP_COMPLETE_EVENT);
}
return result;
}
/**
* Copies the source file or directory to the target file or directory
*
* @param srcPath Absolute volume path of the source file or directory
* @param dstPath Absolute volume path of the target file or directory
* @param progressListener Progress listener for getting individual file updates
*
* @return true if copy succeeds, false otherwise
*
* @throws EncFSCorruptDataException Filename encoding failed
* @throws IOException File provider returned I/O error
*/
public boolean copyPath(String srcPath, String dstPath, EncFSProgressListener progressListener)
throws EncFSCorruptDataException, IOException {
return copyOrMove(srcPath, dstPath, progressListener, PathOperation.COPY);
}
/**
* Copies the source file or directory to the target file or directory.
*
* @param srcPath Absolute volume path of the source file or directory
* @param dstPath Absolute volume path of the target file or directory
*
* @return true if copy succeeds, false otherwise
*
* @throws EncFSCorruptDataException Filename encoding failed
* @throws IOException File provider returned I/O error
*/
public boolean copyPath(String srcPath, String dstPath) throws EncFSCorruptDataException, IOException {
return copyPath(srcPath, dstPath, null);
}
/**
* Moves a file / directory.
*
* @param srcPath Absolute volume path of the file or directory to move
* @param dstPath Absolute volume path of the destination file or directory
* @param progressListener Progress listener for getting individual file updates
*
* @return true if the move succeeds, false otherwise
*
* @throws EncFSCorruptDataException Filename encoding failed
* @throws IOException File provider returned I/O error
*/
public boolean movePath(String srcPath, String dstPath, EncFSProgressListener progressListener)
throws EncFSCorruptDataException, IOException {
return copyOrMove(srcPath, dstPath, progressListener,
PathOperation.MOVE);
}
/**
* Moves a file / directory.
*
* @param srcPath Absolute volume path of the file or directory to move
* @param dstPath Absolute volume path of the destination file or directory
*
* @return true if the move succeeds, false otherwise
*
* @throws EncFSCorruptDataException Filename encoding failed
* @throws IOException File provider returned I/O error
*/
public boolean movePath(String srcPath, String dstPath) throws EncFSCorruptDataException, IOException {
return movePath(srcPath, dstPath, null);
}
/**
* Get list of EncFSFile's under the given directory
*
* @param dirPath Absolute volume path of the directory to list
*
* @return list of EncFSFile under the given directory
*
* @throws EncFSCorruptDataException Filename encoding failed
* @throws IOException File provider returned I/O error
*/
public EncFSFile[] listFilesForPath(String dirPath) throws EncFSCorruptDataException, IOException {
return getFile(dirPath).listFiles();
}
/**
* Opens the specified file as an EncFSInputStream that decrypts the file
* contents automatically.
*/
public EncFSInputStream openInputStreamForPath(String filePath)
throws EncFSCorruptDataException, EncFSUnsupportedException, IOException {
return getFile(filePath).openInputStream();
}
/**
* Opens the specified file as an EncFSOutputStream that encrypts the file
* contents automatically.
*
* @param filePath Absolute volume path of the file
* @param outputLength Length of the output data that will be written to the returned
* 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.
*/
public EncFSOutputStream openOutputStreamForPath(String filePath, long outputLength)
throws EncFSCorruptDataException, EncFSUnsupportedException, IOException {
return getFile(filePath).openOutputStream(outputLength);
}
/**
* Validate the given absolute file name format
*/
private void validateAbsoluteFileName(String fileName, String name) {
if (name==null||name.length()==0) {
throw new IllegalStateException("name should not be blank");
}
if (fileName==null) {
throw new IllegalArgumentException(name+" must not be null");
}
if (fileName.length()==0) {
throw new IllegalArgumentException(name+" must not be blank");
}
if (!fileName.startsWith(PATH_SEPARATOR)) {
throw new IllegalArgumentException(name+" must absolute");
}
}
/**
* Used by EncFSVolumeBuilder to set password-derived key data.
*/
protected void setDerivedKeyData(byte[] passwordDerivedKeyData) {
this.derivedKeyData = passwordDerivedKeyData;
}
/**
* Used by EncFSVolumeBuilder to set file provider.
*/
protected void setFileProvider(EncFSFileProvider fileProvider) {
this.fileProvider = fileProvider;
}
/**
* Used by EncFSVolumeBuilder to set volume configuration.
*/
protected void setVolumeConfig(EncFSConfig volumeConfig) {
this.volumeConfig = volumeConfig;
}
}