summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.go210
-rw-r--r--mount.go221
2 files changed, 224 insertions, 207 deletions
diff --git a/main.go b/main.go
index 9fc9b6b..467a143 100644
--- a/main.go
+++ b/main.go
@@ -1,29 +1,17 @@
package main
import (
- "encoding/json"
"fmt"
- "log/syslog"
"os"
- "os/exec"
- "os/signal"
+
"path/filepath"
"runtime"
"runtime/pprof"
"strconv"
- "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/contentenc"
- "github.com/rfjakob/gocryptfs/internal/cryptocore"
- "github.com/rfjakob/gocryptfs/internal/fusefrontend"
- "github.com/rfjakob/gocryptfs/internal/fusefrontend_reverse"
"github.com/rfjakob/gocryptfs/internal/readpassword"
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
"github.com/rfjakob/gocryptfs/internal/tlog"
@@ -62,7 +50,7 @@ Options:
flagSet.PrintDefaults()
}
-// loadConfig - load the config file "filename", prompting the user for the password
+// loadConfig loads the config file "args.config", prompting the user for the password
func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.ConfFile) {
// Check if the file can be opened at all before prompting for a password
fd, err := os.Open(args.config)
@@ -78,7 +66,6 @@ func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.Conf
tlog.Fatal.Println(err)
os.Exit(ErrExitLoadConf)
}
-
return masterkey, confFile
}
@@ -227,196 +214,5 @@ func main() {
changePassword(&args) // does not return
}
// Mount
- // Check mountpoint
- if flagSet.NArg() != 2 {
- tlog.Fatal.Printf("Usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT", tlog.ProgramName)
- os.Exit(ErrExitUsage)
- }
- 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()
- // main exits with code 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)
- }()
+ os.Exit(doMount(&args))
}
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)
+ }()
+}