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:
Mark Pariente 2012-05-24 19:48:32 -07:00
parent 412d62e2e9
commit 7a370eb704
9 changed files with 138 additions and 33 deletions

View file

@ -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();
}

View file

@ -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) {

View file

@ -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,

View file

@ -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);
}
/*

View file

@ -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;
}

View file

@ -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 {

View file

@ -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

View file

@ -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 {

View file

@ -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();
}