add zero-block pass-through option, enabling allow-holes code

git-svn-id: http://encfs.googlecode.com/svn/trunk@14 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
Valient Gough 2008-04-11 08:37:19 +00:00
parent 9ce4a03887
commit 30ed7062d3
11 changed files with 184 additions and 111 deletions

View File

@ -189,7 +189,7 @@ bool BlockFileIO::write( const IORequest &req )
if(lastBlockSize == 0)
--lastNonEmptyBlock;
if( (req.offset > fileSize) && !_allowHoles )
if( req.offset > fileSize )
{
// extend file first to fill hole with 0's..
const bool forceWrite = false;
@ -340,15 +340,18 @@ void BlockFileIO::padFile( off_t oldSize, off_t newSize, bool forceWrite )
++oldLastBlock;
}
// 2
for(; oldLastBlock != newLastBlock; ++oldLastBlock)
{
rDebug("padding block %" PRIi64, oldLastBlock);
req.offset = oldLastBlock * _blockSize;
req.dataLen = _blockSize;
memset( mb.data, 0, req.dataLen );
cacheWriteOneBlock( req );
}
// 2, pad zero blocks unless holes are allowed
if(!_allowHoles)
{
for(; oldLastBlock != newLastBlock; ++oldLastBlock)
{
rDebug("padding block %" PRIi64, oldLastBlock);
req.offset = oldLastBlock * _blockSize;
req.dataLen = _blockSize;
memset( mb.data, 0, req.dataLen );
cacheWriteOneBlock( req );
}
}
// 3. only necessary if write is forced and block is non 0 length
if(forceWrite && newBlockSize)

View File

@ -40,12 +40,12 @@ public:
virtual int blockSize() const;
protected:
// default is false, but setting this to true will allow holes to be stored
// in the file. Only works if supported by the underlying FileIO
// implementation..
void allowHoles( bool allow );
virtual void allowHoles( bool allow );
protected:
int truncate( off_t size, FileIO *base );
void padFile( off_t oldSize, off_t newSize, bool forceWrite );

View File

@ -372,7 +372,18 @@ bool CipherFileIO::blockRead( unsigned char *buf, int size,
if (reverseEncryption)
return cipher->blockEncode( buf, size, _iv64, key );
else
return cipher->blockDecode( buf, size, _iv64, key );
{
if(_allowHoles)
{
// special case - leave all 0's alone
for(int i=0; i<size; ++i)
if(buf[i] != 0)
return cipher->blockDecode( buf, size, _iv64, key );
return true;
} else
return cipher->blockDecode( buf, size, _iv64, key );
}
}
bool CipherFileIO::streamRead( unsigned char *buf, int size,

View File

@ -726,7 +726,8 @@ shared_ptr<FileNode> DirNode::findOrCreate( const char *plainName)
config->uniqueIV,
config->externalIVChaining,
config->forceDecode,
config->reverseEncryption) );
config->reverseEncryption,
config->allowHoles) );
if(config->externalIVChaining)
node->setName(0, 0, iv);

View File

@ -101,6 +101,7 @@ public:
bool externalIVChaining;
bool forceDecode; // force decoding, even if errors are detected
bool reverseEncryption;
bool allowHoles; // allow holes in files
Config()
: fsSubVersion(0)
, blockSize(1)
@ -111,6 +112,7 @@ public:
, externalIVChaining( false )
, forceDecode( false )
, reverseEncryption ( false )
, allowHoles( false )
{ }
};

View File

@ -48,6 +48,7 @@
using namespace std;
using namespace rel;
using namespace rlog;
using boost::dynamic_pointer_cast;
/*
TODO: locking at the FileNode level is inefficient, since this precludes
@ -65,7 +66,8 @@ FileNode::FileNode(DirNode *parent_,
const char *plaintextName_, const char *cipherName_,
const shared_ptr<Cipher> &dataCipher, const CipherKey &key,
int blockSize, int blockMACBytes, int blockMACRandBytes, bool uniqueIV,
bool externalIVChaining_, bool forceDecode, bool reverseEncryption_ )
bool externalIVChaining_, bool forceDecode, bool reverseEncryption_,
bool allowHoles )
{
pthread_mutex_init( &mutex, 0 );
@ -88,6 +90,9 @@ FileNode::FileNode(DirNode *parent_,
io = shared_ptr<FileIO>(new MACFileIO(io, dataCipher, key,
blockSize,blockMACBytes,blockMACRandBytes,forceDecode));
}
if(allowHoles)
dynamic_pointer_cast<BlockFileIO>(io)->allowHoles( allowHoles );
}
FileNode::~FileNode()

View File

@ -43,7 +43,8 @@ public:
bool uniqueIV, // enable per-file initialization vectors
bool externalIVChaining,
bool forceDecode, // decode, even if decoding errors are detected
bool reverseEncryption );
bool reverseEncryption,
bool allowHoles );
~FileNode();
const char *plaintextName() const;

View File

@ -272,6 +272,8 @@ bool readV5Config( const char *configFile, EncFSConfig *config,
config->blockMACRandBytes =
cfgRdr["blockMACRandBytes"].readInt(0);
config->allowHoles = cfgRdr["allowHoles"].readBool( false );
ok = true;
} catch( rlog::Error &err)
{
@ -411,6 +413,7 @@ bool writeV5Config( const char *configFile, EncFSConfig *config )
cfg["uniqueIV"] << config->uniqueIV;
cfg["chainedIV"] << config->chainedNameIV;
cfg["externalIV"] << config->externalIVChaining;
cfg["allowHoles"] << config->allowHoles;
return cfg.save( configFile );
}
@ -680,22 +683,34 @@ int selectBlockSize( const Cipher::CipherAlgorithm &alg )
}
static
void selectBlockMAC(int *macBytes, int *macRandBytes)
bool boolDefaultNo(const char *prompt)
{
// xgroup(setup)
cout << _("Enable block authentication code headers\n"
"on every block in a file? This adds about 12 bytes per block\n"
"to the storage requirements for a file, and significantly affects\n"
"performance but it also means [almost] any modifications or errors\n"
"within a block will be caught and will cause a read error.\n"
"The default here is No. \n"
"Any response that does not begin with 'y' will mean No: ");
cout << prompt << "\n";
cout << _("The default here is No.\n"
"Any response that does not begin with 'y' will mean No: ");
char answer[10];
fgets( answer, sizeof(answer), stdin );
cout << "\n";
if(tolower(answer[0]) == 'y')
if(tolower(answer[0]) == 'n')
return false;
else
return true;
}
static
void selectBlockMAC(int *macBytes, int *macRandBytes)
{
// xgroup(setup)
bool addMAC = boolDefaultNo(
_("Enable block authentication code headers\n"
"on every block in a file? This adds about 12 bytes per block\n"
"to the storage requirements for a file, and significantly affects\n"
"performance but it also means [almost] any modifications or errors\n"
"within a block will be caught and will cause a read error."));
if(addMAC)
{
*macBytes = 8;
@ -708,6 +723,7 @@ void selectBlockMAC(int *macBytes, int *macRandBytes)
"penalty. \n"
"Select a number of bytes, from 0 (no random bytes) to 8: ");
char answer[10];
int randSize = 0;
fgets( answer, sizeof(answer), stdin );
cout << "\n";
@ -727,58 +743,11 @@ void selectBlockMAC(int *macBytes, int *macRandBytes)
}
static
bool selectUniqueIV()
bool boolDefaultYes(const char *prompt)
{
// xgroup(setup)
cout << _("Enable per-file initialization vectors?\n"
"This adds about 8 bytes per file to the storage requirements.\n"
"It should not affect performance except possibly with applications\n"
"which rely on block-aligned file io for performance.\n"
"The default here is Yes. \n"
"Any response that does not begin with 'n' will mean Yes: ");
char answer[10];
fgets( answer, sizeof(answer), stdin );
cout << "\n";
if(tolower(answer[0]) == 'n')
return false;
else
return true;
}
static
bool selectChainedIV()
{
// xgroup(setup)
cout << _("Enable filename initialization vector chaining?\n"
"This makes filename encoding dependent on the complete path, \n"
"rather then encoding each path element individually. \n"
"This is normally desireable, therefor the default is Yes. \n"
"Any response that does not begin with 'n' will mean Yes: ");
char answer[10];
fgets( answer, sizeof(answer), stdin );
cout << "\n";
if(tolower(answer[0]) == 'n')
return false;
else
return true;
}
static
bool selectExternalChainedIV()
{
// xgroup(setup)
cout << _("Enable filename to IV header chaining?\n"
"This makes file data encoding dependent on the complete file path.\n"
"If a file is renamed, it will not decode sucessfully unless it\n"
"was renamed by encfs with the proper key.\n"
"If this option is enabled, then hard links will not be supported\n"
"in the filesystem.\n"
"The default is No. \n"
"Any response that does not begin with 'y' will mean No: ");
cout << prompt << "\n";
cout << _("The default here is Yes.\n"
"Any response that does not begin with 'n' will mean Yes: ");
char answer[10];
fgets( answer, sizeof(answer), stdin );
@ -790,6 +759,49 @@ bool selectExternalChainedIV()
return false;
}
static
bool selectUniqueIV()
{
// xgroup(setup)
return boolDefaultYes(
_("Enable per-file initialization vectors?\n"
"This adds about 8 bytes per file to the storage requirements.\n"
"It should not affect performance except possibly with applications\n"
"which rely on block-aligned file io for performance."));
}
static
bool selectChainedIV()
{
// xgroup(setup)
return boolDefaultYes(
_("Enable filename initialization vector chaining?\n"
"This makes filename encoding dependent on the complete path, \n"
"rather then encoding each path element individually."));
}
static
bool selectExternalChainedIV()
{
// xgroup(setup)
return boolDefaultNo(
_("Enable filename to IV header chaining?\n"
"This makes file data encoding dependent on the complete file path.\n"
"If a file is renamed, it will not decode sucessfully unless it\n"
"was renamed by encfs with the proper key.\n"
"If this option is enabled, then hard links will not be supported\n"
"in the filesystem."));
}
static
bool selectZeroBlockPassThrough()
{
// xgroup(setup)
return boolDefaultNo(
_("Enable file-hole pass-through?\n"
"This avoids writing encrypted blocks when file holes are created."));
}
RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
bool enableIdleTracking, bool forceDecode,
const std::string &passwordProgram,
@ -822,6 +834,7 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
bool uniqueIV = false;
bool chainedIV = false;
bool externalIV = false;
bool allowHoles = false;
if (reverseEncryption)
{
@ -861,13 +874,11 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
{
// xgroup(setup)
cout << _("Standard configuration selected.") << "\n";
// look for blowfish with 160 bit key..
// block name encoding is now standard as well..
// per-file initialization vectors are now standard, as they shouldn't
// have any significant performance penalty.
keySize = 160;
// AES w/ 192 bit key, block name encoding, per-file initialization
// vectors are all standard.
keySize = 192;
blockSize = DefaultBlockSize;
alg = findCipherAlgorithm("Blowfish", keySize);
alg = findCipherAlgorithm("AES", keySize);
blockMACBytes = 0;
externalIV = false;
nameIOIface = BlockNameIO::CurrentInterface();
@ -917,10 +928,11 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
// xgroup(setup)
cout << _("External chained IV disabled, as both 'IV chaining'\n"
"and 'unique IV' features are required for this option.")
<< endl;
<< "\n";
externalIV = false;
}
selectBlockMAC(&blockMACBytes, &blockMACRandBytes);
allowHoles = selectZeroBlockPassThrough();
}
}
@ -949,6 +961,7 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
config.uniqueIV = uniqueIV;
config.chainedNameIV = chainedIV;
config.externalIVChaining = externalIV;
config.allowHoles = allowHoles;
cout << "\n";
// xgroup(setup)
@ -1035,6 +1048,7 @@ RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
dirNodeConfig->externalIVChaining = config.externalIVChaining;
dirNodeConfig->forceDecode = forceDecode;
dirNodeConfig->reverseEncryption = reverseEncryption;
dirNodeConfig->allowHoles = config.allowHoles;
rootInfo = RootPtr( new EncFS_Root );
rootInfo->cipher = cipher;
@ -1145,6 +1159,11 @@ void showFSInfo( const EncFSConfig &config )
// xgroup(diag)
cout << _("File data IV is chained to filename IV.\n");
}
if(config.allowHoles)
{
// xgroup(diag)
cout << _("File holes passed through to ciphertext.\n");
}
cout << "\n";
}

View File

@ -58,6 +58,7 @@ struct EncFSConfig
bool externalIVChaining; // IV seeding by filename IV chaining
bool chainedNameIV; // filename IV chaining
bool allowHoles; // allow holes in files (implicit zero blocks)
};
enum ConfigType

View File

@ -30,6 +30,8 @@
using namespace rlog;
using namespace rel;
using namespace std;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
static RLogChannel *Info = DEF_CHANNEL("info/MACFileIO", Log_Info);
//
@ -157,6 +159,14 @@ off_t MACFileIO::getSize() const
return size;
}
void MACFileIO::allowHoles( bool allow )
{
BlockFileIO::allowHoles( allow );
shared_ptr<BlockFileIO> bf = dynamic_pointer_cast<BlockFileIO>( base );
if(bf)
bf->allowHoles( allow );
}
ssize_t MACFileIO::readOneBlock( const IORequest &req ) const
{
int headerSize = macBytes + randBytes;
@ -173,31 +183,49 @@ ssize_t MACFileIO::readOneBlock( const IORequest &req ) const
// get the data from the base FileIO layer
ssize_t readSize = base->read( tmp );
// don't store zeros if configured for zero-block pass-through
bool skipBlock;
if( _allowHoles )
{
skipBlock = true;
for(int i=0; i<readSize; ++i)
if(tmp.data[i] != 0)
{
skipBlock = false;
break;
}
} else
skipBlock = false;
if(readSize > headerSize)
{
// At this point the data has been decoded. So, compute the MAC of the
// block and check against the checksum stored in the header..
uint64_t mac = cipher->MAC_64( tmp.data + macBytes,
readSize - macBytes, key );
if(!skipBlock)
{
// At this point the data has been decoded. So, compute the MAC of
// the block and check against the checksum stored in the header..
uint64_t mac = cipher->MAC_64( tmp.data + macBytes,
readSize - macBytes, key );
for(int i=0; i<macBytes; ++i, mac >>= 8)
{
int test = mac & 0xff;
int stored = tmp.data[i];
if(test != stored)
{
// uh oh..
long blockNum = req.offset / bs;
rWarning(_("MAC comparison failure in block %li"),
blockNum);
if( !warnOnly )
{
MemoryPool::release( mb );
throw ERROR(_("MAC comparison failure, refusing to read"));
}
break;
}
}
for(int i=0; i<macBytes; ++i, mac >>= 8)
{
int test = mac & 0xff;
int stored = tmp.data[i];
if(test != stored)
{
// uh oh..
long blockNum = req.offset / bs;
rWarning(_("MAC comparison failure in block %li"),
blockNum);
if( !warnOnly )
{
MemoryPool::release( mb );
throw ERROR(
_("MAC comparison failure, refusing to read"));
}
break;
}
}
}
// now copy the data to the output buffer
readSize -= headerSize;

View File

@ -53,6 +53,8 @@ public:
virtual bool isWritable() const;
virtual void allowHoles( bool allow );
private:
virtual ssize_t readOneBlock( const IORequest &req ) const;
virtual bool writeOneBlock( const IORequest &req );