Make EncFSFileOutputStream take a length parameter
Unfortunately some network storage systems such as Dropbox require length of the file before opening an upload request so we need to plumb file length all throughout the OutputStream creation. It is optional for EncFSLocalFileProvider, but it is good practice to supply this parameter whenever possible.
This commit is contained in:
parent
412d62e2e9
commit
7a370eb704
9 changed files with 138 additions and 33 deletions
src
main/java/org/mrpdaemon/sec/encfs
EncFSConfigWriter.javaEncFSFile.javaEncFSFileInfo.javaEncFSFileOutputStream.javaEncFSFileProvider.javaEncFSLocalFileProvider.javaEncFSVolume.java
test/java/org/mrpdaemon/sec/encfs
|
@ -131,10 +131,11 @@ public class EncFSConfigWriter {
|
|||
throw new EncFSUnsupportedException("Config file already exists");
|
||||
}
|
||||
|
||||
OutputStream os = fileProvider.openOutputStream(configFileName);
|
||||
|
||||
String configFileContents = createConfigFileContents(config, password);
|
||||
|
||||
OutputStream os = fileProvider.openOutputStream(configFileName,
|
||||
configFileContents.length());
|
||||
|
||||
os.write(configFileContents.getBytes());
|
||||
os.close();
|
||||
}
|
||||
|
|
|
@ -296,8 +296,15 @@ public class EncFSFile {
|
|||
* Opens the file as an OutputStream 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 OutputStream that encrypts file contents
|
||||
*
|
||||
*
|
||||
* @throws EncFSCorruptDataException
|
||||
* Filename encoding failed
|
||||
* @throws EncFSUnsupportedException
|
||||
|
@ -305,10 +312,12 @@ public class EncFSFile {
|
|||
* @throws IOException
|
||||
* File provider returned I/O error
|
||||
*/
|
||||
public OutputStream openOutputStream() throws EncFSUnsupportedException,
|
||||
EncFSCorruptDataException, IOException {
|
||||
public OutputStream openOutputStream(long inputLength)
|
||||
throws EncFSUnsupportedException, EncFSCorruptDataException,
|
||||
IOException {
|
||||
return new EncFSOutputStream(volume, volume.getFileProvider()
|
||||
.openOutputStream(getEncryptedPath()));
|
||||
.openOutputStream(getEncryptedPath(),
|
||||
volume.getEncryptedFileLength(inputLength)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -354,7 +363,8 @@ public class EncFSFile {
|
|||
*/
|
||||
try {
|
||||
EncFSUtil.copyWholeStream(this.openInputStream(),
|
||||
dstPath.openOutputStream(), true, true);
|
||||
dstPath.openOutputStream(plainFileInfo.getSize()),
|
||||
true, true);
|
||||
} catch (EncFSCorruptDataException e) {
|
||||
throw new IOException(e);
|
||||
} catch (EncFSUnsupportedException e) {
|
||||
|
|
|
@ -188,26 +188,11 @@ public class EncFSFileInfo {
|
|||
public static EncFSFileInfo getDecodedFileInfo(EncFSVolume volume,
|
||||
String decodedParentPath, String decodedFileName,
|
||||
EncFSFileInfo fileInfo) {
|
||||
EncFSConfig config = volume.getConfig();
|
||||
long size;
|
||||
if (fileInfo.isDirectory()) {
|
||||
size = 0;
|
||||
} else {
|
||||
size = fileInfo.getSize();
|
||||
|
||||
// Account for file header
|
||||
if (config.isUniqueIV() && size > 0) {
|
||||
size -= EncFSFile.HEADER_SIZE;
|
||||
}
|
||||
|
||||
// Account for block headers
|
||||
if (config.getBlockMACBytes() > 0
|
||||
|| config.getBlockMACRandBytes() > 0) {
|
||||
long numBlocks = fileInfo.getSize() / config.getBlockSize();
|
||||
size -= numBlocks
|
||||
* (config.getBlockMACBytes() + config
|
||||
.getBlockMACRandBytes());
|
||||
}
|
||||
size = volume.getDecryptedFileLength(fileInfo.getSize());
|
||||
}
|
||||
|
||||
EncFSFileInfo decEncFileInfo = new EncFSFileInfo(decodedFileName,
|
||||
|
|
|
@ -32,6 +32,11 @@ public class EncFSFileOutputStream extends OutputStream {
|
|||
*
|
||||
* @param encfsFile
|
||||
* EncFSFile to open an output stream to
|
||||
* @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.
|
||||
*
|
||||
* @throws EncFSCorruptDataException
|
||||
* Filename encoding failed
|
||||
|
@ -40,10 +45,10 @@ public class EncFSFileOutputStream extends OutputStream {
|
|||
* @throws IOException
|
||||
* File provider returned I/O error
|
||||
*/
|
||||
public EncFSFileOutputStream(EncFSFile encfsFile) throws IOException,
|
||||
EncFSUnsupportedException, EncFSCorruptDataException,
|
||||
EncFSChecksumException {
|
||||
this.encfsOs = encfsFile.openOutputStream();
|
||||
public EncFSFileOutputStream(EncFSFile encfsFile, long inputLength)
|
||||
throws IOException, EncFSUnsupportedException,
|
||||
EncFSCorruptDataException, EncFSChecksumException {
|
||||
this.encfsOs = encfsFile.openOutputStream(inputLength);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -205,11 +205,16 @@ public interface EncFSFileProvider {
|
|||
*
|
||||
* @param dstFilePath
|
||||
* Path to the destination file
|
||||
* @param outputLength
|
||||
* Length in bytes of the stream that will be written to this
|
||||
* stream. It is up to the file provider to determine whether
|
||||
* this parameter is optional or required.
|
||||
*
|
||||
* @return OutputStream to write to the file
|
||||
*
|
||||
* @throws IOException
|
||||
* Misc. I/O error
|
||||
*/
|
||||
public OutputStream openOutputStream(String dstFilePath) throws IOException;
|
||||
public OutputStream openOutputStream(String dstFilePath, long outputLength)
|
||||
throws IOException;
|
||||
}
|
||||
|
|
|
@ -343,6 +343,25 @@ public class EncFSLocalFileProvider implements EncFSFileProvider {
|
|||
* Misc. I/O error
|
||||
*/
|
||||
public OutputStream openOutputStream(String dstFilePath) throws IOException {
|
||||
return openOutputStream(dstFilePath, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an OutputStream to the given file
|
||||
*
|
||||
* @param dstFilePath
|
||||
* Path to the destination file
|
||||
* @param outputLength
|
||||
* Length in bytes of the stream that will be written to this
|
||||
* stream. It is ignored by this class.
|
||||
*
|
||||
* @return OutputStream to write to the file
|
||||
*
|
||||
* @throws IOException
|
||||
* Misc. I/O error
|
||||
*/
|
||||
public OutputStream openOutputStream(String dstFilePath, long outputLength)
|
||||
throws IOException {
|
||||
File srcF = new File(rootPath.getAbsoluteFile(), dstFilePath);
|
||||
if (srcF.exists() == false) {
|
||||
try {
|
||||
|
|
|
@ -581,6 +581,80 @@ public class EncFSVolume {
|
|||
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 (config.isUniqueIV()) {
|
||||
size -= EncFSFile.HEADER_SIZE;
|
||||
}
|
||||
|
||||
// Account for block headers
|
||||
long headerLength = config.getBlockMACBytes()
|
||||
+ config.getBlockMACRandBytes();
|
||||
if (headerLength > 0) {
|
||||
long blockLength = config.getBlockSize() + headerLength;
|
||||
|
||||
// Calculate number of blocks
|
||||
long numBlocks = ((size - 1) / blockLength) + 1;
|
||||
|
||||
// Subtract headers
|
||||
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 = config.getBlockMACBytes()
|
||||
+ config.getBlockMACRandBytes();
|
||||
if (headerLength > 0) {
|
||||
long blockLength = config.getBlockSize() + headerLength;
|
||||
|
||||
// Calculate number of blocks
|
||||
long numBlocks = ((size - 1) / blockLength) + 1;
|
||||
|
||||
// Add headers
|
||||
size += numBlocks * headerLength;
|
||||
}
|
||||
|
||||
// Account for file header
|
||||
if (config.isUniqueIV()) {
|
||||
size += EncFSFile.HEADER_SIZE;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the file or directory with the given path exists in the
|
||||
* volume
|
||||
|
@ -1163,6 +1237,11 @@ public class EncFSVolume {
|
|||
*
|
||||
* @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.
|
||||
*
|
||||
* @return OutputStream that encrypts file contents
|
||||
*
|
||||
|
@ -1173,11 +1252,11 @@ public class EncFSVolume {
|
|||
* @throws IOException
|
||||
* File provider returned I/O error
|
||||
*/
|
||||
public OutputStream openOutputStreamForPath(String filePath)
|
||||
throws EncFSCorruptDataException, EncFSUnsupportedException,
|
||||
IOException, EncFSChecksumException {
|
||||
public OutputStream openOutputStreamForPath(String filePath,
|
||||
long outputLength) throws EncFSCorruptDataException,
|
||||
EncFSUnsupportedException, IOException, EncFSChecksumException {
|
||||
EncFSFile file = this.getFile(filePath);
|
||||
return file.openOutputStream();
|
||||
return file.openOutputStream(outputLength);
|
||||
}
|
||||
|
||||
// Validate the given absolute file name format
|
||||
|
|
|
@ -70,7 +70,7 @@ public class EncFSVolumeTestCommon {
|
|||
Assert.assertEquals(0,
|
||||
volume.listFilesForPath(EncFSVolume.ROOT_PATH).length);
|
||||
EncFSFile outFile = volume.createFile("/test.txt");
|
||||
OutputStream os = outFile.openOutputStream();
|
||||
OutputStream os = outFile.openOutputStream(11);
|
||||
try {
|
||||
os.write("hello\nworld".getBytes());
|
||||
} finally {
|
||||
|
|
|
@ -135,7 +135,8 @@ public class CommonsVFSFileProvider implements EncFSFileProvider {
|
|||
return srcFile.getContent().getInputStream();
|
||||
}
|
||||
|
||||
public OutputStream openOutputStream(String encSrcFile) throws IOException {
|
||||
public OutputStream openOutputStream(String encSrcFile, long outputLength)
|
||||
throws IOException {
|
||||
FileObject srcFile = resolveFile(encSrcFile);
|
||||
return srcFile.getContent().getOutputStream();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue