mirror of
https://github.com/vgough/encfs.git
synced 2024-12-24 23:59:08 +01:00
reverse: Filesystem is read-only of uniqueIV is enabled
Writing to the ciphertext files can rewrite the header. This would mean we had to re-encrypt the whole file with the new IV. This could be made more fine-grained, for example allowing writes to everywhere but the header. However, this is something that needs a lot of testing to ensure correctness. Writing to the ciphertext is a niche use case of the niche use case of using reverse mode, so it is unlikely it would get the test coverage it needs. To be safe, we deny all modifications of the ciphertext with read-only filesystem error (EROFS) if uniqueIV is enabled. Reverse mode with uniqueIV disabled still supports writing, if somebody really needs it. This use case is not covered by the test suite at the moment.
This commit is contained in:
parent
d1363578fc
commit
32102447e0
@ -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;
|
||||||
|
|
||||||
|
@ -1036,6 +1036,8 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts) {
|
|||||||
if (reverseEncryption) {
|
if (reverseEncryption) {
|
||||||
cout << _("reverse encryption - chained IV and MAC disabled") << "\n";
|
cout << _("reverse encryption - chained IV and MAC disabled") << "\n";
|
||||||
uniqueIV = selectUniqueIV();
|
uniqueIV = selectUniqueIV();
|
||||||
|
if (uniqueIV == 0)
|
||||||
|
opts->readOnly = 0; // Reverse supports rw mode only if uniqueIV is disabled
|
||||||
} 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,6 +364,8 @@ int encfs_readlink(const char *path, char *buf, size_t size) {
|
|||||||
int encfs_symlink(const char *from, const char *to) {
|
int encfs_symlink(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;
|
||||||
@ -384,6 +404,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 +422,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 +442,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 +452,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 +474,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 +491,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 +541,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 +579,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 +592,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 +631,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 +643,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 +701,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));
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,7 @@ int encfs_write(const char *path, const char *buf, size_t size, off_t offset,
|
|||||||
int encfs_statfs(const char *, struct statvfs *fst);
|
int encfs_statfs(const char *, struct statvfs *fst);
|
||||||
int encfs_flush(const char *, struct fuse_file_info *info);
|
int encfs_flush(const char *, struct fuse_file_info *info);
|
||||||
int encfs_fsync(const char *path, int flags, struct fuse_file_info *info);
|
int encfs_fsync(const char *path, int flags, struct fuse_file_info *info);
|
||||||
|
int encfs_readonly_error(...);
|
||||||
|
|
||||||
#ifdef HAVE_XATTR
|
#ifdef HAVE_XATTR
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 => 25;
|
||||||
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");
|
||||||
|
|
||||||
@ -68,7 +69,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");
|
||||||
@ -137,6 +137,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, filhandle 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 +175,7 @@ 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
|
||||||
|
writesDenied();
|
||||||
|
|
||||||
# Umount and delete files
|
# Umount and delete files
|
||||||
cleanup();
|
cleanup();
|
||||||
|
Loading…
Reference in New Issue
Block a user