diff --git a/encfs/FileUtils.cpp b/encfs/FileUtils.cpp index ae0b3b3..e5490bc 100644 --- a/encfs/FileUtils.cpp +++ b/encfs/FileUtils.cpp @@ -35,6 +35,10 @@ #include #include #include +#ifdef __APPLE__ +#include +#include +#endif #include #include #include @@ -222,10 +226,19 @@ ConfigType readConfig_load(ConfigInfo *nm, const char *path, * Try to locate the config file * Tries the most recent format first, then looks for older versions */ -ConfigType readConfig(const string &rootDir, EncFSConfig *config) { +ConfigType readConfig(const string &rootDir, EncFSConfig *config, const string &cmdConfig) { ConfigInfo *nm = ConfigFileMapping; while (nm->fileName != nullptr) { - // allow environment variable to override default config path + // allow command line argument to override default config path + if (!cmdConfig.empty()) { + if (!fileExists(cmdConfig.c_str())) { + RLOG(ERROR) + << "fatal: config file specified on command line does not exist: " + << cmdConfig; + exit(1); + } + return readConfig_load(nm, cmdConfig.c_str(), config); + } // allow environment variable to override default config path if (nm->environmentOverride != nullptr) { char *envFile = getenv(nm->environmentOverride); if (envFile != nullptr) { @@ -433,14 +446,18 @@ bool readV4Config(const char *configFile, EncFSConfig *config, } bool saveConfig(ConfigType type, const string &rootDir, - const EncFSConfig *config) { + const EncFSConfig *config, const string &cmdConfig) { bool ok = false; ConfigInfo *nm = ConfigFileMapping; while (nm->fileName != nullptr) { if (nm->type == type && (nm->saveFunc != nullptr)) { string path = rootDir + nm->fileName; - if (nm->environmentOverride != nullptr) { + if (!cmdConfig.empty()) { + // use command line argument if specified + path.assign(cmdConfig); + } + else if (nm->environmentOverride != nullptr) { // use environment file if specified.. const char *envFile = getenv(nm->environmentOverride); if (envFile != nullptr) { @@ -1199,7 +1216,7 @@ RootPtr createV6Config(EncFS_Context *ctx, return rootInfo; } - if (!saveConfig(Config_V6, rootDir, config.get())) { + if (!saveConfig(Config_V6, rootDir, config.get(), opts->config)) { return rootInfo; } @@ -1559,7 +1576,7 @@ RootPtr initFS(EncFS_Context *ctx, const std::shared_ptr &opts) { RootPtr rootInfo; std::shared_ptr config(new EncFSConfig); - if (readConfig(opts->rootDir, config.get()) != Config_None) { + if (readConfig(opts->rootDir, config.get(), opts->config) != Config_None) { if (config->blockMACBytes == 0 && opts->requireMac) { cout << _( "The configuration disabled MAC, but you passed --require-macs\n"); @@ -1669,6 +1686,15 @@ RootPtr initFS(EncFS_Context *ctx, const std::shared_ptr &opts) { return rootInfo; } +void unmountFS(const char *mountPoint) { + // fuse_unmount succeeds and returns void + fuse_unmount(mountPoint, nullptr); +#ifdef __APPLE__ + // fuse_unmount does not work on Mac OS, see #428 + unmount(mountPoint, MNT_FORCE); +#endif +} + int remountFS(EncFS_Context *ctx) { VLOG(1) << "Attempting to reinitialize filesystem"; @@ -1689,13 +1715,8 @@ bool unmountFS(EncFS_Context *ctx) { ctx->setRoot(std::shared_ptr()); return false; } -// Time to unmount! -#if FUSE_USE_VERSION < 30 - fuse_unmount(ctx->opts->mountPoint.c_str(), nullptr); -#else - fuse_unmount(fuse_get_context()->fuse); -#endif - // fuse_unmount succeeds and returns void + // Time to unmount! + unmountFS(ctx->opts->mountPoint.c_str()); RLOG(INFO) << "Filesystem inactive, unmounted: " << ctx->opts->mountPoint; return true; } diff --git a/encfs/FileUtils.h b/encfs/FileUtils.h index 3e42092..ea9f50f 100644 --- a/encfs/FileUtils.h +++ b/encfs/FileUtils.h @@ -76,6 +76,7 @@ struct EncFS_Opts { bool idleTracking; // turn on idle monitoring of filesystem bool mountOnDemand; // mounting on-demand bool delayMount; // delay initial mount + bool unmount; // unmount bool checkKey; // check crypto key decoding bool forceDecode; // force decode on MAC block failures @@ -98,12 +99,14 @@ struct EncFS_Opts { bool requireMac; // Throw an error if MAC is disabled ConfigMode configMode; + std::string config; // path to configuration file (or empty) EncFS_Opts() { createIfNotFound = true; idleTracking = false; mountOnDemand = false; delayMount = false; + unmount = false; checkKey = true; forceDecode = false; useStdin = false; @@ -120,19 +123,21 @@ struct EncFS_Opts { /* Read existing config file. Looks for any supported configuration version. */ -ConfigType readConfig(const std::string &rootDir, EncFSConfig *config); +ConfigType readConfig(const std::string &rootDir, EncFSConfig *config, const std::string &cmdConfig); /* Save the configuration. Saves back as the same configuration type as was read from. */ bool saveConfig(ConfigType type, const std::string &rootdir, - const EncFSConfig *config); + const EncFSConfig *config, const std::string &cmdConfig); class EncFS_Context; RootPtr initFS(EncFS_Context *ctx, const std::shared_ptr &opts); +void unmountFS(const char *mountPoint); + RootPtr createV6Config(EncFS_Context *ctx, const std::shared_ptr &opts); diff --git a/encfs/encfs.pod b/encfs/encfs.pod index 2f51a76..5137be6 100644 --- a/encfs/encfs.pod +++ b/encfs/encfs.pod @@ -16,11 +16,11 @@ encfs - mounts or creates an encrypted virtual filesystem =head1 SYNOPSIS -B [B<--version>] [B<-v>|B<--verbose>] [B<-t>|B<--syslogtag>] +B [B<--version>] [B<-v>|B<--verbose>] [B<-c>|B<--config>] [B<-t>|B<--syslogtag>] [B<-s>] [B<-f>] [B<--annotate>] [B<--standard>] [B<--paranoia>] [B<--reverse>] [B<--reversewrite>] [B<--extpass=program>] [B<-S>|B<--stdinpass>] [B<--anykey>] [B<--forcedecode>] [B<-require-macs>] -[B<-i MINUTES>|B<--idle=MINUTES>] [B<-m>|B<--ondemand>] [B<--delaymount>] +[B<-i MINUTES>|B<--idle=MINUTES>] [B<-m>|B<--ondemand>] [B<--delaymount>] [B<-u>|B<--unmount>] [B<--public>] [B<--nocache>] [B<--no-default-flags>] [B<-o FUSE_OPTION>] [B<-d>|B<--fuse-debug>] [B<-H>|B<--fuse-help>] I I @@ -48,6 +48,10 @@ may be an increasing number of choices. Shows B version. Using B<--verbose> before B<--version> may display additional information. +=item B<-c>, B<--config> + +Causes B to use the supplied file as the configuration file. + =item B<-v>, B<--verbose> Causes B to enable logging of various debug channels within B. @@ -204,6 +208,10 @@ password. If this succeeds, then the filesystem becomes available again. Do not mount the filesystem when encfs starts; instead, delay mounting until first use. This option only makes sense with B<--ondemand>. +=item B<-u>, B<--unmount> + +Unmounts the specified I. + =item B<--public> Attempt to make encfs behave as a typical multi-user filesystem. By default, diff --git a/encfs/encfsctl.cpp b/encfs/encfsctl.cpp index 2af8c46..5602086 100644 --- a/encfs/encfsctl.cpp +++ b/encfs/encfsctl.cpp @@ -165,7 +165,7 @@ static int showInfo(int argc, char **argv) { if (!checkDir(rootDir)) return EXIT_FAILURE; std::shared_ptr config(new EncFSConfig); - ConfigType type = readConfig(rootDir, config.get()); + ConfigType type = readConfig(rootDir, config.get(), ""); // show information stored in config.. switch (type) { @@ -637,7 +637,7 @@ static int do_chpasswd(bool useStdin, bool annotate, bool checkOnly, int argc, if (!checkDir(rootDir)) return EXIT_FAILURE; EncFSConfig *config = new EncFSConfig; - ConfigType cfgType = readConfig(rootDir, config); + ConfigType cfgType = readConfig(rootDir, config, ""); if (cfgType == Config_None) { cout << _("Unable to load or parse config file\n"); @@ -698,7 +698,7 @@ static int do_chpasswd(bool useStdin, bool annotate, bool checkOnly, int argc, config->assignKeyData(keyBuf, encodedKeySize); delete[] keyBuf; - if (saveConfig(cfgType, rootDir, config)) { + if (saveConfig(cfgType, rootDir, config, "")) { // password modified -- changes volume key of filesystem.. cout << _("Volume Key successfully updated.\n"); result = EXIT_SUCCESS; diff --git a/encfs/main.cpp b/encfs/main.cpp index bec900e..1c61b7c 100644 --- a/encfs/main.cpp +++ b/encfs/main.cpp @@ -162,6 +162,10 @@ static void usage(const char *name) { "reverse encryption\n") << _(" --reversewrite\t\t" "reverse encryption with writes enabled\n") + << _(" -c, --config=path\t\t" + "specifies config file (overrides ENV variable)\n") + << _(" -u, --unmount\t\t" + "unmounts specified mountPoint\n") // xgroup(usage) << _(" --extpass=program\tUse external program for password prompt\n" @@ -217,6 +221,7 @@ static bool processArgs(int argc, char *argv[], out->opts->annotate = false; out->opts->reverseEncryption = false; out->opts->requireMac = false; + out->opts->unmount = false; bool useDefaultFlags = true; @@ -255,6 +260,8 @@ static bool processArgs(int argc, char *argv[], {"standard", 0, nullptr, '1'}, // standard configuration {"paranoia", 0, nullptr, '2'}, // standard configuration {"require-macs", 0, nullptr, LONG_OPT_REQUIRE_MAC}, // require MACs + {"config", 1, nullptr, 'c'}, // command-line-supplied config location + {"unmount", 1, nullptr, 'u'}, // unmount {nullptr, 0, nullptr, 0}}; while (true) { @@ -269,8 +276,10 @@ static bool processArgs(int argc, char *argv[], // 'S' : password from stdin // 'o' : arguments meant for fuse // 't' : syslog tag + // 'c' : configuration file + // 'u' : unmount int res = - getopt_long(argc, argv, "HsSfvdmi:o:t:", long_options, &option_index); + getopt_long(argc, argv, "HsSfvdmi:o:t:c:u", long_options, &option_index); if (res == -1) { break; @@ -298,6 +307,14 @@ static bool processArgs(int argc, char *argv[], case LONG_OPT_REQUIRE_MAC: out->opts->requireMac = true; break; + case 'c': + /* Take config file path from command + * line instead of ENV variable */ + out->opts->config.assign(optarg); + break; + case 'u': + out->opts->unmount = true; + break; case 'f': out->isDaemon = false; // this option was added in fuse 2.x @@ -376,7 +393,7 @@ static bool processArgs(int argc, char *argv[], break; case 'P': if (geteuid() != 0) { - RLOG(WARNING) << "option '--public' ignored for non-root user"; + cerr << "option '--public' ignored for non-root user"; } else { out->opts->ownerCreate = true; // add 'allow_other' option @@ -407,7 +424,7 @@ static bool processArgs(int argc, char *argv[], // missing parameter for option.. break; default: - RLOG(WARNING) << "getopt error: " << res; + cerr << "getopt error: " << res; break; } } @@ -416,6 +433,17 @@ static bool processArgs(int argc, char *argv[], PUSHARG("-s"); } + // for --unmount, we should have exactly 1 argument - the mount point + if (out->opts->unmount) { + if (optind + 1 == argc) { + out->opts->mountPoint = slashTerminate(argv[optind++]); + return true; + } + // no mount point specified + cerr << _("Expecting one argument, aborting.") << endl; + return false; + } + // we should have at least 2 arguments left over - the source directory and // the mount point. if (optind + 2 <= argc) { @@ -425,7 +453,7 @@ static bool processArgs(int argc, char *argv[], out->opts->mountPoint = slashTerminate(argv[optind++]); } else { // no mount point specified - cerr << _("Missing one or more arguments, aborting."); + cerr << _("Missing one or more arguments, aborting.") << endl; return false; } @@ -575,6 +603,13 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } + // Let's unmount if requested + if (encfsArgs->opts->unmount) { + unmountFS(encfsArgs->opts->mountPoint.c_str()); + cout << "Filesystem unmounting: " << encfsArgs->opts->mountPoint << endl; + return 0; + } + encfs::initLogging(encfsArgs->isVerbose, encfsArgs->isDaemon); ELPP_INITIALIZE_SYSLOG(encfsArgs->syslogTag.c_str(), LOG_PID, LOG_USER); diff --git a/integration/common.pl b/integration/common.pl index 7d2cca9..3d18256 100755 --- a/integration/common.pl +++ b/integration/common.pl @@ -2,13 +2,7 @@ # works on Linux AND OSX sub portable_unmount { my $crypt = shift; - my $fusermount = qx(which fusermount); - chomp($fusermount); - if(-f $fusermount) { - qx($fusermount -u "$crypt"); - } else { - qx(umount "$crypt"); - } + qx(./build/encfs -u "$crypt" >/dev/null); } # Helper function diff --git a/integration/normal.t.pl b/integration/normal.t.pl index e239f64..7cbe932 100755 --- a/integration/normal.t.pl +++ b/integration/normal.t.pl @@ -441,8 +441,9 @@ sub create_unmount_remount # Unmount portable_unmount($mnt); - # Mount again - system("./build/encfs --extpass=\"echo test\" $crypt $mnt 2>&1"); + # Mount again, testing -c as the same time + rename("$crypt/.encfs6.xml", "$crypt/.encfs6_moved.xml"); + system("./build/encfs -c $crypt/.encfs6_moved.xml --extpass=\"echo test\" $crypt $mnt 2>&1"); ok( $? == 0, "encfs command returns 0") || return; # Check if content is still there