//go:build linux || (darwin && amd64) // +build linux darwin,amd64 // Package mount2 implements a FUSE mounting system for rclone remotes. package mount2 import ( "fmt" "log" "runtime" fusefs "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" "github.com/rclone/rclone/cmd/mountlib" "github.com/rclone/rclone/fs" "github.com/rclone/rclone/vfs" ) func init() { mountlib.NewMountCommand("mount2", true, mount) mountlib.AddRc("mount2", mount) } // mountOptions configures the options from the command line flags // // man mount.fuse for more info and note the -o flag for other options func mountOptions(fsys *FS, f fs.Fs, opt *mountlib.Options) (mountOpts *fuse.MountOptions) { mountOpts = &fuse.MountOptions{ AllowOther: fsys.opt.AllowOther, FsName: opt.DeviceName, Name: "rclone", DisableXAttrs: true, Debug: fsys.opt.DebugFUSE, MaxReadAhead: int(fsys.opt.MaxReadAhead), // RememberInodes: true, // SingleThreaded: true, /* AllowOther bool // Options are passed as -o string to fusermount. Options []string // Default is _DEFAULT_BACKGROUND_TASKS, 12. This numbers // controls the allowed number of requests that relate to // async I/O. Concurrency for synchronous I/O is not limited. MaxBackground int // Write size to use. If 0, use default. This number is // capped at the kernel maximum. MaxWrite int // Max read ahead to use. If 0, use default. This number is // capped at the kernel maximum. MaxReadAhead int // If IgnoreSecurityLabels is set, all security related xattr // requests will return NO_DATA without passing through the // user defined filesystem. You should only set this if you // file system implements extended attributes, and you are not // interested in security labels. IgnoreSecurityLabels bool // ignoring labels should be provided as a fusermount mount option. // If RememberInodes is set, we will never forget inodes. // This may be useful for NFS. RememberInodes bool // Values shown in "df -T" and friends // First column, "Filesystem" FsName string // Second column, "Type", will be shown as "fuse." + Name Name string // If set, wrap the file system in a single-threaded locking wrapper. SingleThreaded bool // If set, return ENOSYS for Getxattr calls, so the kernel does not issue any // Xattr operations at all. DisableXAttrs bool // If set, print debugging information. Debug bool // If set, ask kernel to forward file locks to FUSE. If using, // you must implement the GetLk/SetLk/SetLkw methods. EnableLocks bool // If set, ask kernel not to do automatic data cache invalidation. // The filesystem is fully responsible for invalidating data cache. ExplicitDataCacheControl bool */ } var opts []string // FIXME doesn't work opts = append(opts, fmt.Sprintf("max_readahead=%d", maxReadAhead)) if fsys.opt.AllowOther { opts = append(opts, "allow_other") } if fsys.opt.AllowRoot { opts = append(opts, "allow_root") } if fsys.opt.DefaultPermissions { opts = append(opts, "default_permissions") } if fsys.VFS.Opt.ReadOnly { opts = append(opts, "ro") } if fsys.opt.WritebackCache { log.Printf("FIXME --write-back-cache not supported") // FIXME opts = append(opts,fuse.WritebackCache()) } // Some OS X only options if runtime.GOOS == "darwin" { opts = append(opts, // VolumeName sets the volume name shown in Finder. fmt.Sprintf("volname=%s", opt.VolumeName), // NoAppleXattr makes OSXFUSE disallow extended attributes with the // prefix "com.apple.". This disables persistent Finder state and // other such information. "noapplexattr", // NoAppleDouble makes OSXFUSE disallow files with names used by OS X // to store extended attributes on file systems that do not support // them natively. // // Such file names are: // // ._* // .DS_Store "noappledouble", ) } mountOpts.Options = opts return mountOpts } // mount the file system // // The mount point will be ready when this returns. // // returns an error, and an error channel for the serve process to // report an error when fusermount is called. func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error, func() error, error) { f := VFS.Fs() if err := mountlib.CheckOverlap(f, mountpoint); err != nil { return nil, nil, err } if err := mountlib.CheckAllowNonEmpty(mountpoint, opt); err != nil { return nil, nil, err } fs.Debugf(f, "Mounting on %q", mountpoint) fsys := NewFS(VFS, opt) // nodeFsOpts := &fusefs.PathNodeFsOptions{ // ClientInodes: false, // Debug: mountlib.DebugFUSE, // } // nodeFs := fusefs.NewPathNodeFs(fsys, nodeFsOpts) //mOpts := fusefs.NewOptions() // default options // FIXME // mOpts.EntryTimeout = 10 * time.Second // mOpts.AttrTimeout = 10 * time.Second // mOpts.NegativeTimeout = 10 * time.Second //mOpts.Debug = mountlib.DebugFUSE //conn := fusefs.NewFileSystemConnector(nodeFs.Root(), mOpts) mountOpts := mountOptions(fsys, f, opt) // FIXME fill out opts := fusefs.Options{ MountOptions: *mountOpts, EntryTimeout: &opt.AttrTimeout, AttrTimeout: &opt.AttrTimeout, // UID // GID } root, err := fsys.Root() if err != nil { return nil, nil, err } rawFS := fusefs.NewNodeFS(root, &opts) server, err := fuse.NewServer(rawFS, mountpoint, &opts.MountOptions) if err != nil { return nil, nil, err } //mountOpts := &fuse.MountOptions{} //server, err := fusefs.Mount(mountpoint, fsys, &opts) // server, err := fusefs.Mount(mountpoint, root, &opts) // if err != nil { // return nil, nil, err // } umount := func() error { // Shutdown the VFS fsys.VFS.Shutdown() return server.Unmount() } // serverSettings := server.KernelSettings() // fs.Debugf(f, "Server settings %+v", serverSettings) // Serve the mount point in the background returning error to errChan errs := make(chan error, 1) go func() { server.Serve() errs <- nil }() fs.Debugf(f, "Waiting for the mount to start...") err = server.WaitMount() if err != nil { return nil, nil, err } fs.Debugf(f, "Mount started") return errs, umount, nil }