aboutsummaryrefslogtreecommitdiff
path: root/mount.go
diff options
context:
space:
mode:
Diffstat (limited to 'mount.go')
-rw-r--r--mount.go221
1 files changed, 221 insertions, 0 deletions
diff --git a/mount.go b/mount.go
new file mode 100644
index 0000000..56f1dc1
--- /dev/null
+++ b/mount.go
@@ -0,0 +1,221 @@
+package main
+
+import (
+ "encoding/json"
+ "log/syslog"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/hanwen/go-fuse/fuse"
+ "github.com/hanwen/go-fuse/fuse/nodefs"
+ "github.com/hanwen/go-fuse/fuse/pathfs"
+
+ "github.com/rfjakob/gocryptfs/internal/configfile"
+ "github.com/rfjakob/gocryptfs/internal/cryptocore"
+ "github.com/rfjakob/gocryptfs/internal/fusefrontend"
+ "github.com/rfjakob/gocryptfs/internal/fusefrontend_reverse"
+ "github.com/rfjakob/gocryptfs/internal/tlog"
+)
+
+// doMount mounts an encrypted directory.
+// Called from main.
+func doMount(args *argContainer) int {
+ // Check mountpoint
+ if flagSet.NArg() != 2 {
+ tlog.Fatal.Printf("Usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT", tlog.ProgramName)
+ os.Exit(ErrExitUsage)
+ }
+ var err error
+ args.mountpoint, err = filepath.Abs(flagSet.Arg(1))
+ if err != nil {
+ tlog.Fatal.Printf("Invalid mountpoint: %v", err)
+ os.Exit(ErrExitMountPoint)
+ }
+ if args.nonempty {
+ err = checkDir(args.mountpoint)
+ } else {
+ err = checkDirEmpty(args.mountpoint)
+ }
+ if err != nil {
+ tlog.Fatal.Printf("Invalid mountpoint: %v", err)
+ os.Exit(ErrExitMountPoint)
+ }
+ // Get master key
+ var masterkey []byte
+ var confFile *configfile.ConfFile
+ if args.masterkey != "" {
+ // "-masterkey"
+ tlog.Info.Printf("Using explicit master key.")
+ masterkey = parseMasterKey(args.masterkey)
+ tlog.Info.Printf(tlog.ColorYellow +
+ "THE MASTER KEY IS VISIBLE VIA \"ps ax\" AND MAY BE STORED IN YOUR SHELL HISTORY!\n" +
+ "ONLY USE THIS MODE FOR EMERGENCIES." + tlog.ColorReset)
+ } else if args.zerokey {
+ // "-zerokey"
+ tlog.Info.Printf("Using all-zero dummy master key.")
+ tlog.Info.Printf(tlog.ColorYellow +
+ "ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING." +
+ tlog.ColorReset)
+ masterkey = make([]byte, cryptocore.KeyLen)
+ } else {
+ // Load master key from config file
+ masterkey, confFile = loadConfig(args)
+ printMasterKey(masterkey)
+ }
+ // Initialize FUSE server
+ tlog.Debug.Printf("cli args: %v", args)
+ srv := initFuseFrontend(masterkey, args, confFile)
+ tlog.Info.Println(tlog.ColorGreen + "Filesystem mounted and ready." + tlog.ColorReset)
+ // We have been forked into the background, as evidenced by the set
+ // "notifypid".
+ if args.notifypid > 0 {
+ // Chdir to the root directory so we don't block unmounting the CWD
+ os.Chdir("/")
+ // Switch to syslog
+ if !args.nosyslog {
+ tlog.Info.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_INFO)
+ tlog.Debug.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_DEBUG)
+ tlog.Warn.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_WARNING)
+ tlog.SwitchLoggerToSyslog(syslog.LOG_USER | syslog.LOG_WARNING)
+ // Daemons should close all fds (and we don't want to get killed by
+ // SIGPIPE if any of those get closed on the other end)
+ os.Stderr.Close()
+ os.Stdout.Close()
+ os.Stdin.Close()
+ }
+ // Send SIGUSR1 to our parent
+ sendUsr1(args.notifypid)
+ }
+ // Wait for SIGINT in the background and unmount ourselves if we get it.
+ // This prevents a dangling "Transport endpoint is not connected"
+ // mountpoint if the user hits CTRL-C.
+ handleSigint(srv, args.mountpoint)
+ // Jump into server loop. Returns when it gets an umount request from the kernel.
+ srv.Serve()
+ return 0
+}
+
+// initFuseFrontend - initialize gocryptfs/fusefrontend
+// Calls os.Exit on errors
+func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfFile) *fuse.Server {
+ // Reconciliate CLI and config file arguments into a fusefrontend.Args struct
+ // that is passed to the filesystem implementation
+ cryptoBackend := cryptocore.BackendGoGCM
+ if args.openssl {
+ cryptoBackend = cryptocore.BackendOpenSSL
+ }
+ if args.aessiv {
+ cryptoBackend = cryptocore.BackendAESSIV
+ }
+ frontendArgs := fusefrontend.Args{
+ Cipherdir: args.cipherdir,
+ Masterkey: key,
+ PlaintextNames: args.plaintextnames,
+ LongNames: args.longnames,
+ CryptoBackend: cryptoBackend,
+ }
+ // confFile is nil when "-zerokey" or "-masterkey" was used
+ if confFile != nil {
+ // Settings from the config file override command line args
+ frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames)
+ if confFile.IsFeatureFlagSet(configfile.FlagAESSIV) {
+ frontendArgs.CryptoBackend = cryptocore.BackendAESSIV
+ } else if args.reverse {
+ tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file")
+ os.Exit(ErrExitUsage)
+ }
+ }
+ // If allow_other is set and we run as root, try to give newly created files to
+ // the right user.
+ if args.allow_other && os.Getuid() == 0 {
+ frontendArgs.PreserveOwner = true
+ }
+ jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t")
+ tlog.Debug.Printf("frontendArgs: %s", string(jsonBytes))
+ var finalFs pathfs.FileSystem
+ if args.reverse {
+ finalFs = fusefrontend_reverse.NewFS(frontendArgs)
+ } else {
+ finalFs = fusefrontend.NewFS(frontendArgs)
+ }
+ pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
+ pathFs := pathfs.NewPathNodeFs(finalFs, pathFsOpts)
+ fuseOpts := &nodefs.Options{
+ // These options are to be compatible with libfuse defaults,
+ // making benchmarking easier.
+ NegativeTimeout: time.Second,
+ AttrTimeout: time.Second,
+ EntryTimeout: time.Second,
+ }
+ conn := nodefs.NewFileSystemConnector(pathFs.Root(), fuseOpts)
+ var mOpts fuse.MountOptions
+ mOpts.AllowOther = false
+ if args.allow_other {
+ tlog.Info.Printf(tlog.ColorYellow + "The option \"-allow_other\" is set. Make sure the file " +
+ "permissions protect your data from unwanted access." + tlog.ColorReset)
+ mOpts.AllowOther = true
+ // Make the kernel check the file permissions for us
+ mOpts.Options = append(mOpts.Options, "default_permissions")
+ }
+ if args.nonempty {
+ mOpts.Options = append(mOpts.Options, "nonempty")
+ }
+ // Set values shown in "df -T" and friends
+ // First column, "Filesystem"
+ mOpts.Options = append(mOpts.Options, "fsname="+args.cipherdir)
+ // Second column, "Type", will be shown as "fuse." + Name
+ mOpts.Name = "gocryptfs"
+ if args.reverse {
+ mOpts.Name += "-reverse"
+ }
+
+ // The kernel enforces read-only operation, we just have to pass "ro".
+ // Reverse mounts are always read-only
+ if args.ro || args.reverse {
+ mOpts.Options = append(mOpts.Options, "ro")
+ }
+ // Add additional mount options (if any) after the stock ones, so the user has
+ // a chance to override them.
+ if args.o != "" {
+ parts := strings.Split(args.o, ",")
+ tlog.Debug.Printf("Adding -o mount options: %v", parts)
+ mOpts.Options = append(mOpts.Options, parts...)
+ }
+ srv, err := fuse.NewServer(conn.RawFS(), args.mountpoint, &mOpts)
+ if err != nil {
+ tlog.Fatal.Printf("Mount failed: %v", err)
+ os.Exit(ErrExitMount)
+ }
+ srv.SetDebug(args.fusedebug)
+
+ // All FUSE file and directory create calls carry explicit permission
+ // information. We need an unrestricted umask to create the files and
+ // directories with the requested permissions.
+ syscall.Umask(0000)
+
+ return srv
+}
+
+func handleSigint(srv *fuse.Server, mountpoint string) {
+ ch := make(chan os.Signal, 1)
+ signal.Notify(ch, os.Interrupt)
+ signal.Notify(ch, syscall.SIGTERM)
+ go func() {
+ <-ch
+ err := srv.Unmount()
+ if err != nil {
+ tlog.Warn.Print(err)
+ tlog.Info.Printf("Trying lazy unmount")
+ cmd := exec.Command("fusermount", "-u", "-z", mountpoint)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.Run()
+ }
+ os.Exit(1)
+ }()
+}