"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(¤tTime, 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 }