mirror of
https://github.com/vgough/encfs.git
synced 2025-03-31 03:16:16 +02:00
commit
40531024c8
@ -275,25 +275,43 @@ string DirNode::rootDirectory() {
|
||||
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) {
|
||||
return rootDir + naming->encodePath(plaintextPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as cipherPath(), but does not prefix the ciphertext root directory
|
||||
*/
|
||||
string DirNode::cipherPathWithoutRoot(const char *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_) {
|
||||
try {
|
||||
if (!strncmp(cipherPath_, rootDir.c_str(), rootDir.length())) {
|
||||
return naming->decodePath(cipherPath_ + rootDir.length());
|
||||
}
|
||||
|
||||
// Handle special absolute path encodings.
|
||||
char mark = fsConfig->reverseEncryption ? '/' : '+';
|
||||
char mark = '+';
|
||||
string prefix = "/";
|
||||
if (fsConfig->reverseEncryption) {
|
||||
mark = '/';
|
||||
prefix = "+";
|
||||
}
|
||||
if (cipherPath_[0] == mark) {
|
||||
return string(fsConfig->reverseEncryption ? "+" : "/") +
|
||||
naming->decodeName(cipherPath_ + 1, strlen(cipherPath_ + 1));
|
||||
return prefix + naming->decodeName(cipherPath_ + 1, strlen(cipherPath_ + 1));
|
||||
}
|
||||
|
||||
// Default.
|
||||
|
@ -41,6 +41,9 @@ struct EncFS_Opts;
|
||||
class Cipher;
|
||||
class NameIO;
|
||||
|
||||
/**
|
||||
* Persistent configuration (stored in config file .encfs6.xml)
|
||||
*/
|
||||
struct EncFSConfig {
|
||||
ConfigType cfgType;
|
||||
|
||||
|
@ -967,7 +967,6 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts) {
|
||||
long desiredKDFDuration = NormalKDFDuration;
|
||||
|
||||
if (reverseEncryption) {
|
||||
uniqueIV = false;
|
||||
chainedIV = false;
|
||||
externalIV = false;
|
||||
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 (reverseEncryption) {
|
||||
rError(_("Paranoia configuration not supported for --reverse"));
|
||||
rError(_("Paranoia configuration not supported for reverse encryption"));
|
||||
return rootInfo;
|
||||
}
|
||||
|
||||
@ -1011,7 +1010,7 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts) {
|
||||
uniqueIV = true;
|
||||
|
||||
if (reverseEncryption) {
|
||||
cout << _("--reverse specified, not using chained IV") << "\n";
|
||||
cout << _("reverse encryption - chained IV disabled") << "\n";
|
||||
} else {
|
||||
chainedIV = true;
|
||||
}
|
||||
@ -1035,7 +1034,13 @@ RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts) {
|
||||
blockSize = selectBlockSize(alg);
|
||||
nameIOIface = selectNameCoding();
|
||||
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 {
|
||||
chainedIV = selectChainedIV();
|
||||
uniqueIV = selectUniqueIV();
|
||||
|
@ -58,6 +58,11 @@ typedef shared_ptr<EncFS_Root> RootPtr;
|
||||
|
||||
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 {
|
||||
std::string rootDir;
|
||||
bool createIfNotFound; // create filesystem if not found
|
||||
@ -81,6 +86,8 @@ struct EncFS_Opts {
|
||||
* behind the back of EncFS (for example, in reverse mode).
|
||||
* See main.cpp for a longer explaination. */
|
||||
|
||||
bool readOnly; // Mount read-only
|
||||
|
||||
ConfigMode configMode;
|
||||
|
||||
EncFS_Opts() {
|
||||
@ -96,6 +103,7 @@ struct EncFS_Opts {
|
||||
reverseEncryption = false;
|
||||
configMode = Config_Prompt;
|
||||
noCache = false;
|
||||
readOnly = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -70,6 +70,18 @@ static EncFS_Context *context() {
|
||||
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
|
||||
static int withCipherPath(const char *opName, const char *path,
|
||||
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
|
||||
about the encrypted data through rlog interfaces.
|
||||
|
||||
|
||||
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
|
||||
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) {
|
||||
EncFS_Context *ctx = context();
|
||||
|
||||
if (isReadOnly(ctx)) return -EROFS;
|
||||
|
||||
int res = -EIO;
|
||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||
if (!FSRoot) return res;
|
||||
@ -255,6 +268,8 @@ int encfs_mkdir(const char *path, mode_t mode) {
|
||||
fuse_context *fctx = fuse_get_context();
|
||||
EncFS_Context *ctx = context();
|
||||
|
||||
if (isReadOnly(ctx)) return -EROFS;
|
||||
|
||||
int res = -EIO;
|
||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||
if (!FSRoot) return res;
|
||||
@ -287,6 +302,8 @@ int encfs_mkdir(const char *path, mode_t mode) {
|
||||
int encfs_unlink(const char *path) {
|
||||
EncFS_Context *ctx = context();
|
||||
|
||||
if (isReadOnly(ctx)) return -EROFS;
|
||||
|
||||
int res = -EIO;
|
||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||
if (!FSRoot) return res;
|
||||
@ -307,6 +324,7 @@ int _do_rmdir(EncFS_Context *, const string &cipherPath) {
|
||||
}
|
||||
|
||||
int encfs_rmdir(const char *path) {
|
||||
if (isReadOnly(NULL)) return -EROFS;
|
||||
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));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if (isReadOnly(ctx)) return -EROFS;
|
||||
|
||||
int res = -EIO;
|
||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||
if (!FSRoot) return res;
|
||||
|
||||
try {
|
||||
string fromCName = FSRoot->cipherPath(from);
|
||||
// allow fully qualified names in symbolic links.
|
||||
string fromCName = FSRoot->relativeCipherPath(from);
|
||||
string toCName = FSRoot->cipherPath(to);
|
||||
string toCName = FSRoot->relativeCipherPath(to);
|
||||
|
||||
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);
|
||||
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 (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) {
|
||||
EncFS_Context *ctx = context();
|
||||
|
||||
if (isReadOnly(ctx)) return -EROFS;
|
||||
|
||||
int res = -EIO;
|
||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&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) {
|
||||
EncFS_Context *ctx = context();
|
||||
|
||||
if (isReadOnly(ctx)) return -EROFS;
|
||||
|
||||
int res = -EIO;
|
||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&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) {
|
||||
if (isReadOnly(NULL)) return -EROFS;
|
||||
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) {
|
||||
if (isReadOnly(NULL)) return -EROFS;
|
||||
return withCipherPath("chown", path, bind(_do_chown, _1, _2, uid, gid));
|
||||
}
|
||||
|
||||
int _do_truncate(FileNode *fnode, off_t size) { return fnode->truncate(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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@ -446,6 +477,7 @@ int _do_utime(EncFS_Context *, const string &cyName, 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));
|
||||
}
|
||||
|
||||
@ -462,12 +494,16 @@ int _do_utimens(EncFS_Context *, const string &cyName,
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
int encfs_open(const char *path, struct fuse_file_info *file) {
|
||||
EncFS_Context *ctx = context();
|
||||
|
||||
if (isReadOnly(ctx) && (file->flags & O_WRONLY || file->flags & O_RDWR))
|
||||
return -EROFS;
|
||||
|
||||
int res = -EIO;
|
||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
||||
if (!FSRoot) return res;
|
||||
@ -508,6 +544,7 @@ int _do_flush(FileNode *fnode) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Called on each close() of a file descriptor
|
||||
int encfs_flush(const char *path, struct fuse_file_info *fi) {
|
||||
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) {
|
||||
if (isReadOnly(NULL)) return -EROFS;
|
||||
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,
|
||||
struct fuse_file_info *file) {
|
||||
if (isReadOnly(NULL)) return -EROFS;
|
||||
return withFileNode("write", path, file,
|
||||
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,
|
||||
size_t size, int flags, uint32_t position) {
|
||||
if (isReadOnly(NULL)) return -EROFS;
|
||||
(void)flags;
|
||||
return withCipherPath("setxattr", path, bind(_do_setxattr, _1, _2, name,
|
||||
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,
|
||||
size_t size, int flags) {
|
||||
if (isReadOnly(NULL)) return -EROFS;
|
||||
return withCipherPath("setxattr", path,
|
||||
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) {
|
||||
if (isReadOnly(NULL)) return -EROFS;
|
||||
|
||||
return withCipherPath("removexattr", path,
|
||||
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
|
||||
// affect how many arguments we can handle, just how many we can pass on..
|
||||
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 {
|
||||
string mountPoint; // where to make filesystem visible
|
||||
bool isDaemon; // true == spawn in background, log to syslog
|
||||
@ -273,20 +279,23 @@ static bool processArgs(int argc, char *argv[],
|
||||
case 'D':
|
||||
out->opts->forceDecode = true;
|
||||
break;
|
||||
/* By default, the kernel caches file metadata for one second.
|
||||
* This is fine for EncFS' normal mode, but for --reverse, this
|
||||
* means that the encrypted view will be up to one second out of
|
||||
* date.
|
||||
* Quoting Goswin von Brederlow:
|
||||
* "Caching only works correctly if you implement a disk based
|
||||
* filesystem, one where only the fuse process can alter
|
||||
* metadata and all access goes only through fuse. Any overlay
|
||||
* filesystem where something can change the underlying
|
||||
* filesystem without going through fuse can run into
|
||||
* inconsistencies."
|
||||
* Enabling reverse automatically enables noCache. */
|
||||
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.
|
||||
* This is fine for EncFS' normal mode, but for --reverse, this
|
||||
* means that the encrypted view will be up to one second out of
|
||||
* date.
|
||||
* Quoting Goswin von Brederlow:
|
||||
* "Caching only works correctly if you implement a disk based
|
||||
* filesystem, one where only the fuse process can alter
|
||||
* metadata and all access goes only through fuse. Any overlay
|
||||
* filesystem where something can change the underlying
|
||||
* filesystem without going through fuse can run into
|
||||
* inconsistencies."
|
||||
* Enabling reverse automatically enables noCache */
|
||||
case 514:
|
||||
/* Disable EncFS block cache
|
||||
* Causes reverse grow tests to fail because short reads
|
||||
|
@ -72,5 +72,26 @@ sub waitForFile
|
||||
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
|
||||
return 1;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# Test EncFS normal and paranoid mode
|
||||
|
||||
use Test::More qw( no_plan );
|
||||
use Test::More tests => 101;
|
||||
use File::Path;
|
||||
use File::Copy;
|
||||
use File::Temp;
|
||||
@ -88,7 +88,7 @@ sub corruption
|
||||
sub internalModification
|
||||
{
|
||||
$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");
|
||||
|
||||
open(my $out1, "+<", "$crypt/internal");
|
||||
@ -289,7 +289,7 @@ sub links
|
||||
is( readlink("$crypt/data-rel"), "data", "read rel symlink");
|
||||
|
||||
SKIP: {
|
||||
skip "No hardlink support" unless $hardlinkTests;
|
||||
skip "No hardlink support", 2 unless $hardlinkTests;
|
||||
|
||||
ok( link("$crypt/data", "$crypt/data.2"), "hard link");
|
||||
checkContents("$crypt/data.2", $contents, "hardlink read");
|
||||
@ -306,6 +306,7 @@ sub mount
|
||||
mkdir($raw) || BAIL_OUT("Could not create $raw: $!");
|
||||
mkdir($crypt) || BAIL_OUT("Could not create $crypt: $!");
|
||||
|
||||
delete $ENV{"ENCFS6_CONFIG"};
|
||||
qx(./encfs/encfs --extpass="echo test" $args $raw $crypt);
|
||||
|
||||
ok( -f "$raw/.encfs6.xml", "created control file");
|
||||
|
@ -3,10 +3,11 @@
|
||||
# Test EncFS --reverse mode
|
||||
|
||||
use warnings;
|
||||
use Test::More qw( no_plan );
|
||||
use Test::More tests => 26;
|
||||
use File::Path;
|
||||
use File::Temp;
|
||||
use IO::Handle;
|
||||
use Errno qw(EROFS);
|
||||
|
||||
require("tests/common.inc");
|
||||
|
||||
@ -44,6 +45,7 @@ sub cleanup
|
||||
# Directory structure: plain -[encrypt]-> ciphertext -[decrypt]-> decrypted
|
||||
sub mount
|
||||
{
|
||||
delete $ENV{"ENCFS6_CONFIG"};
|
||||
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'");
|
||||
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("diff -r -q $plain $decrypted")==0, "decrypted files are identical");
|
||||
|
||||
ok(-f "$plain/encfs/encfs.cpp", "file exists");
|
||||
unlink("$plain/encfs/encfs.cpp");
|
||||
ok(! -f "$decrypted/encfs.cpp", "file deleted");
|
||||
@ -78,9 +79,11 @@ sub copy_test
|
||||
# Parameter: symlink target
|
||||
sub symlink_test
|
||||
{
|
||||
my $target = shift(@_);
|
||||
my $target = shift;
|
||||
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");
|
||||
}
|
||||
|
||||
@ -129,7 +132,7 @@ sub grow {
|
||||
}
|
||||
|
||||
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
|
||||
my $cname = encName("largeRead");
|
||||
# cfh ... ciphertext file handle
|
||||
@ -137,6 +140,32 @@ sub largeRead {
|
||||
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
|
||||
newWorkingDir();
|
||||
mount();
|
||||
@ -149,6 +178,8 @@ symlink_test("/"); # absolute
|
||||
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("!§\$%&/()\\<>#+="); # special characters
|
||||
symlink_test("$plain/foo");
|
||||
writesDenied();
|
||||
|
||||
# Umount and delete files
|
||||
cleanup();
|
||||
|
Loading…
Reference in New Issue
Block a user