"Fossies" - the Fresh Open Source Software Archive

Member "encfs-1.9.5/encfs/main.cpp" (27 Apr 2018, 29226 Bytes) of package /linux/misc/encfs-1.9.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "main.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.9.4_vs_1.9.5.

    1 /*****************************************************************************
    2  * Author:   Valient Gough <vgough@pobox.com>
    3  *
    4  *****************************************************************************
    5  * Copyright (c) 2003-2004, Valient Gough
    6  *
    7  * This library is free software; you can distribute it and/or modify it under
    8  * the terms of the GNU General Public License (GPL), as published by the Free
    9  * Software Foundation; either version 2 of the License, or (at your option)
   10  * any later version.
   11  *
   12  * This library is distributed in the hope that it will be useful, but WITHOUT
   13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GPL in the file COPYING for more
   15  * details.
   16  *
   17  */
   18 
   19 #include <cerrno>
   20 #include <cstdio>
   21 #include <cstdlib>
   22 #include <cstring>
   23 #include <ctime>
   24 #include <exception>
   25 #include <getopt.h>
   26 #include <iostream>
   27 #include <memory>
   28 #include <pthread.h>
   29 #include <sstream>
   30 #include <string>
   31 #ifdef __CYGWIN__
   32 #include <sys/cygwin.h>
   33 #endif
   34 #include <sys/stat.h>
   35 #include <sys/time.h>
   36 #include <unistd.h>
   37 
   38 #include "Context.h"
   39 #include "Error.h"
   40 #include "FileUtils.h"
   41 #include "MemoryPool.h"
   42 #include "autosprintf.h"
   43 #include "config.h"
   44 #include "encfs.h"
   45 #include "fuse.h"
   46 #include "i18n.h"
   47 #include "openssl.h"
   48 
   49 /* Arbitrary identifiers for long options that do
   50  * not have a short version */
   51 #define LONG_OPT_ANNOTATE 513
   52 #define LONG_OPT_NOCACHE 514
   53 #define LONG_OPT_NODATACACHE 515
   54 #define LONG_OPT_NOATTRCACHE 516
   55 #define LONG_OPT_REQUIRE_MAC 517
   56 #define LONG_OPT_INSECURE 518
   57 
   58 using namespace std;
   59 using namespace encfs;
   60 using gnu::autosprintf;
   61 
   62 namespace encfs {
   63 
   64 class DirNode;
   65 
   66 // Maximum number of arguments that we're going to pass on to fuse.  Doesn't
   67 // affect how many arguments we can handle, just how many we can pass on..
   68 const int MaxFuseArgs = 32;
   69 /**
   70  * EncFS_Args stores the parsed command-line arguments
   71  *
   72  * See also: struct EncFS_Opts (FileUtils.h), stores internal settings that are
   73  * derived from the arguments
   74  */
   75 struct EncFS_Args {
   76   bool isDaemon;    // true == spawn in background, log to syslog
   77   bool isThreaded;  // true == threaded
   78   bool isVerbose;   // false == only enable warning/error messages
   79   int idleTimeout;  // 0 == idle time in minutes to trigger unmount
   80   const char *fuseArgv[MaxFuseArgs];
   81   int fuseArgc;
   82   std::string syslogTag;  // syslog tag to use when logging using syslog
   83 
   84   std::shared_ptr<EncFS_Opts> opts;
   85 
   86   // for debugging
   87   // In case someone sends me a log dump, I want to know how what options are
   88   // in effect.  Not internationalized, since it is something that is mostly
   89   // useful for me!
   90   string toString() {
   91     ostringstream ss;
   92     ss << (isDaemon ? "(daemon) " : "(fg) ");
   93     ss << (isThreaded ? "(threaded) " : "(UP) ");
   94     if (idleTimeout > 0) {
   95       ss << "(timeout " << idleTimeout << ") ";
   96     }
   97     if (opts->checkKey) {
   98       ss << "(keyCheck) ";
   99     }
  100     if (opts->forceDecode) {
  101       ss << "(forceDecode) ";
  102     }
  103     if (opts->ownerCreate) {
  104       ss << "(ownerCreate) ";
  105     }
  106     if (opts->useStdin) {
  107       ss << "(useStdin) ";
  108     }
  109     if (opts->annotate) {
  110       ss << "(annotate) ";
  111     }
  112     if (opts->reverseEncryption) {
  113       ss << "(reverseEncryption) ";
  114     }
  115     if (opts->mountOnDemand) {
  116       ss << "(mountOnDemand) ";
  117     }
  118     if (opts->delayMount) {
  119       ss << "(delayMount) ";
  120     }
  121     for (int i = 0; i < fuseArgc; ++i) {
  122       ss << fuseArgv[i] << ' ';
  123     }
  124     return ss.str();
  125   }
  126 
  127   EncFS_Args() : opts(new EncFS_Opts()) {}
  128 };
  129 
  130 static int oldStderr = STDERR_FILENO;
  131 
  132 }  // namespace encfs
  133 
  134 static void usage(const char *name) {
  135   // xgroup(usage)
  136   cerr << autosprintf(_("Build: encfs version %s"), VERSION) << "\n\n"
  137        // xgroup(usage)
  138        << autosprintf(
  139               _("Usage: %s [options] rootDir mountPoint [-- [FUSE Mount "
  140                 "Options]]"),
  141               name)
  142        << "\n\n"
  143        // xgroup(usage)
  144        << _("Common Options:\n"
  145             "  -H\t\t\t"
  146             "show optional FUSE Mount Options\n"
  147             "  -s\t\t\t"
  148             "disable multithreaded operation\n"
  149             "  -f\t\t\t"
  150             "run in foreground (don't spawn daemon).\n"
  151             "\t\t\tError messages will be sent to stderr\n"
  152             "\t\t\tinstead of syslog.\n")
  153 
  154        // xgroup(usage)
  155        << _("  -v, --verbose\t\t"
  156             "verbose: output encfs debug messages\n"
  157             "  -i, --idle=MINUTES\t"
  158             "Auto unmount after period of inactivity\n"
  159             "  --anykey\t\t"
  160             "Do not verify correct key is being used\n"
  161             "  --forcedecode\t\t"
  162             "decode data even if an error is detected\n"
  163             "\t\t\t(for filesystems using MAC block headers)\n")
  164        << _("  --public\t\t"
  165             "act as a typical multi-user filesystem\n"
  166             "\t\t\t(encfs must be run as root)\n")
  167        << _("  --reverse\t\t"
  168             "reverse encryption\n")
  169        << _("  --reversewrite\t\t"
  170             "reverse encryption with writes enabled\n")
  171        << _("  -c, --config=path\t\t"
  172             "specifies config file (overrides ENV variable)\n")
  173        << _("  -u, --unmount\t\t"
  174             "unmounts specified mountPoint\n")
  175 
  176        // xgroup(usage)
  177        << _("  --extpass=program\tUse external program for password prompt\n"
  178             "\n"
  179             "Example, to mount at ~/crypt with raw storage in ~/.crypt :\n"
  180             "    encfs ~/.crypt ~/crypt\n"
  181             "\n")
  182        // xgroup(usage)
  183        << _("For more information, see the man page encfs(1)") << "\n"
  184        << endl;
  185 }
  186 
  187 static void FuseUsage() {
  188   // xgroup(usage)
  189   cerr << _("encfs [options] rootDir mountPoint -- [FUSE Mount Options]\n"
  190             "valid FUSE Mount Options follow:\n")
  191        << endl;
  192 
  193   int argc = 2;
  194   const char *argv[] = {"...", "-h"};
  195   fuse_main(argc, const_cast<char **>(argv), (fuse_operations *)nullptr,
  196             nullptr);
  197 }
  198 
  199 #define PUSHARG(ARG)                        \
  200   do {                                      \
  201     rAssert(out->fuseArgc < MaxFuseArgs);   \
  202     out->fuseArgv[out->fuseArgc++] = (ARG); \
  203   } while (false)
  204 
  205 static string slashTerminate(const string &src) {
  206   string result = src;
  207   if (result[result.length() - 1] != '/') {
  208     result.append("/");
  209   }
  210   return result;
  211 }
  212 
  213 static bool processArgs(int argc, char *argv[],
  214                         const std::shared_ptr<EncFS_Args> &out) {
  215   // set defaults
  216   out->isDaemon = true;
  217   out->isThreaded = true;
  218   out->isVerbose = false;
  219   out->idleTimeout = 0;
  220   out->fuseArgc = 0;
  221   out->syslogTag = "encfs";
  222   out->opts->idleTracking = false;
  223   out->opts->checkKey = true;
  224   out->opts->forceDecode = false;
  225   out->opts->ownerCreate = false;
  226   out->opts->useStdin = false;
  227   out->opts->annotate = false;
  228   out->opts->reverseEncryption = false;
  229   out->opts->requireMac = false;
  230   out->opts->insecure = false;
  231   out->opts->unmount = false;
  232 
  233   bool useDefaultFlags = true;
  234 
  235   // pass executable name through
  236   out->fuseArgv[0] = lastPathElement(argv[0]);
  237   ++out->fuseArgc;
  238 
  239   // leave a space for mount point, as FUSE expects the mount point before
  240   // any flags
  241   out->fuseArgv[1] = nullptr;
  242   ++out->fuseArgc;
  243 
  244   // TODO: can flags be internationalized?
  245   static struct option long_options[] = {
  246       {"fuse-debug", 0, nullptr, 'd'},   // Fuse debug mode
  247       {"forcedecode", 0, nullptr, 'D'},  // force decode
  248       // {"foreground", 0, 0, 'f'}, // foreground mode (no daemon)
  249       {"fuse-help", 0, nullptr, 'H'},         // fuse_mount usage
  250       {"idle", 1, nullptr, 'i'},              // idle timeout
  251       {"anykey", 0, nullptr, 'k'},            // skip key checks
  252       {"no-default-flags", 0, nullptr, 'N'},  // don't use default fuse flags
  253       {"ondemand", 0, nullptr, 'm'},          // mount on-demand
  254       {"delaymount", 0, nullptr, 'M'},        // delay initial mount until use
  255       {"public", 0, nullptr, 'P'},            // public mode
  256       {"extpass", 1, nullptr, 'p'},           // external password program
  257       // {"single-thread", 0, 0, 's'},  // single-threaded mode
  258       {"stdinpass", 0, nullptr, 'S'},  // read password from stdin
  259       {"syslogtag", 1, nullptr, 't'},  // syslog tag
  260       {"annotate", 0, nullptr,
  261        LONG_OPT_ANNOTATE},  // Print annotation lines to stderr
  262       {"nocache", 0, nullptr, LONG_OPT_NOCACHE},         // disable all caching
  263       {"nodatacache", 0, nullptr, LONG_OPT_NODATACACHE}, // disable data caching
  264       {"noattrcache", 0, nullptr, LONG_OPT_NOATTRCACHE}, // disable attr caching
  265       {"verbose", 0, nullptr, 'v'},               // verbose mode
  266       {"version", 0, nullptr, 'V'},               // version
  267       {"reverse", 0, nullptr, 'r'},               // reverse encryption
  268       {"reversewrite", 0, nullptr, 'R'},          // reverse encryption with write enabled
  269       {"standard", 0, nullptr, '1'},              // standard configuration
  270       {"paranoia", 0, nullptr, '2'},              // standard configuration
  271       {"require-macs", 0, nullptr, LONG_OPT_REQUIRE_MAC},  // require MACs
  272       {"insecure", 0, nullptr, LONG_OPT_INSECURE},// allows to use null data encryption
  273       {"config", 1, nullptr, 'c'},                // command-line-supplied config location
  274       {"unmount", 1, nullptr, 'u'},               // unmount
  275       {nullptr, 0, nullptr, 0}};
  276 
  277   while (true) {
  278     int option_index = 0;
  279 
  280     // 's' : single-threaded mode
  281     // 'f' : foreground mode
  282     // 'v' : verbose mode (same as --verbose)
  283     // 'd' : fuse debug mode (same as --fusedebug)
  284     // 'i' : idle-timeout, takes argument
  285     // 'm' : mount-on-demand
  286     // 'S' : password from stdin
  287     // 'o' : arguments meant for fuse
  288     // 't' : syslog tag
  289     // 'c' : configuration file
  290     // 'u' : unmount
  291     int res =
  292         getopt_long(argc, argv, "HsSfvdmi:o:t:c:u", long_options, &option_index);
  293 
  294     if (res == -1) {
  295       break;
  296     }
  297 
  298     switch (res) {
  299       case '1':
  300         out->opts->configMode = Config_Standard;
  301         break;
  302       case '2':
  303         out->opts->configMode = Config_Paranoia;
  304         break;
  305       case 's':
  306         out->isThreaded = false;
  307         break;
  308       case 'S':
  309         out->opts->useStdin = true;
  310         break;
  311       case 't':
  312         out->syslogTag = optarg;
  313         break;
  314       case LONG_OPT_ANNOTATE:
  315         out->opts->annotate = true;
  316         break;
  317       case LONG_OPT_REQUIRE_MAC:
  318         out->opts->requireMac = true;
  319         break;
  320       case LONG_OPT_INSECURE:
  321         out->opts->insecure = true;
  322         break;
  323       case 'c':
  324         /* Take config file path from command 
  325          * line instead of ENV variable */
  326         out->opts->config.assign(optarg);
  327         break;
  328       case 'u':
  329         //we want to log to console, not to syslog, in case of error
  330         out->isDaemon = false;
  331         out->opts->unmount = true;
  332         break;
  333       case 'f':
  334         out->isDaemon = false;
  335         // this option was added in fuse 2.x
  336         PUSHARG("-f");
  337         break;
  338       case 'v':
  339         out->isVerbose = true;
  340         break;
  341       case 'd':
  342         PUSHARG("-d");
  343         break;
  344       case 'i':
  345         out->idleTimeout = strtol(optarg, (char **)nullptr, 10);
  346         out->opts->idleTracking = true;
  347         break;
  348       case 'k':
  349         out->opts->checkKey = false;
  350         break;
  351       case 'D':
  352         out->opts->forceDecode = true;
  353         break;
  354       case 'r':
  355         out->opts->reverseEncryption = true;
  356         /* Reverse encryption does not support writing unless uniqueIV
  357          * is disabled (expert mode) */
  358         out->opts->readOnly = true;
  359         /* By default, the kernel caches file metadata for one second.
  360          * This is fine for EncFS' normal mode, but for --reverse, this
  361          * means that the encrypted view will be up to one second out of
  362          * date.
  363          * Quoting Goswin von Brederlow:
  364          * "Caching only works correctly if you implement a disk based
  365          * filesystem, one where only the fuse process can alter
  366          * metadata and all access goes only through fuse. Any overlay
  367          * filesystem where something can change the underlying
  368          * filesystem without going through fuse can run into
  369          * inconsistencies."
  370          * However, disabling the caches causes a factor 3
  371          * slowdown. If you are concerned about inconsistencies,
  372          * please use --nocache. */
  373         break;
  374       case 'R':
  375         out->opts->reverseEncryption = true;
  376          /* At least this is what the user wants, we will see later
  377             if it is possible */
  378         out->opts->readOnly = false;
  379         break;
  380       case LONG_OPT_NOCACHE:
  381         /* Disable EncFS block cache
  382          * Causes reverse grow tests to fail because short reads
  383          * are returned */
  384         out->opts->noCache = true;
  385         /* Disable kernel stat() cache
  386          * Causes reverse grow tests to fail because stale stat() data
  387          * is returned */
  388         PUSHARG("-oattr_timeout=0");
  389         /* Disable kernel dentry cache
  390          * Fallout unknown, disabling for safety */
  391         PUSHARG("-oentry_timeout=0");
  392 #ifdef __CYGWIN__
  393         // Should be enforced due to attr_timeout=0, but does not seem to work correctly
  394         // https://github.com/billziss-gh/winfsp/issues/155
  395         PUSHARG("-oFileInfoTimeout=0");
  396 #endif
  397         break;
  398       case LONG_OPT_NODATACACHE:
  399         out->opts->noCache = true;
  400         break;
  401       case LONG_OPT_NOATTRCACHE:
  402         PUSHARG("-oattr_timeout=0");
  403         PUSHARG("-oentry_timeout=0");
  404 #ifdef __CYGWIN__
  405         PUSHARG("-oFileInfoTimeout=0");
  406 #endif
  407         break;
  408       case 'm':
  409         out->opts->mountOnDemand = true;
  410         break;
  411       case 'M':
  412         out->opts->delayMount = true;
  413         break;
  414       case 'N':
  415         useDefaultFlags = false;
  416         break;
  417       case 'o':
  418         PUSHARG("-o");
  419         PUSHARG(optarg);
  420         break;
  421       case 'p':
  422         out->opts->passwordProgram.assign(optarg);
  423         break;
  424       case 'P':
  425         if (geteuid() != 0) {
  426           cerr << "option '--public' ignored for non-root user";
  427         } else {
  428           out->opts->ownerCreate = true;
  429           // add 'allow_other' option
  430           // add 'default_permissions' option (default)
  431           PUSHARG("-o");
  432           PUSHARG("allow_other");
  433         }
  434         break;
  435       case 'V':
  436         // xgroup(usage)
  437         cerr << autosprintf(_("encfs version %s"), VERSION) << endl;
  438 #if defined(HAVE_XATTR)
  439         // "--verbose" has to be passed before "--version" for this to work.
  440         if (out->isVerbose) {
  441           cerr << "Compiled with : HAVE_XATTR" << endl;
  442         }
  443 #endif
  444         exit(EXIT_SUCCESS);
  445         break;
  446       case 'H':
  447         FuseUsage();
  448         exit(EXIT_SUCCESS);
  449         break;
  450       case '?':
  451         // invalid options..
  452         break;
  453       case ':':
  454         // missing parameter for option..
  455         break;
  456       default:
  457         cerr << "getopt error: " << res;
  458         break;
  459     }
  460   }
  461 
  462   if (!out->isThreaded) {
  463     PUSHARG("-s");
  464   }
  465 
  466   // for --unmount, we should have exactly 1 argument - the mount point
  467   if (out->opts->unmount) {
  468     if (optind + 1 == argc) {
  469       // unmountPoint is kept as given by the user : in Cygwin, it is used
  470       // by pkill to terminate the correct process. We can't then use a
  471       // Linux-converted Windows-style mountPoint to unmount...
  472       out->opts->unmountPoint = string(argv[optind++]);
  473       return true;
  474     }
  475     // no mount point specified
  476     cerr << _("Expecting one argument, aborting.") << endl;
  477     return false;
  478   }
  479 
  480   // we should have at least 2 arguments left over - the source directory and
  481   // the mount point.
  482   if (optind + 2 <= argc) {
  483     // both rootDir and mountPoint are assumed to be slash terminated in the
  484     // rest of the code.
  485     out->opts->rootDir = slashTerminate(argv[optind++]);
  486     out->opts->unmountPoint = string(argv[optind++]);
  487     out->opts->mountPoint = slashTerminate(out->opts->unmountPoint);
  488   } else {
  489     // no mount point specified
  490     cerr << _("Missing one or more arguments, aborting.") << endl;
  491     return false;
  492   }
  493 
  494   // If there are still extra unparsed arguments, pass them onto FUSE..
  495   if (optind < argc) {
  496     rAssert(out->fuseArgc < MaxFuseArgs);
  497 
  498     while (optind < argc) {
  499       rAssert(out->fuseArgc < MaxFuseArgs);
  500       out->fuseArgv[out->fuseArgc++] = argv[optind];
  501       ++optind;
  502     }
  503   }
  504 
  505   // Add default flags unless --no-default-flags was passed
  506   if (useDefaultFlags) {
  507 
  508     // Expose the underlying stable inode number
  509     PUSHARG("-o");
  510     PUSHARG("use_ino");
  511 
  512     // "default_permissions" comes with a performance cost, and only makes
  513     // sense if "allow_other"" is used.
  514     // But it works around the issues "open_readonly_workaround" causes,
  515     // so enable it unconditionally.
  516     // See https://github.com/vgough/encfs/issues/181 and
  517     // https://github.com/vgough/encfs/issues/112 for more info.
  518     PUSHARG("-o");
  519     PUSHARG("default_permissions");
  520 
  521 #if defined(__APPLE__)
  522     // With OSXFuse, the 'local' flag selects a local filesystem mount icon in
  523     // Finder.
  524     PUSHARG("-o");
  525     PUSHARG("local");
  526 #endif
  527   }
  528 
  529 #ifdef __CYGWIN__
  530   // Windows users may use Windows paths
  531   // https://cygwin.com/cygwin-api/cygwin-functions.html
  532   out->opts->mountPoint = string((char *)cygwin_create_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, out->opts->mountPoint.c_str()));
  533   out->opts->rootDir = string((char *)cygwin_create_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, out->opts->rootDir.c_str()));
  534 #endif
  535 
  536   // sanity check
  537   if (out->isDaemon && (!isAbsolutePath(out->opts->mountPoint.c_str()) ||
  538                         !isAbsolutePath(out->opts->rootDir.c_str()))) {
  539     cerr <<
  540         // xgroup(usage)
  541         _("When specifying daemon mode, you must use absolute paths "
  542           "(beginning with '/')")
  543          << endl;
  544     return false;
  545   }
  546 
  547   // the raw directory may not be a subdirectory of the mount point.
  548   {
  549     string testMountPoint = out->opts->mountPoint;
  550     string testRootDir = out->opts->rootDir.substr(0, testMountPoint.length());
  551 
  552     if (testMountPoint == testRootDir) {
  553       cerr <<
  554           // xgroup(usage)
  555           _("The raw directory may not be a subdirectory of the "
  556             "mount point.")
  557            << endl;
  558       return false;
  559     }
  560   }
  561 
  562   if (out->opts->delayMount && !out->opts->mountOnDemand) {
  563     cerr <<
  564         // xgroup(usage)
  565         _("You must use mount-on-demand with delay-mount") << endl;
  566     return false;
  567   }
  568 
  569   if (out->opts->mountOnDemand && out->opts->passwordProgram.empty()) {
  570     cerr <<
  571         // xgroup(usage)
  572         _("Must set password program when using mount-on-demand") << endl;
  573     return false;
  574   }
  575 
  576   // check that the directories exist, or that we can create them..
  577   if (!isDirectory(out->opts->rootDir.c_str()) &&
  578       !userAllowMkdir(out->opts->annotate ? 1 : 0, out->opts->rootDir.c_str(),
  579                       0700)) {
  580     cerr << _("Unable to locate root directory, aborting.") << endl;
  581     return false;
  582   }
  583 #ifdef __CYGWIN__
  584   if (isDirectory(out->opts->mountPoint.c_str())) {
  585     cerr << _("Mount point must not exist before mouting, aborting.") << endl;
  586     return false;
  587   }
  588   if ((strncmp(out->opts->mountPoint.c_str(), "/cygdrive/", 10) != 0) ||
  589       (out->opts->mountPoint.length() != 12)) {
  590     cerr << _("A drive is prefered for mouting, ")
  591          << _("so a path like X: (or /cygdrive/x) should rather be used. ")
  592          << _("Mounting anyway.")  << endl;
  593   }
  594 #else
  595   if (!isDirectory(out->opts->mountPoint.c_str()) &&
  596       !userAllowMkdir(out->opts->annotate ? 2 : 0,
  597                       out->opts->mountPoint.c_str(), 0700)) {
  598     cerr << _("Unable to locate mount point, aborting.") << endl;
  599     return false;
  600   }
  601 #endif
  602 
  603   // fill in mount path for fuse
  604   out->fuseArgv[1] = out->opts->mountPoint.c_str();
  605 #ifdef __CYGWIN__
  606   if ((strncmp(out->opts->mountPoint.c_str(), "/cygdrive/", 10) == 0) &&
  607       (out->opts->mountPoint.length() == 12)) {
  608     out->opts->cygDrive = out->opts->mountPoint.substr(10,1).append(":");
  609     out->fuseArgv[1] = out->opts->cygDrive.c_str();
  610   }
  611 #endif
  612 
  613   return true;
  614 }
  615 
  616 static void *idleMonitor(void *);
  617 
  618 void *encfs_init(fuse_conn_info *conn) {
  619   auto *ctx = (EncFS_Context *)fuse_get_context()->private_data;
  620 
  621   // set fuse connection options
  622   conn->async_read = 1u;
  623 
  624 #ifdef __CYGWIN__
  625   // WinFsp needs this to partially handle read-only FS
  626   // See https://github.com/billziss-gh/winfsp/issues/157 for details
  627   if (ctx->opts->readOnly) {
  628     conn->want |= (conn->capable & FSP_FUSE_CAP_READ_ONLY);
  629   }
  630 #endif
  631 
  632   // if an idle timeout is specified, then setup a thread to monitor the
  633   // filesystem.
  634   if (ctx->args->idleTimeout > 0) {
  635     VLOG(1) << "starting idle monitoring thread";
  636     ctx->running = true;
  637 
  638     int res =
  639         pthread_create(&ctx->monitorThread, nullptr, idleMonitor, (void *)ctx);
  640     if (res != 0) {
  641       RLOG(ERROR) << "error starting idle monitor thread, "
  642                      "res = "
  643                   << res << ", " << strerror(res);
  644     }
  645   }
  646 
  647   if (ctx->args->isDaemon && oldStderr >= 0) {
  648     VLOG(1) << "Closing stderr";
  649     close(oldStderr);
  650     oldStderr = -1;
  651   }
  652 
  653   return (void *)ctx;
  654 }
  655 
  656 int main(int argc, char *argv[]) {
  657 #if defined(ENABLE_NLS) && defined(LOCALEDIR)
  658   setlocale(LC_ALL, "");
  659   bindtextdomain(PACKAGE, LOCALEDIR);
  660   textdomain(PACKAGE);
  661 #endif
  662 
  663   // anything that comes from the user should be considered tainted until
  664   // we've processed it and only allowed through what we support.
  665   std::shared_ptr<EncFS_Args> encfsArgs(new EncFS_Args);
  666   for (int i = 0; i < MaxFuseArgs; ++i) {
  667     encfsArgs->fuseArgv[i] = nullptr;  // libfuse expects null args..
  668   }
  669 
  670   if (argc == 1 || !processArgs(argc, argv, encfsArgs)) {
  671     usage(argv[0]);
  672     return EXIT_FAILURE;
  673   }
  674 
  675   encfs::initLogging(encfsArgs->isVerbose, encfsArgs->isDaemon);
  676   ELPP_INITIALIZE_SYSLOG(encfsArgs->syslogTag.c_str(), LOG_PID, LOG_USER);
  677 
  678   // Let's unmount if requested
  679   if (encfsArgs->opts->unmount) {
  680     // We use cout here to avoid logging to stderr (and to mess-up tests output)
  681     cout << "Filesystem unmounting: " << encfsArgs->opts->unmountPoint << endl;
  682     unmountFS(encfsArgs->opts->unmountPoint.c_str());
  683     return 0;
  684   }
  685 
  686   VLOG(1) << "Root directory: " << encfsArgs->opts->rootDir;
  687   VLOG(1) << "Fuse arguments: " << encfsArgs->toString();
  688 
  689   fuse_operations encfs_oper;
  690   // in case this code is compiled against a newer FUSE library and new
  691   // members have been added to fuse_operations, make sure they get set to
  692   // 0..
  693   memset(&encfs_oper, 0, sizeof(fuse_operations));
  694 
  695   encfs_oper.getattr = encfs_getattr;
  696   encfs_oper.readlink = encfs_readlink;
  697   encfs_oper.readdir = encfs_readdir;
  698   encfs_oper.mknod = encfs_mknod;
  699   encfs_oper.mkdir = encfs_mkdir;
  700   encfs_oper.unlink = encfs_unlink;
  701   encfs_oper.rmdir = encfs_rmdir;
  702   encfs_oper.symlink = encfs_symlink;
  703   encfs_oper.rename = encfs_rename;
  704   encfs_oper.link = encfs_link;
  705   encfs_oper.chmod = encfs_chmod;
  706   encfs_oper.chown = encfs_chown;
  707   encfs_oper.truncate = encfs_truncate;
  708   encfs_oper.utime = encfs_utime;  // deprecated for utimens
  709   encfs_oper.open = encfs_open;
  710   encfs_oper.read = encfs_read;
  711   encfs_oper.write = encfs_write;
  712   encfs_oper.statfs = encfs_statfs;
  713   encfs_oper.flush = encfs_flush;
  714   encfs_oper.release = encfs_release;
  715   encfs_oper.fsync = encfs_fsync;
  716 #ifdef HAVE_XATTR
  717   encfs_oper.setxattr = encfs_setxattr;
  718   encfs_oper.getxattr = encfs_getxattr;
  719   encfs_oper.listxattr = encfs_listxattr;
  720   encfs_oper.removexattr = encfs_removexattr;
  721 #endif  // HAVE_XATTR
  722   // encfs_oper.opendir = encfs_opendir;
  723   // encfs_oper.readdir = encfs_readdir;
  724   // encfs_oper.releasedir = encfs_releasedir;
  725   // encfs_oper.fsyncdir = encfs_fsyncdir;
  726   encfs_oper.init = encfs_init;
  727   // encfs_oper.access = encfs_access;
  728   encfs_oper.create = encfs_create;
  729   encfs_oper.ftruncate = encfs_ftruncate;
  730   encfs_oper.fgetattr = encfs_fgetattr;
  731   // encfs_oper.lock = encfs_lock;
  732   encfs_oper.utimens = encfs_utimens;
  733   // encfs_oper.bmap = encfs_bmap;
  734 
  735   openssl_init(encfsArgs->isThreaded);
  736 
  737   // context is not a smart pointer because it will live for the life of
  738   // the filesystem.
  739   auto ctx = std::make_shared<EncFS_Context>();
  740   ctx->publicFilesystem = encfsArgs->opts->ownerCreate;
  741   RootPtr rootInfo = initFS(ctx.get(), encfsArgs->opts);
  742 
  743   int returnCode = EXIT_FAILURE;
  744 
  745   if (rootInfo) {
  746     // turn off delayMount, as our prior call to initFS has already
  747     // respected any delay, and we want future calls to actually
  748     // mount.
  749     encfsArgs->opts->delayMount = false;
  750 
  751     // set the globally visible root directory node
  752     ctx->setRoot(rootInfo->root);
  753     ctx->args = encfsArgs;
  754     ctx->opts = encfsArgs->opts;
  755 
  756     if (!encfsArgs->isThreaded && encfsArgs->idleTimeout > 0) {
  757       // xgroup(usage)
  758       cerr << _("Note: requested single-threaded mode, but an idle\n"
  759                 "timeout was specified.  The filesystem will operate\n"
  760                 "single-threaded, but threads will still be used to\n"
  761                 "implement idle checking.")
  762            << endl;
  763     }
  764 
  765     // reset umask now, since we don't want it to interfere with the
  766     // pass-thru calls..
  767     umask(0);
  768 
  769     if (encfsArgs->isDaemon) {
  770       // keep around a pointer just in case we end up needing it to
  771       // report a fatal condition later (fuse_main exits unexpectedly)...
  772       oldStderr = dup(STDERR_FILENO);
  773     }
  774 
  775     try {
  776       time_t startTime, endTime;
  777 
  778       if (encfsArgs->opts->annotate) {
  779         cerr << "$STATUS$ fuse_main_start" << endl;
  780       }
  781 
  782       // FIXME: workaround for fuse_main returning an error on normal
  783       // exit.  Only print information if fuse_main returned
  784       // immediately..
  785       time(&startTime);
  786 
  787       // fuse_main returns an error code in newer versions of fuse..
  788       int res = fuse_main(encfsArgs->fuseArgc,
  789                           const_cast<char **>(encfsArgs->fuseArgv), &encfs_oper,
  790                           (void *)ctx.get());
  791 
  792       time(&endTime);
  793 
  794       if (encfsArgs->opts->annotate) {
  795         cerr << "$STATUS$ fuse_main_end" << endl;
  796       }
  797 
  798       if (res == 0) {
  799         returnCode = EXIT_SUCCESS;
  800       }
  801 
  802       if (res != 0 && encfsArgs->isDaemon && (oldStderr >= 0) &&
  803           (endTime - startTime <= 1)) {
  804         // the users will not have seen any message from fuse, so say a
  805         // few words in libfuse's memory..
  806         FILE *out = fdopen(oldStderr, "a");
  807         // xgroup(usage)
  808         fputs(_("fuse failed.  Common problems:\n"
  809                 " - fuse kernel module not installed (modprobe fuse)\n"
  810                 " - invalid options -- see usage message\n"),
  811               out);
  812         fclose(out);
  813       }
  814     } catch (std::exception &ex) {
  815       RLOG(ERROR) << "Internal error: Caught exception from main loop: "
  816                   << ex.what();
  817     } catch (...) {
  818       RLOG(ERROR) << "Internal error: Caught unexpected exception";
  819     }
  820 
  821     if (ctx->args->idleTimeout > 0) {
  822       ctx->running = false;
  823       // wake up the thread if it is waiting..
  824       VLOG(1) << "waking up monitoring thread";
  825       pthread_mutex_lock(&ctx->wakeupMutex);
  826       pthread_cond_signal(&ctx->wakeupCond);
  827       pthread_mutex_unlock(&ctx->wakeupMutex);
  828       VLOG(1) << "joining with idle monitoring thread";
  829       pthread_join(ctx->monitorThread, nullptr);
  830       VLOG(1) << "join done";
  831     }
  832   }
  833 
  834   // cleanup so that we can check for leaked resources..
  835   rootInfo.reset();
  836   ctx->setRoot(std::shared_ptr<DirNode>());
  837 
  838   MemoryPool::destroyAll();
  839   openssl_shutdown(encfsArgs->isThreaded);
  840 
  841   return returnCode;
  842 }
  843 
  844 /*
  845     Idle monitoring thread.  This is only used when idle monitoring is enabled.
  846     It will cause the filesystem to be automatically unmounted (causing us to
  847     commit suicide) if the filesystem stays idle too long.  Idle time is only
  848     checked if there are no open files, as I don't want to risk problems by
  849     having the filesystem unmounted from underneath open files!
  850 */
  851 const int ActivityCheckInterval = 10;
  852 
  853 static void *idleMonitor(void *_arg) {
  854   auto *ctx = (EncFS_Context *)_arg;
  855   std::shared_ptr<EncFS_Args> arg = ctx->args;
  856 
  857   const int timeoutCycles = 60 * arg->idleTimeout / ActivityCheckInterval;
  858 
  859   bool unmountres = false;
  860 
  861   // We will notify when FS will be unmounted, so notify that it has just been
  862   // mounted
  863   RLOG(INFO) << "Filesystem mounted: " << arg->opts->unmountPoint;
  864 
  865   pthread_mutex_lock(&ctx->wakeupMutex);
  866 
  867   while (ctx->running) {
  868     unmountres = ctx->usageAndUnmount(timeoutCycles);
  869     if (unmountres) {
  870       break;
  871     }
  872 
  873     struct timeval currentTime;
  874     gettimeofday(&currentTime, nullptr);
  875     struct timespec wakeupTime;
  876     wakeupTime.tv_sec = currentTime.tv_sec + ActivityCheckInterval;
  877     wakeupTime.tv_nsec = currentTime.tv_usec * 1000;
  878     pthread_cond_timedwait(&ctx->wakeupCond, &ctx->wakeupMutex, &wakeupTime);
  879   }
  880 
  881   pthread_mutex_unlock(&ctx->wakeupMutex);
  882 
  883   // If we are here FS has been unmounted, so if the idleMonitor did not unmount itself,
  884   // let's notify (certainly due to a kill signal, a manual unmount...)
  885   if (!unmountres) {
  886     RLOG(INFO) << "Filesystem unmounted: " << arg->opts->unmountPoint;
  887   }
  888 
  889   VLOG(1) << "Idle monitoring thread exiting";
  890 
  891   return nullptr;
  892 }