mirror of
https://github.com/vgough/encfs.git
synced 2025-05-29 14:41:11 +02:00
commit
40531024c8
@ -275,25 +275,43 @@ string DirNode::rootDirectory() {
|
|||||||
return string(rootDir, 0, rootDir.length() - 1);
|
return string(rootDir, 0, rootDir.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt a plain-text file path to the ciphertext path with the
|
||||||
|
* ciphertext root directory name prefixed.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* $ encfs -f -v cipher plain
|
||||||
|
* $ cd plain
|
||||||
|
* $ touch foobar
|
||||||
|
* cipherPath: /foobar encoded to cipher/NKAKsn2APtmquuKPoF4QRPxS
|
||||||
|
*/
|
||||||
string DirNode::cipherPath(const char *plaintextPath) {
|
string DirNode::cipherPath(const char *plaintextPath) {
|
||||||
return rootDir + naming->encodePath(plaintextPath);
|
return rootDir + naming->encodePath(plaintextPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as cipherPath(), but does not prefix the ciphertext root directory
|
||||||
|
*/
|
||||||
string DirNode::cipherPathWithoutRoot(const char *plaintextPath) {
|
string DirNode::cipherPathWithoutRoot(const char *plaintextPath) {
|
||||||
return naming->encodePath(plaintextPath);
|
return naming->encodePath(plaintextPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the decrypted version of cipherPath
|
||||||
|
*
|
||||||
|
* In reverse mode, returns the encrypted version of cipherPath
|
||||||
|
*/
|
||||||
string DirNode::plainPath(const char *cipherPath_) {
|
string DirNode::plainPath(const char *cipherPath_) {
|
||||||
try {
|
try {
|
||||||
if (!strncmp(cipherPath_, rootDir.c_str(), rootDir.length())) {
|
|
||||||
return naming->decodePath(cipherPath_ + rootDir.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle special absolute path encodings.
|
// Handle special absolute path encodings.
|
||||||
char mark = fsConfig->reverseEncryption ? '/' : '+';
|
char mark = '+';
|
||||||
|
string prefix = "/";
|
||||||
|
if (fsConfig->reverseEncryption) {
|
||||||
|
mark = '/';
|
||||||
|
prefix = "+";
|
||||||
|
}
|
||||||
if (cipherPath_[0] == mark) {
|
if (cipherPath_[0] == mark) {
|
||||||
return string(fsConfig->reverseEncryption ? "+" : "/") +
|
return prefix + naming->decodeName(cipherPath_ + 1, strlen(cipherPath_ + 1));
|
||||||
naming->decodeName(cipherPath_ + 1, strlen(cipherPath_ + 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default.
|
// Default.
|
||||||
|
@ -41,6 +41,9 @@ struct EncFS_Opts;
|
|||||||
class Cipher;
|
class Cipher;
|
||||||
class NameIO;
|
class NameIO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persistent configuration (stored in config file .encfs6.xml)
|
||||||
|
*/
|
||||||
struct EncFSConfig {
|
struct EncFSConfig {
|
||||||
ConfigType cfgType;
|
ConfigType cfgType;
|
||||||
|
|
||||||
|
@ -967,7 +967,6 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts) {
|
|||||||
long desiredKDFDuration = NormalKDFDuration;
|
long desiredKDFDuration = NormalKDFDuration;
|
||||||
|
|
||||||
if (reverseEncryption) {
|
if (reverseEncryption) {
|
||||||
uniqueIV = false;
|
|
||||||
chainedIV = false;
|
chainedIV = false;
|
||||||
externalIV = false;
|
externalIV = false;
|
||||||
blockMACBytes = 0;
|
blockMACBytes = 0;
|
||||||
@ -976,7 +975,7 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts) {
|
|||||||
|
|
||||||
if (configMode == Config_Paranoia || answer[0] == 'p') {
|
if (configMode == Config_Paranoia || answer[0] == 'p') {
|
||||||
if (reverseEncryption) {
|
if (reverseEncryption) {
|
||||||
rError(_("Paranoia configuration not supported for --reverse"));
|
rError(_("Paranoia configuration not supported for reverse encryption"));
|
||||||
return rootInfo;
|
return rootInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1011,7 +1010,7 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts) {
|
|||||||
uniqueIV = true;
|
uniqueIV = true;
|
||||||
|
|
||||||
if (reverseEncryption) {
|
if (reverseEncryption) {
|
||||||
cout << _("--reverse specified, not using chained IV") << "\n";
|
cout << _("reverse encryption - chained IV disabled") << "\n";
|
||||||
} else {
|
} else {
|
||||||
chainedIV = true;
|
chainedIV = true;
|
||||||
}
|
}
|
||||||
@ -1035,7 +1034,13 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts) {
|
|||||||
blockSize = selectBlockSize(alg);
|
blockSize = selectBlockSize(alg);
|
||||||
nameIOIface = selectNameCoding();
|
nameIOIface = selectNameCoding();
|
||||||
if (reverseEncryption) {
|
if (reverseEncryption) {
|
||||||
cout << _("--reverse specified, not using unique/chained IV") << "\n";
|
cout << _("reverse encryption - chained IV and MAC disabled") << "\n";
|
||||||
|
uniqueIV = selectUniqueIV();
|
||||||
|
/* Reverse mounts are read-only by default (set in main.cpp).
|
||||||
|
* If uniqueIV is off, writing can be allowed, because there
|
||||||
|
* is no header that could be overwritten */
|
||||||
|
if (uniqueIV == false)
|
||||||
|
opts->readOnly = false;
|
||||||
} else {
|
} else {
|
||||||
chainedIV = selectChainedIV();
|
chainedIV = selectChainedIV();
|
||||||
uniqueIV = selectUniqueIV();
|
uniqueIV = selectUniqueIV();
|
||||||
|
@ -58,6 +58,11 @@ typedef shared_ptr<EncFS_Root> RootPtr;
|
|||||||
|
|
||||||
enum ConfigMode { Config_Prompt, Config_Standard, Config_Paranoia };
|
enum ConfigMode { Config_Prompt, Config_Standard, Config_Paranoia };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EncFS_Opts stores internal settings
|
||||||
|
*
|
||||||
|
* See struct EncFS_Args (main.cpp) for the parsed command line arguments
|
||||||
|
*/
|
||||||
struct EncFS_Opts {
|
struct EncFS_Opts {
|
||||||
std::string rootDir;
|
std::string rootDir;
|
||||||
bool createIfNotFound; // create filesystem if not found
|
bool createIfNotFound; // create filesystem if not found
|
||||||
@ -81,6 +86,8 @@ struct EncFS_Opts {
|
|||||||
* behind the back of EncFS (for example, in reverse mode).
|
* behind the back of EncFS (for example, in reverse mode).
|
||||||
* See main.cpp for a longer explaination. */
|
* See main.cpp for a longer explaination. */
|
||||||
|
|
||||||
|
bool readOnly; // Mount read-only
|
||||||
|
|
||||||
ConfigMode configMode;
|
ConfigMode configMode;
|
||||||
|
|
||||||
EncFS_Opts() {
|
EncFS_Opts() {
|
||||||
@ -96,6 +103,7 @@ struct EncFS_Opts {
|
|||||||
reverseEncryption = false;
|
reverseEncryption = false;
|
||||||
configMode = Config_Prompt;
|
configMode = Config_Prompt;
|
||||||
noCache = false;
|
noCache = false;
|
||||||
|
readOnly = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,6 +70,18 @@ static EncFS_Context *context() {
|
|||||||
return (EncFS_Context *)fuse_get_context()->private_data;
|
return (EncFS_Context *)fuse_get_context()->private_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function - determine if the filesystem is read-only
|
||||||
|
* Optionally takes a pointer to the EncFS_Context, will get it from FUSE
|
||||||
|
* if the argument is NULL.
|
||||||
|
*/
|
||||||
|
static bool isReadOnly(EncFS_Context *ctx) {
|
||||||
|
if (ctx == NULL)
|
||||||
|
ctx = (EncFS_Context *)fuse_get_context()->private_data;
|
||||||
|
|
||||||
|
return ctx->opts->readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
// helper function -- apply a functor to a cipher path, given the plain path
|
// helper function -- apply a functor to a cipher path, given the plain path
|
||||||
static int withCipherPath(const char *opName, const char *path,
|
static int withCipherPath(const char *opName, const char *path,
|
||||||
function<int(EncFS_Context *, const string &)> op,
|
function<int(EncFS_Context *, const string &)> op,
|
||||||
@ -130,11 +142,10 @@ static int withFileNode(const char *opName, const char *path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The rLog messages below always print out encrypted filenames, not
|
The rLog messages below always prints out encrypted filenames, not
|
||||||
plaintext. The reason is so that it isn't possible to leak information
|
plaintext. The reason is so that it isn't possible to leak information
|
||||||
about the encrypted data through rlog interfaces.
|
about the encrypted data through rlog interfaces.
|
||||||
|
|
||||||
|
|
||||||
The purpose of this layer of code is to take the FUSE request and dispatch
|
The purpose of this layer of code is to take the FUSE request and dispatch
|
||||||
to the internal interfaces. Any marshaling of arguments and return types
|
to the internal interfaces. Any marshaling of arguments and return types
|
||||||
can be done here.
|
can be done here.
|
||||||
@ -215,6 +226,8 @@ int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) {
|
|||||||
int encfs_mknod(const char *path, mode_t mode, dev_t rdev) {
|
int encfs_mknod(const char *path, mode_t mode, dev_t rdev) {
|
||||||
EncFS_Context *ctx = context();
|
EncFS_Context *ctx = context();
|
||||||
|
|
||||||
|
if (isReadOnly(ctx)) return -EROFS;
|
||||||
|
|
||||||
int res = -EIO;
|
int res = -EIO;
|
||||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||||
if (!FSRoot) return res;
|
if (!FSRoot) return res;
|
||||||
@ -255,6 +268,8 @@ int encfs_mkdir(const char *path, mode_t mode) {
|
|||||||
fuse_context *fctx = fuse_get_context();
|
fuse_context *fctx = fuse_get_context();
|
||||||
EncFS_Context *ctx = context();
|
EncFS_Context *ctx = context();
|
||||||
|
|
||||||
|
if (isReadOnly(ctx)) return -EROFS;
|
||||||
|
|
||||||
int res = -EIO;
|
int res = -EIO;
|
||||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||||
if (!FSRoot) return res;
|
if (!FSRoot) return res;
|
||||||
@ -287,6 +302,8 @@ int encfs_mkdir(const char *path, mode_t mode) {
|
|||||||
int encfs_unlink(const char *path) {
|
int encfs_unlink(const char *path) {
|
||||||
EncFS_Context *ctx = context();
|
EncFS_Context *ctx = context();
|
||||||
|
|
||||||
|
if (isReadOnly(ctx)) return -EROFS;
|
||||||
|
|
||||||
int res = -EIO;
|
int res = -EIO;
|
||||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||||
if (!FSRoot) return res;
|
if (!FSRoot) return res;
|
||||||
@ -307,6 +324,7 @@ int _do_rmdir(EncFS_Context *, const string &cipherPath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int encfs_rmdir(const char *path) {
|
int encfs_rmdir(const char *path) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
return withCipherPath("rmdir", path, bind(_do_rmdir, _1, _2));
|
return withCipherPath("rmdir", path, bind(_do_rmdir, _1, _2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,17 +361,22 @@ int encfs_readlink(const char *path, char *buf, size_t size) {
|
|||||||
bind(_do_readlink, _1, _2, buf, size));
|
bind(_do_readlink, _1, _2, buf, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
int encfs_symlink(const char *from, const char *to) {
|
/**
|
||||||
|
* Create a symbolic link pointing to "to" named "from"
|
||||||
|
*/
|
||||||
|
int encfs_symlink(const char *to, const char *from) {
|
||||||
EncFS_Context *ctx = context();
|
EncFS_Context *ctx = context();
|
||||||
|
|
||||||
|
if (isReadOnly(ctx)) return -EROFS;
|
||||||
|
|
||||||
int res = -EIO;
|
int res = -EIO;
|
||||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||||
if (!FSRoot) return res;
|
if (!FSRoot) return res;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
string fromCName = FSRoot->cipherPath(from);
|
||||||
// allow fully qualified names in symbolic links.
|
// allow fully qualified names in symbolic links.
|
||||||
string fromCName = FSRoot->relativeCipherPath(from);
|
string toCName = FSRoot->relativeCipherPath(to);
|
||||||
string toCName = FSRoot->cipherPath(to);
|
|
||||||
|
|
||||||
rLog(Info, "symlink %s -> %s", fromCName.c_str(), toCName.c_str());
|
rLog(Info, "symlink %s -> %s", fromCName.c_str(), toCName.c_str());
|
||||||
|
|
||||||
@ -366,7 +389,7 @@ int encfs_symlink(const char *from, const char *to) {
|
|||||||
olduid = setfsuid(context->uid);
|
olduid = setfsuid(context->uid);
|
||||||
oldgid = setfsgid(context->gid);
|
oldgid = setfsgid(context->gid);
|
||||||
}
|
}
|
||||||
res = ::symlink(fromCName.c_str(), toCName.c_str());
|
res = ::symlink(toCName.c_str(), fromCName.c_str());
|
||||||
if (olduid >= 0) setfsuid(olduid);
|
if (olduid >= 0) setfsuid(olduid);
|
||||||
if (oldgid >= 0) setfsgid(oldgid);
|
if (oldgid >= 0) setfsgid(oldgid);
|
||||||
|
|
||||||
@ -384,6 +407,8 @@ int encfs_symlink(const char *from, const char *to) {
|
|||||||
int encfs_link(const char *from, const char *to) {
|
int encfs_link(const char *from, const char *to) {
|
||||||
EncFS_Context *ctx = context();
|
EncFS_Context *ctx = context();
|
||||||
|
|
||||||
|
if (isReadOnly(ctx)) return -EROFS;
|
||||||
|
|
||||||
int res = -EIO;
|
int res = -EIO;
|
||||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||||
if (!FSRoot) return res;
|
if (!FSRoot) return res;
|
||||||
@ -400,6 +425,8 @@ int encfs_link(const char *from, const char *to) {
|
|||||||
int encfs_rename(const char *from, const char *to) {
|
int encfs_rename(const char *from, const char *to) {
|
||||||
EncFS_Context *ctx = context();
|
EncFS_Context *ctx = context();
|
||||||
|
|
||||||
|
if (isReadOnly(ctx)) return -EROFS;
|
||||||
|
|
||||||
int res = -EIO;
|
int res = -EIO;
|
||||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||||
if (!FSRoot) return res;
|
if (!FSRoot) return res;
|
||||||
@ -418,6 +445,7 @@ int _do_chmod(EncFS_Context *, const string &cipherPath, mode_t mode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int encfs_chmod(const char *path, mode_t mode) {
|
int encfs_chmod(const char *path, mode_t mode) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
return withCipherPath("chmod", path, bind(_do_chmod, _1, _2, mode));
|
return withCipherPath("chmod", path, bind(_do_chmod, _1, _2, mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,16 +455,19 @@ int _do_chown(EncFS_Context *, const string &cyName, uid_t u, gid_t g) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int encfs_chown(const char *path, uid_t uid, gid_t gid) {
|
int encfs_chown(const char *path, uid_t uid, gid_t gid) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
return withCipherPath("chown", path, bind(_do_chown, _1, _2, uid, gid));
|
return withCipherPath("chown", path, bind(_do_chown, _1, _2, uid, gid));
|
||||||
}
|
}
|
||||||
|
|
||||||
int _do_truncate(FileNode *fnode, off_t size) { return fnode->truncate(size); }
|
int _do_truncate(FileNode *fnode, off_t size) { return fnode->truncate(size); }
|
||||||
|
|
||||||
int encfs_truncate(const char *path, off_t size) {
|
int encfs_truncate(const char *path, off_t size) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
return withFileNode("truncate", path, NULL, bind(_do_truncate, _1, size));
|
return withFileNode("truncate", path, NULL, bind(_do_truncate, _1, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
int encfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) {
|
int encfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
return withFileNode("ftruncate", path, fi, bind(_do_truncate, _1, size));
|
return withFileNode("ftruncate", path, fi, bind(_do_truncate, _1, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,6 +477,7 @@ int _do_utime(EncFS_Context *, const string &cyName, struct utimbuf *buf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int encfs_utime(const char *path, struct utimbuf *buf) {
|
int encfs_utime(const char *path, struct utimbuf *buf) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
return withCipherPath("utime", path, bind(_do_utime, _1, _2, buf));
|
return withCipherPath("utime", path, bind(_do_utime, _1, _2, buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,12 +494,16 @@ int _do_utimens(EncFS_Context *, const string &cyName,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int encfs_utimens(const char *path, const struct timespec ts[2]) {
|
int encfs_utimens(const char *path, const struct timespec ts[2]) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
return withCipherPath("utimens", path, bind(_do_utimens, _1, _2, ts));
|
return withCipherPath("utimens", path, bind(_do_utimens, _1, _2, ts));
|
||||||
}
|
}
|
||||||
|
|
||||||
int encfs_open(const char *path, struct fuse_file_info *file) {
|
int encfs_open(const char *path, struct fuse_file_info *file) {
|
||||||
EncFS_Context *ctx = context();
|
EncFS_Context *ctx = context();
|
||||||
|
|
||||||
|
if (isReadOnly(ctx) && (file->flags & O_WRONLY || file->flags & O_RDWR))
|
||||||
|
return -EROFS;
|
||||||
|
|
||||||
int res = -EIO;
|
int res = -EIO;
|
||||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||||
if (!FSRoot) return res;
|
if (!FSRoot) return res;
|
||||||
@ -508,6 +544,7 @@ int _do_flush(FileNode *fnode) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called on each close() of a file descriptor
|
||||||
int encfs_flush(const char *path, struct fuse_file_info *fi) {
|
int encfs_flush(const char *path, struct fuse_file_info *fi) {
|
||||||
return withFileNode("flush", path, fi, bind(_do_flush, _1));
|
return withFileNode("flush", path, fi, bind(_do_flush, _1));
|
||||||
}
|
}
|
||||||
@ -545,6 +582,7 @@ int _do_fsync(FileNode *fnode, int dataSync) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int encfs_fsync(const char *path, int dataSync, struct fuse_file_info *file) {
|
int encfs_fsync(const char *path, int dataSync, struct fuse_file_info *file) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
return withFileNode("fsync", path, file, bind(_do_fsync, _1, dataSync));
|
return withFileNode("fsync", path, file, bind(_do_fsync, _1, dataSync));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,6 +595,7 @@ int _do_write(FileNode *fnode, unsigned char *ptr, size_t size, off_t offset) {
|
|||||||
|
|
||||||
int encfs_write(const char *path, const char *buf, size_t size, off_t offset,
|
int encfs_write(const char *path, const char *buf, size_t size, off_t offset,
|
||||||
struct fuse_file_info *file) {
|
struct fuse_file_info *file) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
return withFileNode("write", path, file,
|
return withFileNode("write", path, file,
|
||||||
bind(_do_write, _1, (unsigned char *)buf, size, offset));
|
bind(_do_write, _1, (unsigned char *)buf, size, offset));
|
||||||
}
|
}
|
||||||
@ -595,6 +634,7 @@ int _do_setxattr(EncFS_Context *, const string &cyName, const char *name,
|
|||||||
}
|
}
|
||||||
int encfs_setxattr(const char *path, const char *name, const char *value,
|
int encfs_setxattr(const char *path, const char *name, const char *value,
|
||||||
size_t size, int flags, uint32_t position) {
|
size_t size, int flags, uint32_t position) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
(void)flags;
|
(void)flags;
|
||||||
return withCipherPath("setxattr", path, bind(_do_setxattr, _1, _2, name,
|
return withCipherPath("setxattr", path, bind(_do_setxattr, _1, _2, name,
|
||||||
value, size, position));
|
value, size, position));
|
||||||
@ -606,6 +646,7 @@ int _do_setxattr(EncFS_Context *, const string &cyName, const char *name,
|
|||||||
}
|
}
|
||||||
int encfs_setxattr(const char *path, const char *name, const char *value,
|
int encfs_setxattr(const char *path, const char *name, const char *value,
|
||||||
size_t size, int flags) {
|
size_t size, int flags) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
return withCipherPath("setxattr", path,
|
return withCipherPath("setxattr", path,
|
||||||
bind(_do_setxattr, _1, _2, name, value, size, flags));
|
bind(_do_setxattr, _1, _2, name, value, size, flags));
|
||||||
}
|
}
|
||||||
@ -663,6 +704,8 @@ int _do_removexattr(EncFS_Context *, const string &cyName, const char *name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int encfs_removexattr(const char *path, const char *name) {
|
int encfs_removexattr(const char *path, const char *name) {
|
||||||
|
if (isReadOnly(NULL)) return -EROFS;
|
||||||
|
|
||||||
return withCipherPath("removexattr", path,
|
return withCipherPath("removexattr", path,
|
||||||
bind(_do_removexattr, _1, _2, name));
|
bind(_do_removexattr, _1, _2, name));
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,12 @@ using gnu::autosprintf;
|
|||||||
// Maximum number of arguments that we're going to pass on to fuse. Doesn't
|
// Maximum number of arguments that we're going to pass on to fuse. Doesn't
|
||||||
// affect how many arguments we can handle, just how many we can pass on..
|
// affect how many arguments we can handle, just how many we can pass on..
|
||||||
const int MaxFuseArgs = 32;
|
const int MaxFuseArgs = 32;
|
||||||
|
/**
|
||||||
|
* EncFS_Args stores the parsed command-line arguments
|
||||||
|
*
|
||||||
|
* See also: struct EncFS_Opts (FileUtils.h), stores internal settings that are
|
||||||
|
* derived from the arguments
|
||||||
|
*/
|
||||||
struct EncFS_Args {
|
struct EncFS_Args {
|
||||||
string mountPoint; // where to make filesystem visible
|
string mountPoint; // where to make filesystem visible
|
||||||
bool isDaemon; // true == spawn in background, log to syslog
|
bool isDaemon; // true == spawn in background, log to syslog
|
||||||
@ -273,6 +279,11 @@ static bool processArgs(int argc, char *argv[],
|
|||||||
case 'D':
|
case 'D':
|
||||||
out->opts->forceDecode = true;
|
out->opts->forceDecode = true;
|
||||||
break;
|
break;
|
||||||
|
case 'r':
|
||||||
|
out->opts->reverseEncryption = true;
|
||||||
|
/* Reverse encryption does not support writing unless uniqueIV
|
||||||
|
* is disabled (expert mode) */
|
||||||
|
out->opts->readOnly = true;
|
||||||
/* By default, the kernel caches file metadata for one second.
|
/* By default, the kernel caches file metadata for one second.
|
||||||
* This is fine for EncFS' normal mode, but for --reverse, this
|
* This is fine for EncFS' normal mode, but for --reverse, this
|
||||||
* means that the encrypted view will be up to one second out of
|
* means that the encrypted view will be up to one second out of
|
||||||
@ -284,9 +295,7 @@ static bool processArgs(int argc, char *argv[],
|
|||||||
* filesystem where something can change the underlying
|
* filesystem where something can change the underlying
|
||||||
* filesystem without going through fuse can run into
|
* filesystem without going through fuse can run into
|
||||||
* inconsistencies."
|
* inconsistencies."
|
||||||
* Enabling reverse automatically enables noCache. */
|
* Enabling reverse automatically enables noCache */
|
||||||
case 'r':
|
|
||||||
out->opts->reverseEncryption = true;
|
|
||||||
case 514:
|
case 514:
|
||||||
/* Disable EncFS block cache
|
/* Disable EncFS block cache
|
||||||
* Causes reverse grow tests to fail because short reads
|
* Causes reverse grow tests to fail because short reads
|
||||||
|
@ -72,5 +72,26 @@ sub waitForFile
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# writeZeroes($filename, $size)
|
||||||
|
# Write zeroes of size $size to file $filename
|
||||||
|
sub writeZeroes
|
||||||
|
{
|
||||||
|
my $filename = shift;
|
||||||
|
my $size = shift;
|
||||||
|
open(my $fh, ">", $filename);
|
||||||
|
my $bs = 4096; # 4 KiB
|
||||||
|
my $block = "\0" x $bs;
|
||||||
|
my $remain;
|
||||||
|
for($remain = $size; $remain >= $bs; $remain -= $bs)
|
||||||
|
{
|
||||||
|
print($fh $block) or BAIL_OUT("Could not write to $filename: $!");
|
||||||
|
}
|
||||||
|
if($remain > 0)
|
||||||
|
{
|
||||||
|
$block = "\0" x $remain;
|
||||||
|
print($fh $block) or BAIL_OUT("Could not write to $filename: $!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# As this file will be require()'d, it needs to return true
|
# As this file will be require()'d, it needs to return true
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Test EncFS normal and paranoid mode
|
# Test EncFS normal and paranoid mode
|
||||||
|
|
||||||
use Test::More qw( no_plan );
|
use Test::More tests => 101;
|
||||||
use File::Path;
|
use File::Path;
|
||||||
use File::Copy;
|
use File::Copy;
|
||||||
use File::Temp;
|
use File::Temp;
|
||||||
@ -88,7 +88,7 @@ sub corruption
|
|||||||
sub internalModification
|
sub internalModification
|
||||||
{
|
{
|
||||||
$ofile = "$workingDir/crypt-internal-$$";
|
$ofile = "$workingDir/crypt-internal-$$";
|
||||||
qx(dd if=/dev/urandom of=$ofile bs=2k count=2 2> /dev/null);
|
writeZeroes($ofile, 2*1024);
|
||||||
ok(copy($ofile, "$crypt/internal"), "copying crypt-internal file");
|
ok(copy($ofile, "$crypt/internal"), "copying crypt-internal file");
|
||||||
|
|
||||||
open(my $out1, "+<", "$crypt/internal");
|
open(my $out1, "+<", "$crypt/internal");
|
||||||
@ -289,7 +289,7 @@ sub links
|
|||||||
is( readlink("$crypt/data-rel"), "data", "read rel symlink");
|
is( readlink("$crypt/data-rel"), "data", "read rel symlink");
|
||||||
|
|
||||||
SKIP: {
|
SKIP: {
|
||||||
skip "No hardlink support" unless $hardlinkTests;
|
skip "No hardlink support", 2 unless $hardlinkTests;
|
||||||
|
|
||||||
ok( link("$crypt/data", "$crypt/data.2"), "hard link");
|
ok( link("$crypt/data", "$crypt/data.2"), "hard link");
|
||||||
checkContents("$crypt/data.2", $contents, "hardlink read");
|
checkContents("$crypt/data.2", $contents, "hardlink read");
|
||||||
@ -306,6 +306,7 @@ sub mount
|
|||||||
mkdir($raw) || BAIL_OUT("Could not create $raw: $!");
|
mkdir($raw) || BAIL_OUT("Could not create $raw: $!");
|
||||||
mkdir($crypt) || BAIL_OUT("Could not create $crypt: $!");
|
mkdir($crypt) || BAIL_OUT("Could not create $crypt: $!");
|
||||||
|
|
||||||
|
delete $ENV{"ENCFS6_CONFIG"};
|
||||||
qx(./encfs/encfs --extpass="echo test" $args $raw $crypt);
|
qx(./encfs/encfs --extpass="echo test" $args $raw $crypt);
|
||||||
|
|
||||||
ok( -f "$raw/.encfs6.xml", "created control file");
|
ok( -f "$raw/.encfs6.xml", "created control file");
|
||||||
|
@ -3,10 +3,11 @@
|
|||||||
# Test EncFS --reverse mode
|
# Test EncFS --reverse mode
|
||||||
|
|
||||||
use warnings;
|
use warnings;
|
||||||
use Test::More qw( no_plan );
|
use Test::More tests => 26;
|
||||||
use File::Path;
|
use File::Path;
|
||||||
use File::Temp;
|
use File::Temp;
|
||||||
use IO::Handle;
|
use IO::Handle;
|
||||||
|
use Errno qw(EROFS);
|
||||||
|
|
||||||
require("tests/common.inc");
|
require("tests/common.inc");
|
||||||
|
|
||||||
@ -44,6 +45,7 @@ sub cleanup
|
|||||||
# Directory structure: plain -[encrypt]-> ciphertext -[decrypt]-> decrypted
|
# Directory structure: plain -[encrypt]-> ciphertext -[decrypt]-> decrypted
|
||||||
sub mount
|
sub mount
|
||||||
{
|
{
|
||||||
|
delete $ENV{"ENCFS6_CONFIG"};
|
||||||
system("./encfs/encfs --extpass=\"echo test\" --standard $plain $ciphertext --reverse");
|
system("./encfs/encfs --extpass=\"echo test\" --standard $plain $ciphertext --reverse");
|
||||||
ok(waitForFile("$plain/.encfs6.xml"), "plain .encfs6.xml exists") or BAIL_OUT("'$plain/.encfs6.xml'");
|
ok(waitForFile("$plain/.encfs6.xml"), "plain .encfs6.xml exists") or BAIL_OUT("'$plain/.encfs6.xml'");
|
||||||
my $e = encName(".encfs6.xml");
|
my $e = encName(".encfs6.xml");
|
||||||
@ -68,7 +70,6 @@ sub copy_test
|
|||||||
{
|
{
|
||||||
ok(system("cp -a encfs $plain")==0, "copying files to plain");
|
ok(system("cp -a encfs $plain")==0, "copying files to plain");
|
||||||
ok(system("diff -r -q $plain $decrypted")==0, "decrypted files are identical");
|
ok(system("diff -r -q $plain $decrypted")==0, "decrypted files are identical");
|
||||||
|
|
||||||
ok(-f "$plain/encfs/encfs.cpp", "file exists");
|
ok(-f "$plain/encfs/encfs.cpp", "file exists");
|
||||||
unlink("$plain/encfs/encfs.cpp");
|
unlink("$plain/encfs/encfs.cpp");
|
||||||
ok(! -f "$decrypted/encfs.cpp", "file deleted");
|
ok(! -f "$decrypted/encfs.cpp", "file deleted");
|
||||||
@ -78,9 +79,11 @@ sub copy_test
|
|||||||
# Parameter: symlink target
|
# Parameter: symlink target
|
||||||
sub symlink_test
|
sub symlink_test
|
||||||
{
|
{
|
||||||
my $target = shift(@_);
|
my $target = shift;
|
||||||
symlink($target, "$plain/symlink");
|
symlink($target, "$plain/symlink");
|
||||||
ok( readlink("$decrypted/symlink") eq "$target", "symlink to '$target'");
|
$dec = readlink("$decrypted/symlink");
|
||||||
|
ok( $dec eq $target, "symlink to '$target'") or
|
||||||
|
print("# (original) $target' != '$dec' (decrypted)\n");
|
||||||
unlink("$plain/symlink");
|
unlink("$plain/symlink");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +132,7 @@ sub grow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub largeRead {
|
sub largeRead {
|
||||||
system("dd if=/dev/zero of=$plain/largeRead bs=1M count=1 2> /dev/null");
|
writeZeroes("$plain/largeRead", 1024*1024);
|
||||||
# ciphertext file name
|
# ciphertext file name
|
||||||
my $cname = encName("largeRead");
|
my $cname = encName("largeRead");
|
||||||
# cfh ... ciphertext file handle
|
# cfh ... ciphertext file handle
|
||||||
@ -137,6 +140,32 @@ sub largeRead {
|
|||||||
ok(sizeVerify($cfh, 1024*1024+8), "1M file size");
|
ok(sizeVerify($cfh, 1024*1024+8), "1M file size");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Check that the reverse mount is read-only
|
||||||
|
# (writing is not supported in reverse mode because of the added
|
||||||
|
# complexity and the marginal use case)
|
||||||
|
sub writesDenied {
|
||||||
|
$fn = "$plain/writesDenied";
|
||||||
|
writeZeroes($fn, 1024);
|
||||||
|
my $efn = $ciphertext . "/" . encName("writesDenied");
|
||||||
|
open(my $fh, ">", $efn);
|
||||||
|
if( ok( $! == EROFS, "open for write denied, EROFS")) {
|
||||||
|
ok( 1, "writing denied, filehandle not open");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print($fh "foo");
|
||||||
|
ok( $! == EROFS, "writing denied, EROFS");
|
||||||
|
}
|
||||||
|
$target = $ciphertext . "/" . encName("writesDenied2");
|
||||||
|
rename($efn, $target);
|
||||||
|
ok( $! == EROFS, "rename denied, EROFS") or die();
|
||||||
|
unlink($efn);
|
||||||
|
ok( $! == EROFS, "unlink denied, EROFS");
|
||||||
|
utime(undef, undef, $efn) ;
|
||||||
|
ok( $! == EROFS, "touch denied, EROFS");
|
||||||
|
truncate($efn, 10);
|
||||||
|
ok( $! == EROFS, "truncate denied, EROFS");
|
||||||
|
}
|
||||||
|
|
||||||
# Setup mounts
|
# Setup mounts
|
||||||
newWorkingDir();
|
newWorkingDir();
|
||||||
mount();
|
mount();
|
||||||
@ -149,6 +178,8 @@ symlink_test("/"); # absolute
|
|||||||
symlink_test("foo"); # relative
|
symlink_test("foo"); # relative
|
||||||
symlink_test("/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/15/17/18"); # long
|
symlink_test("/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/15/17/18"); # long
|
||||||
symlink_test("!§\$%&/()\\<>#+="); # special characters
|
symlink_test("!§\$%&/()\\<>#+="); # special characters
|
||||||
|
symlink_test("$plain/foo");
|
||||||
|
writesDenied();
|
||||||
|
|
||||||
# Umount and delete files
|
# Umount and delete files
|
||||||
cleanup();
|
cleanup();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user