func pathfsFrontend(key []byte, cipherdir string, mountpoint string, debug bool, openssl bool) *fuse.Server { finalFs := pathfs_frontend.NewFS(key, cipherdir, openssl) 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 // Set values shown in "df -T" and friends // First column, "Filesystem" mOpts.Options = append(mOpts.Options, "fsname="+cipherdir) // Second column, "Type", will be shown as "fuse." + Name mOpts.Name = "gocryptfs" srv, err := fuse.NewServer(conn.RawFS(), mountpoint, &mOpts) if err != nil { fmt.Printf("Mount failed: %v", err) os.Exit(ERREXIT_MOUNT) } srv.SetDebug(debug) return srv }
func newFuseFS(tmpDir string, rpcFS *RpcFs, writableRoot string) (*fuseFS, error) { tmpDir, err := ioutil.TempDir(tmpDir, "termite-task") if err != nil { return nil, err } fs := &fuseFS{ writableRoot: strings.TrimLeft(writableRoot, "/"), workers: map[string]*workerFS{}, rpcFS: rpcFS, rpcNodeFS: pathfs.NewPathNodeFs(&multiRPCFS{rpcFS}, &pathfs.PathNodeFsOptions{ClientInodes: true}), tmpDir: tmpDir, mount: filepath.Join(tmpDir, "mnt"), } if err := os.Mkdir(fs.mount, 0755); err != nil { return nil, err } fs.fsConnector = nodefs.NewFileSystemConnector(fs.rpcNodeFS.Root(), nodeFSOptions()) fuseOpts := fuse.MountOptions{} if os.Geteuid() == 0 { fuseOpts.AllowOther = true } fs.server, err = fuse.NewServer(fs.fsConnector.RawFS(), fs.mount, &fuseOpts) if err != nil { return nil, err } go fs.server.Serve() return fs, nil }
// Mounts a filesystem with the given root node on the given directory func MountRoot(mountpoint string, root Node, opts *Options) (*fuse.Server, *FileSystemConnector, error) { conn := NewFileSystemConnector(root, opts) mountOpts := fuse.MountOptions{} if opts != nil && opts.Debug { mountOpts.Debug = opts.Debug } s, err := fuse.NewServer(conn.RawFS(), mountpoint, &mountOpts) if err != nil { return nil, nil, err } return s, conn, nil }
func main() { cachedir := flag.String("cachedir", "/tmp/termite-cache", "content cache") server := flag.String("server", "localhost:1234", "file server") secretFile := flag.String("secret", "/tmp/secret.txt", "file containing password.") flag.Parse() if flag.NArg() < 1 { fmt.Fprintf(os.Stderr, "usage: %s MOUNTPOINT\n", os.Args[0]) os.Exit(2) } secret, err := ioutil.ReadFile(*secretFile) if err != nil { log.Fatal("ReadFile", err) } rpcConn, err := termite.SetupClient(*server, secret) if err != nil { log.Fatal("dialing:", err) } var fs fuse.FileSystem cache := termite.NewDiskFileCache(*cachedir) fs = termite.NewRpcFs(rpc.NewClient(rpcConn), cache) conn := fuse.NewFileSystemConnector(fs, nil) state := fuse.NewMountState(conn) opts := fuse.MountOptions{} if os.Geteuid() == 0 { opts.AllowOther = true } state.Mount(flag.Arg(0), &opts) if err != nil { fmt.Printf("Mount fail: %v\n", err) os.Exit(1) } state.Debug = true state.Loop(false) }
// pathfsFrontend - initialize gocryptfs/pathfs_frontend // Calls os.Exit on errors func pathfsFrontend(key []byte, args argContainer, confFile *cryptfs.ConfFile) *fuse.Server { // Reconciliate CLI and config file arguments into a Args struct that is passed to the // filesystem implementation frontendArgs := pathfs_frontend.Args{ Cipherdir: args.cipherdir, Masterkey: key, OpenSSL: args.openssl, PlaintextNames: args.plaintextnames, DirIV: args.diriv, EMENames: args.emenames, GCMIV128: args.gcmiv128, } // 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(cryptfs.FlagPlaintextNames) frontendArgs.DirIV = confFile.IsFeatureFlagSet(cryptfs.FlagDirIV) frontendArgs.EMENames = confFile.IsFeatureFlagSet(cryptfs.FlagEMENames) frontendArgs.GCMIV128 = confFile.IsFeatureFlagSet(cryptfs.FlagGCMIV128) } // EMENames implies DirIV, both on the command line and in the config file. if frontendArgs.EMENames { frontendArgs.DirIV = true } // PlainTexnames disables both EMENames and DirIV if frontendArgs.PlaintextNames { frontendArgs.DirIV = false frontendArgs.EMENames = false } cryptfs.Debug.Printf("frontendArgs: ") cryptfs.Debug.JSONDump(frontendArgs) finalFs := pathfs_frontend.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 // 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" srv, err := fuse.NewServer(conn.RawFS(), args.mountpoint, &mOpts) if err != nil { fmt.Printf("Mount failed: %v", err) os.Exit(ERREXIT_MOUNT) } 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 newWorkerFuseFs(tmpDir string, rpcFs fuse.FileSystem, writableRoot string, nobody *User) (*workerFuseFs, error) { tmpDir, err := ioutil.TempDir(tmpDir, "termite-task") if err != nil { return nil, err } me := &workerFuseFs{ tmpDir: tmpDir, writableRoot: strings.TrimLeft(writableRoot, "/"), tasks: map[*WorkerTask]bool{}, } type dirInit struct { dst *string val string } tmpBacking := "" for _, v := range []dirInit{ {&me.rwDir, "rw"}, {&me.mount, "mnt"}, {&tmpBacking, "tmp-backing"}, } { *v.dst = filepath.Join(me.tmpDir, v.val) err = os.Mkdir(*v.dst, 0700) if err != nil { return nil, err } } fuseOpts := fuse.MountOptions{} if os.Geteuid() == 0 { fuseOpts.AllowOther = true } me.rpcNodeFs = fuse.NewPathNodeFs(rpcFs, nil) ttl := 30 * time.Second mOpts := fuse.FileSystemOptions{ EntryTimeout: ttl, AttrTimeout: ttl, NegativeTimeout: ttl, // 32-bit programs have trouble with 64-bit inode // numbers. PortableInodes: true, } me.fsConnector = fuse.NewFileSystemConnector(me.rpcNodeFs, &mOpts) me.MountState = fuse.NewMountState(me.fsConnector) err = me.MountState.Mount(me.mount, &fuseOpts) if err != nil { return nil, err } go me.MountState.Loop() me.unionFs = fs.NewMemUnionFs( me.rwDir, &fuse.PrefixFileSystem{rpcFs, me.writableRoot}) me.procFs = fs.NewProcFs() me.procFs.StripPrefix = me.mount if nobody != nil { me.procFs.Uid = nobody.Uid } type submount struct { mountpoint string fs fuse.NodeFileSystem } mounts := []submount{ {"proc", fuse.NewPathNodeFs(me.procFs, nil)}, {"sys", fuse.NewPathNodeFs(&fuse.ReadonlyFileSystem{fuse.NewLoopbackFileSystem("/sys")}, nil)}, {"tmp", fuse.NewMemNodeFs(tmpBacking + "/tmp")}, {"dev", fs.NewDevNullFs()}, {"var/tmp", fuse.NewMemNodeFs(tmpBacking + "/vartmp")}, } for _, s := range mounts { subOpts := &mOpts if s.mountpoint == "proc" { subOpts = nil } code := me.rpcNodeFs.Mount(s.mountpoint, s.fs, subOpts) if !code.Ok() { if err := me.MountState.Unmount(); err != nil { log.Fatal("FUSE unmount error during cleanup:", err) } return nil, errors.New(fmt.Sprintf("submount error for %s: %v", s.mountpoint, code)) } } if strings.HasPrefix(me.writableRoot, "tmp/") { parent, _ := filepath.Split(me.writableRoot) err := os.MkdirAll(filepath.Join(me.mount, parent), 0755) if err != nil { if err := me.MountState.Unmount(); err != nil { log.Fatal("FUSE unmount error during cleanup:", err) } return nil, errors.New(fmt.Sprintf("Mkdir of %q in /tmp fail: %v", parent, err)) } // This is hackish, but we don't want rpcfs/fsserver // getting confused by asking for tmp/foo/bar // directly. rpcFs.GetAttr("tmp", nil) rpcFs.GetAttr(me.writableRoot, nil) } code := me.rpcNodeFs.Mount(me.writableRoot, me.unionFs, &mOpts) if !code.Ok() { if err := me.MountState.Unmount(); err != nil { log.Fatal("FUSE unmount error during cleanup:", err) } return nil, errors.New(fmt.Sprintf("submount error for %s: %v", me.writableRoot, code)) } return me, nil }
// 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, ConfigCustom: args._configCustom, Raw64: args.raw64, NoPrealloc: args.noprealloc, } // 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) frontendArgs.Raw64 = confFile.IsFeatureFlagSet(configfile.FlagRaw64) 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 var ctlSockBackend ctlsock.Interface if args.reverse { fs := fusefrontend_reverse.NewFS(frontendArgs) finalFs = fs ctlSockBackend = fs } else { fs := fusefrontend.NewFS(frontendArgs) finalFs = fs ctlSockBackend = fs } // We have opened the socket early so that we cannot fail here after // asking the user for the password if args._ctlsockFd != nil { go ctlsock.Serve(args._ctlsockFd, ctlSockBackend) } 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.ko != "" { parts := strings.Split(args.ko, ",") tlog.Debug.Printf("Adding -ko 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 }