From 0272a7f405a3f587f2fb41f285b898ff2178f182 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Thu, 23 Jul 2020 17:17:01 +0100 Subject: [PATCH] mount: change interface of mount commands to take mount options This is in preparation of being able to pass mount options to the rc command "mount/mount" --- backend/cache/cache_mount_unix_test.go | 4 +- backend/cache/cache_mount_windows_test.go | 2 +- cmd/cmount/mount.go | 38 +++---- cmd/mount/dir.go | 13 +-- cmd/mount/file.go | 4 +- cmd/mount/fs.go | 8 +- cmd/mount/mount.go | 38 +++---- cmd/mount2/file.go | 12 ++- cmd/mount2/fs.go | 14 +-- cmd/mount2/mount.go | 24 ++--- cmd/mount2/node.go | 14 +-- cmd/mountlib/mount.go | 115 +++++++++++++--------- cmd/mountlib/rc.go | 2 +- vfs/vfstest/fs.go | 16 +-- vfs/vfstest_test.go | 3 +- 15 files changed, 166 insertions(+), 141 deletions(-) diff --git a/backend/cache/cache_mount_unix_test.go b/backend/cache/cache_mount_unix_test.go index ea7e7bdbb..44ab30ed5 100644 --- a/backend/cache/cache_mount_unix_test.go +++ b/backend/cache/cache_mount_unix_test.go @@ -21,7 +21,7 @@ import ( func (r *run) mountFs(t *testing.T, f fs.Fs) { device := f.Name() + ":" + f.Root() var options = []fuse.MountOption{ - fuse.MaxReadahead(uint32(mountlib.MaxReadAhead)), + fuse.MaxReadahead(uint32(mountlib.Opt.MaxReadAhead)), fuse.Subtype("rclone"), fuse.FSName(device), fuse.VolumeName(device), fuse.NoAppleDouble(), @@ -33,7 +33,7 @@ func (r *run) mountFs(t *testing.T, f fs.Fs) { c, err := fuse.Mount(r.mntDir, options...) require.NoError(t, err) VFS := vfs.New(f, &vfsflags.Opt) - filesys := mount.NewFS(VFS) + filesys := mount.NewFS(VFS, &mountlib.Opt) server := fusefs.New(c, nil) // Serve the mount point in the background returning error to errChan diff --git a/backend/cache/cache_mount_windows_test.go b/backend/cache/cache_mount_windows_test.go index 17c6a2c5a..b55edd627 100644 --- a/backend/cache/cache_mount_windows_test.go +++ b/backend/cache/cache_mount_windows_test.go @@ -41,7 +41,7 @@ func (r *run) mountFs(t *testing.T, f fs.Fs) { options := []string{ "-o", "fsname=" + device, "-o", "subtype=rclone", - "-o", fmt.Sprintf("max_readahead=%d", mountlib.MaxReadAhead), + "-o", fmt.Sprintf("max_readahead=%d", mountlib.Opt.MaxReadAhead), "-o", "uid=-1", "-o", "gid=-1", "-o", "allow_other", diff --git a/cmd/cmount/mount.go b/cmd/cmount/mount.go index 64b1b69f0..5f33b9a19 100644 --- a/cmd/cmount/mount.go +++ b/cmd/cmount/mount.go @@ -38,29 +38,29 @@ func init() { } // mountOptions configures the options from the command line flags -func mountOptions(VFS *vfs.VFS, device string, mountpoint string) (options []string) { +func mountOptions(VFS *vfs.VFS, device string, mountpoint string, opt *mountlib.Options) (options []string) { // Options options = []string{ "-o", "fsname=" + device, "-o", "subtype=rclone", - "-o", fmt.Sprintf("max_readahead=%d", mountlib.MaxReadAhead), - "-o", fmt.Sprintf("attr_timeout=%g", mountlib.AttrTimeout.Seconds()), + "-o", fmt.Sprintf("max_readahead=%d", opt.MaxReadAhead), + "-o", fmt.Sprintf("attr_timeout=%g", opt.AttrTimeout.Seconds()), // This causes FUSE to supply O_TRUNC with the Open // call which is more efficient for cmount. However // it does not work with cgofuse on Windows with // WinFSP so cmount must work with or without it. "-o", "atomic_o_trunc", } - if mountlib.DebugFUSE { + if opt.DebugFUSE { options = append(options, "-o", "debug") } // OSX options if runtime.GOOS == "darwin" { - if mountlib.NoAppleDouble { + if opt.NoAppleDouble { options = append(options, "-o", "noappledouble") } - if mountlib.NoAppleXattr { + if opt.NoAppleXattr { options = append(options, "-o", "noapplexattr") } } @@ -74,35 +74,35 @@ func mountOptions(VFS *vfs.VFS, device string, mountpoint string) (options []str } if runtime.GOOS == "darwin" || runtime.GOOS == "windows" { - if mountlib.VolumeName != "" { - options = append(options, "-o", "volname="+mountlib.VolumeName) + if opt.VolumeName != "" { + options = append(options, "-o", "volname="+opt.VolumeName) } } - if mountlib.AllowNonEmpty { + if opt.AllowNonEmpty { options = append(options, "-o", "nonempty") } - if mountlib.AllowOther { + if opt.AllowOther { options = append(options, "-o", "allow_other") } - if mountlib.AllowRoot { + if opt.AllowRoot { options = append(options, "-o", "allow_root") } - if mountlib.DefaultPermissions { + if opt.DefaultPermissions { options = append(options, "-o", "default_permissions") } if VFS.Opt.ReadOnly { options = append(options, "-o", "ro") } - if mountlib.WritebackCache { + if opt.WritebackCache { // FIXME? options = append(options, "-o", WritebackCache()) } - if mountlib.DaemonTimeout != 0 { - options = append(options, "-o", fmt.Sprintf("daemon_timeout=%d", int(mountlib.DaemonTimeout.Seconds()))) + if opt.DaemonTimeout != 0 { + options = append(options, "-o", fmt.Sprintf("daemon_timeout=%d", int(opt.DaemonTimeout.Seconds()))) } - for _, option := range mountlib.ExtraOptions { + for _, option := range opt.ExtraOptions { options = append(options, "-o", option) } - for _, option := range mountlib.ExtraFlags { + for _, option := range opt.ExtraFlags { options = append(options, option) } return options @@ -128,7 +128,7 @@ func waitFor(fn func() bool) (ok bool) { // // 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) (<-chan error, func() error, error) { +func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error, func() error, error) { f := VFS.Fs() fs.Debugf(f, "Mounting on %q", mountpoint) @@ -152,7 +152,7 @@ func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) host.SetCapCaseInsensitive(f.Features().CaseInsensitive) // Create options - options := mountOptions(VFS, f.Name()+":"+f.Root(), mountpoint) + options := mountOptions(VFS, f.Name()+":"+f.Root(), mountpoint, opt) fs.Debugf(f, "Mounting with options: %q", options) // Serve the mount point in the background returning error to errChan diff --git a/cmd/mount/dir.go b/cmd/mount/dir.go index 7be77c77f..38c7eb7a7 100644 --- a/cmd/mount/dir.go +++ b/cmd/mount/dir.go @@ -19,6 +19,7 @@ import ( // Dir represents a directory entry type Dir struct { *vfs.Dir + fsys *FS } // Check interface satisfied @@ -27,7 +28,7 @@ var _ fusefs.Node = (*Dir)(nil) // Attr updates the attributes of a directory func (d *Dir) Attr(ctx context.Context, a *fuse.Attr) (err error) { defer log.Trace(d, "")("attr=%+v, err=%v", a, &err) - a.Valid = mountlib.AttrTimeout + a.Valid = d.fsys.opt.AttrTimeout a.Gid = d.VFS().Opt.GID a.Uid = d.VFS().Opt.UID a.Mode = os.ModeDir | d.VFS().Opt.DirPerms @@ -75,7 +76,7 @@ func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.Lo if err != nil { return nil, translateError(err) } - resp.EntryValid = mountlib.AttrTimeout + resp.EntryValid = d.fsys.opt.AttrTimeout // Check the mnode to see if it has a fuse Node cached // We must return the same fuse nodes for vfs Nodes node, ok := mnode.Sys().(fusefs.Node) @@ -84,9 +85,9 @@ func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.Lo } switch x := mnode.(type) { case *vfs.File: - node = &File{x} + node = &File{x, d.fsys} case *vfs.Dir: - node = &Dir{x} + node = &Dir{x, d.fsys} default: panic("bad type") } @@ -139,7 +140,7 @@ func (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.Cr if err != nil { return nil, nil, translateError(err) } - node = &File{file} + node = &File{file, d.fsys} file.SetSys(node) // cache the FUSE node for later return node, &FileHandle{fh}, err } @@ -153,7 +154,7 @@ func (d *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (node fusefs.No if err != nil { return nil, translateError(err) } - node = &Dir{dir} + node = &Dir{dir, d.fsys} dir.SetSys(node) // cache the FUSE node for later return node, nil } diff --git a/cmd/mount/file.go b/cmd/mount/file.go index 077d1a7ad..fbefca457 100644 --- a/cmd/mount/file.go +++ b/cmd/mount/file.go @@ -8,7 +8,6 @@ import ( "bazil.org/fuse" fusefs "bazil.org/fuse/fs" - "github.com/rclone/rclone/cmd/mountlib" "github.com/rclone/rclone/fs/log" "github.com/rclone/rclone/vfs" ) @@ -16,6 +15,7 @@ import ( // File represents a file type File struct { *vfs.File + fsys *FS } // Check interface satisfied @@ -24,7 +24,7 @@ var _ fusefs.Node = (*File)(nil) // Attr fills out the attributes for the file func (f *File) Attr(ctx context.Context, a *fuse.Attr) (err error) { defer log.Trace(f, "")("a=%+v, err=%v", a, &err) - a.Valid = mountlib.AttrTimeout + a.Valid = f.fsys.opt.AttrTimeout modTime := f.File.ModTime() Size := uint64(f.File.Size()) Blocks := (Size + 511) / 512 diff --git a/cmd/mount/fs.go b/cmd/mount/fs.go index 0fb384397..7a7aa667e 100644 --- a/cmd/mount/fs.go +++ b/cmd/mount/fs.go @@ -20,17 +20,19 @@ import ( // FS represents the top level filing system type FS struct { *vfs.VFS - f fs.Fs + f fs.Fs + opt *mountlib.Options } // Check interface satisfied var _ fusefs.FS = (*FS)(nil) // NewFS makes a new FS -func NewFS(VFS *vfs.VFS) *FS { +func NewFS(VFS *vfs.VFS, opt *mountlib.Options) *FS { fsys := &FS{ VFS: VFS, f: VFS.Fs(), + opt: opt, } return fsys } @@ -42,7 +44,7 @@ func (f *FS) Root() (node fusefs.Node, err error) { if err != nil { return nil, translateError(err) } - return &Dir{root}, nil + return &Dir{root, f}, nil } // Check interface satisfied diff --git a/cmd/mount/mount.go b/cmd/mount/mount.go index c49cb3b6f..8886bae6d 100644 --- a/cmd/mount/mount.go +++ b/cmd/mount/mount.go @@ -20,52 +20,52 @@ func init() { } // mountOptions configures the options from the command line flags -func mountOptions(VFS *vfs.VFS, device string) (options []fuse.MountOption) { +func mountOptions(VFS *vfs.VFS, device string, opt *mountlib.Options) (options []fuse.MountOption) { options = []fuse.MountOption{ - fuse.MaxReadahead(uint32(mountlib.MaxReadAhead)), + fuse.MaxReadahead(uint32(opt.MaxReadAhead)), fuse.Subtype("rclone"), fuse.FSName(device), - fuse.VolumeName(mountlib.VolumeName), + fuse.VolumeName(opt.VolumeName), // Options from benchmarking in the fuse module //fuse.MaxReadahead(64 * 1024 * 1024), //fuse.WritebackCache(), } - if mountlib.AsyncRead { + if opt.AsyncRead { options = append(options, fuse.AsyncRead()) } - if mountlib.NoAppleDouble { + if opt.NoAppleDouble { options = append(options, fuse.NoAppleDouble()) } - if mountlib.NoAppleXattr { + if opt.NoAppleXattr { options = append(options, fuse.NoAppleXattr()) } - if mountlib.AllowNonEmpty { + if opt.AllowNonEmpty { options = append(options, fuse.AllowNonEmptyMount()) } - if mountlib.AllowOther { + if opt.AllowOther { options = append(options, fuse.AllowOther()) } - if mountlib.AllowRoot { + if opt.AllowRoot { // options = append(options, fuse.AllowRoot()) fs.Errorf(nil, "Ignoring --allow-root. Support has been removed upstream - see https://github.com/bazil/fuse/issues/144 for more info") } - if mountlib.DefaultPermissions { + if opt.DefaultPermissions { options = append(options, fuse.DefaultPermissions()) } if VFS.Opt.ReadOnly { options = append(options, fuse.ReadOnly()) } - if mountlib.WritebackCache { + if opt.WritebackCache { options = append(options, fuse.WritebackCache()) } - if mountlib.DaemonTimeout != 0 { - options = append(options, fuse.DaemonTimeout(fmt.Sprint(int(mountlib.DaemonTimeout.Seconds())))) + if opt.DaemonTimeout != 0 { + options = append(options, fuse.DaemonTimeout(fmt.Sprint(int(opt.DaemonTimeout.Seconds())))) } - if len(mountlib.ExtraOptions) > 0 { + if len(opt.ExtraOptions) > 0 { fs.Errorf(nil, "-o/--option not supported with this FUSE backend") } - if len(mountlib.ExtraFlags) > 0 { + if len(opt.ExtraFlags) > 0 { fs.Errorf(nil, "--fuse-flag not supported with this FUSE backend") } return options @@ -77,8 +77,8 @@ func mountOptions(VFS *vfs.VFS, device string) (options []fuse.MountOption) { // // 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) (<-chan error, func() error, error) { - if mountlib.DebugFUSE { +func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error, func() error, error) { + if opt.DebugFUSE { fuse.Debug = func(msg interface{}) { fs.Debugf("fuse", "%v", msg) } @@ -86,12 +86,12 @@ func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) f := VFS.Fs() fs.Debugf(f, "Mounting on %q", mountpoint) - c, err := fuse.Mount(mountpoint, mountOptions(VFS, f.Name()+":"+f.Root())...) + c, err := fuse.Mount(mountpoint, mountOptions(VFS, f.Name()+":"+f.Root(), opt)...) if err != nil { return nil, nil, err } - filesys := NewFS(VFS) + filesys := NewFS(VFS, opt) server := fusefs.New(c, nil) // Serve the mount point in the background returning error to errChan diff --git a/cmd/mount2/file.go b/cmd/mount2/file.go index 32aba8644..416d5d59d 100644 --- a/cmd/mount2/file.go +++ b/cmd/mount2/file.go @@ -33,13 +33,15 @@ import ( // FOPEN_DIRECT_IO flag from their `Open` method. See directio_test.go // for an example. type FileHandle struct { - h vfs.Handle + h vfs.Handle + fsys *FS } // Create a new FileHandle -func newFileHandle(h vfs.Handle) *FileHandle { +func newFileHandle(h vfs.Handle, fsys *FS) *FileHandle { return &FileHandle{ - h: h, + h: h, + fsys: fsys, } } @@ -115,7 +117,7 @@ var _ fusefs.FileFsyncer = (*FileHandle)(nil) // is assumed, and the 'blocks' field is set accordingly. func (f *FileHandle) Getattr(ctx context.Context, out *fuse.AttrOut) (errno syscall.Errno) { defer log.Trace(f, "")("attr=%v, errno=%v", &out, &errno) - setAttrOut(f.h.Node(), out) + f.fsys.setAttrOut(f.h.Node(), out) return 0 } @@ -125,7 +127,7 @@ var _ fusefs.FileGetattrer = (*FileHandle)(nil) func (f *FileHandle) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) (errno syscall.Errno) { defer log.Trace(f, "in=%v", in)("attr=%v, errno=%v", &out, &errno) var err error - setAttrOut(f.h.Node(), out) + f.fsys.setAttrOut(f.h.Node(), out) size, ok := in.GetSize() if ok { err = f.h.Truncate(int64(size)) diff --git a/cmd/mount2/fs.go b/cmd/mount2/fs.go index e359d79f6..00e97e8a3 100644 --- a/cmd/mount2/fs.go +++ b/cmd/mount2/fs.go @@ -20,13 +20,15 @@ import ( type FS struct { VFS *vfs.VFS f fs.Fs + opt *mountlib.Options } // NewFS creates a pathfs.FileSystem from the fs.Fs passed in -func NewFS(VFS *vfs.VFS) *FS { +func NewFS(VFS *vfs.VFS, opt *mountlib.Options) *FS { fsys := &FS{ VFS: VFS, f: VFS.Fs(), + opt: opt, } return fsys } @@ -84,16 +86,16 @@ func setAttr(node vfs.Node, attr *fuse.Attr) { } // fill in AttrOut from node -func setAttrOut(node vfs.Node, out *fuse.AttrOut) { +func (f *FS) setAttrOut(node vfs.Node, out *fuse.AttrOut) { setAttr(node, &out.Attr) - out.SetTimeout(mountlib.AttrTimeout) + out.SetTimeout(f.opt.AttrTimeout) } // fill in EntryOut from node -func setEntryOut(node vfs.Node, out *fuse.EntryOut) { +func (f *FS) setEntryOut(node vfs.Node, out *fuse.EntryOut) { setAttr(node, &out.Attr) - out.SetEntryTimeout(mountlib.AttrTimeout) - out.SetAttrTimeout(mountlib.AttrTimeout) + out.SetEntryTimeout(f.opt.AttrTimeout) + out.SetAttrTimeout(f.opt.AttrTimeout) } // Translate errors from mountlib into Syscall error numbers diff --git a/cmd/mount2/mount.go b/cmd/mount2/mount.go index a30515b92..4acd7b3ea 100644 --- a/cmd/mount2/mount.go +++ b/cmd/mount2/mount.go @@ -27,12 +27,12 @@ func init() { func mountOptions(fsys *FS, f fs.Fs) (mountOpts *fuse.MountOptions) { device := f.Name() + ":" + f.Root() mountOpts = &fuse.MountOptions{ - AllowOther: mountlib.AllowOther, + AllowOther: fsys.opt.AllowOther, FsName: device, Name: "rclone", DisableXAttrs: true, - Debug: mountlib.DebugFUSE, - MaxReadAhead: int(mountlib.MaxReadAhead), + Debug: fsys.opt.DebugFUSE, + MaxReadAhead: int(fsys.opt.MaxReadAhead), // RememberInodes: true, // SingleThreaded: true, @@ -96,22 +96,22 @@ func mountOptions(fsys *FS, f fs.Fs) (mountOpts *fuse.MountOptions) { } var opts []string // FIXME doesn't work opts = append(opts, fmt.Sprintf("max_readahead=%d", maxReadAhead)) - if mountlib.AllowNonEmpty { + if fsys.opt.AllowNonEmpty { opts = append(opts, "nonempty") } - if mountlib.AllowOther { + if fsys.opt.AllowOther { opts = append(opts, "allow_other") } - if mountlib.AllowRoot { + if fsys.opt.AllowRoot { opts = append(opts, "allow_root") } - if mountlib.DefaultPermissions { + if fsys.opt.DefaultPermissions { opts = append(opts, "default_permissions") } if fsys.VFS.Opt.ReadOnly { opts = append(opts, "ro") } - if mountlib.WritebackCache { + if fsys.opt.WritebackCache { log.Printf("FIXME --write-back-cache not supported") // FIXME opts = append(opts,fuse.WritebackCache()) } @@ -147,11 +147,11 @@ func mountOptions(fsys *FS, f fs.Fs) (mountOpts *fuse.MountOptions) { // // 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) (<-chan error, func() error, error) { +func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error, func() error, error) { f := VFS.Fs() fs.Debugf(f, "Mounting on %q", mountpoint) - fsys := NewFS(VFS) + fsys := NewFS(VFS, opt) // nodeFsOpts := &fusefs.PathNodeFsOptions{ // ClientInodes: false, // Debug: mountlib.DebugFUSE, @@ -171,8 +171,8 @@ func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) // FIXME fill out opts := fusefs.Options{ MountOptions: *mountOpts, - EntryTimeout: &mountlib.AttrTimeout, - AttrTimeout: &mountlib.AttrTimeout, + EntryTimeout: &opt.AttrTimeout, + AttrTimeout: &opt.AttrTimeout, // UID // GID } diff --git a/cmd/mount2/node.go b/cmd/mount2/node.go index e1c52ce7b..8619df10f 100644 --- a/cmd/mount2/node.go +++ b/cmd/mount2/node.go @@ -111,7 +111,7 @@ var _ = (fusefs.NodeStatfser)((*Node)(nil)) // with the Options.NullPermissions setting. If blksize is unset, 4096 // is assumed, and the 'blocks' field is set accordingly. func (n *Node) Getattr(ctx context.Context, f fusefs.FileHandle, out *fuse.AttrOut) syscall.Errno { - setAttrOut(n.node, out) + n.fsys.setAttrOut(n.node, out) return 0 } @@ -121,7 +121,7 @@ var _ = (fusefs.NodeGetattrer)((*Node)(nil)) func (n *Node) Setattr(ctx context.Context, f fusefs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) (errno syscall.Errno) { defer log.Trace(n, "in=%v", in)("out=%#v, errno=%v", &out, &errno) var err error - setAttrOut(n.node, out) + n.fsys.setAttrOut(n.node, out) size, ok := in.GetSize() if ok { err = n.node.Truncate(int64(size)) @@ -158,7 +158,7 @@ func (n *Node) Open(ctx context.Context, flags uint32) (fh fusefs.FileHandle, fu if entry := n.node.DirEntry(); entry != nil && entry.Size() < 0 { fuseFlags |= fuse.FOPEN_DIRECT_IO } - return newFileHandle(handle), fuseFlags, 0 + return newFileHandle(handle, n.fsys), fuseFlags, 0 } var _ = (fusefs.NodeOpener)((*Node)(nil)) @@ -197,7 +197,7 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ino // FIXME // out.SetEntryTimeout(dt time.Duration) // out.SetAttrTimeout(dt time.Duration) - setEntryOut(vfsNode, out) + n.fsys.setEntryOut(vfsNode, out) return n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode}), 0 } @@ -306,7 +306,7 @@ func (n *Node) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.En return nil, translateError(err) } newNode := newNode(n.fsys, newDir) - setEntryOut(newNode.node, out) + n.fsys.setEntryOut(newNode.node, out) newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode}) return newInode, 0 } @@ -333,7 +333,7 @@ func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint3 if err != nil { return nil, nil, 0, translateError(err) } - fh = newFileHandle(handle) + fh = newFileHandle(handle, n.fsys) // FIXME // fh = &fusefs.WithFlags{ // File: fh, @@ -346,7 +346,7 @@ func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint3 if errno != 0 { return nil, nil, 0, errno } - setEntryOut(vfsNode, out) + n.fsys.setEntryOut(vfsNode, out) newNode := newNode(n.fsys, vfsNode) fs.Debugf(nil, "attr=%#v", out.Attr) newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode}) diff --git a/cmd/mountlib/mount.go b/cmd/mountlib/mount.go index 0aca24465..f820bce82 100644 --- a/cmd/mountlib/mount.go +++ b/cmd/mountlib/mount.go @@ -17,37 +17,48 @@ import ( "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/config" "github.com/rclone/rclone/fs/config/flags" + "github.com/rclone/rclone/fs/rc" "github.com/rclone/rclone/lib/atexit" "github.com/rclone/rclone/vfs" "github.com/rclone/rclone/vfs/vfsflags" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) -// Options set by command line flags -var ( - DebugFUSE = false - AllowNonEmpty = false - AllowRoot = false - AllowOther = false - DefaultPermissions = false - WritebackCache = false - Daemon = false - MaxReadAhead fs.SizeSuffix = 128 * 1024 +// Options for creating the mount +type Options struct { + DebugFUSE bool + AllowNonEmpty bool + AllowRoot bool + AllowOther bool + DefaultPermissions bool + WritebackCache bool + Daemon bool + MaxReadAhead fs.SizeSuffix ExtraOptions []string ExtraFlags []string - AttrTimeout = 1 * time.Second // how long the kernel caches attribute for + AttrTimeout time.Duration // how long the kernel caches attribute for VolumeName string - NoAppleDouble = true // use noappledouble by default - NoAppleXattr = false // do not use noapplexattr by default + NoAppleDouble bool + NoAppleXattr bool DaemonTimeout time.Duration // OSXFUSE only - AsyncRead = true // do async reads by default -) + AsyncRead bool +} + +// DefaultOpt is the default values for creating the mount +var DefaultOpt = Options{ + MaxReadAhead: 128 * 1024, + AttrTimeout: 1 * time.Second, // how long the kernel caches attribute for + NoAppleDouble: true, // use noappledouble by default + NoAppleXattr: false, // do not use noapplexattr by default + AsyncRead: true, // do async reads by default +} type ( // UnmountFn is called to unmount the file system UnmountFn func() error // MountFn is called to mount the file system - MountFn func(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) + MountFn func(VFS *vfs.VFS, mountpoint string, opt *Options) (<-chan error, func() error, error) ) // Global constants @@ -58,7 +69,35 @@ const ( func init() { // DaemonTimeout defaults to non zero for macOS if runtime.GOOS == "darwin" { - DaemonTimeout = 15 * time.Minute + DefaultOpt.DaemonTimeout = 15 * time.Minute + } +} + +// Options set by command line flags +var ( + Opt = DefaultOpt +) + +// AddFlags adds the non filing system specific flags to the command +func AddFlags(flagSet *pflag.FlagSet) { + rc.AddOption("mount", &Opt) + flags.BoolVarP(flagSet, &Opt.DebugFUSE, "debug-fuse", "", Opt.DebugFUSE, "Debug the FUSE internals - needs -v.") + flags.BoolVarP(flagSet, &Opt.AllowNonEmpty, "allow-non-empty", "", Opt.AllowNonEmpty, "Allow mounting over a non-empty directory (not Windows).") + flags.BoolVarP(flagSet, &Opt.AllowRoot, "allow-root", "", Opt.AllowRoot, "Allow access to root user.") + flags.BoolVarP(flagSet, &Opt.AllowOther, "allow-other", "", Opt.AllowOther, "Allow access to other users.") + flags.BoolVarP(flagSet, &Opt.DefaultPermissions, "default-permissions", "", Opt.DefaultPermissions, "Makes kernel enforce access control based on the file mode.") + flags.BoolVarP(flagSet, &Opt.WritebackCache, "write-back-cache", "", Opt.WritebackCache, "Makes kernel buffer writes before sending them to rclone. Without this, writethrough caching is used.") + flags.FVarP(flagSet, &Opt.MaxReadAhead, "max-read-ahead", "", "The number of bytes that can be prefetched for sequential reads.") + flags.DurationVarP(flagSet, &Opt.AttrTimeout, "attr-timeout", "", Opt.AttrTimeout, "Time for which file/directory attributes are cached.") + flags.StringArrayVarP(flagSet, &Opt.ExtraOptions, "option", "o", []string{}, "Option for libfuse/WinFsp. Repeat if required.") + flags.StringArrayVarP(flagSet, &Opt.ExtraFlags, "fuse-flag", "", []string{}, "Flags or arguments to be passed direct to libfuse/WinFsp. Repeat if required.") + flags.BoolVarP(flagSet, &Opt.Daemon, "daemon", "", Opt.Daemon, "Run mount as a daemon (background mode).") + flags.StringVarP(flagSet, &Opt.VolumeName, "volname", "", Opt.VolumeName, "Set the volume name (not supported by all OSes).") + flags.DurationVarP(flagSet, &Opt.DaemonTimeout, "daemon-timeout", "", Opt.DaemonTimeout, "Time limit for rclone to respond to kernel (not supported by all OSes).") + flags.BoolVarP(flagSet, &Opt.AsyncRead, "async-read", "", Opt.AsyncRead, "Use asynchronous reads.") + if runtime.GOOS == "darwin" { + flags.BoolVarP(flagSet, &Opt.NoAppleDouble, "noappledouble", "", Opt.NoAppleDouble, "Sets the OSXFUSE option noappledouble.") + flags.BoolVarP(flagSet, &Opt.NoAppleXattr, "noapplexattr", "", Opt.NoAppleXattr, "Sets the OSXFUSE option noapplexattr.") } } @@ -301,7 +340,7 @@ be copied to the vfs cache before opening with --vfs-cache-mode full. Run: func(command *cobra.Command, args []string) { cmd.CheckArgs(2, 2, command, args) - if Daemon { + if Opt.Daemon { config.PassConfigKeyForDaemonization = true } @@ -321,17 +360,18 @@ be copied to the vfs cache before opening with --vfs-cache-mode full. // Skip checkMountEmpty if --allow-non-empty flag is used or if // the Operating System is Windows - if !AllowNonEmpty && runtime.GOOS != "windows" { + if !Opt.AllowNonEmpty && runtime.GOOS != "windows" { err := checkMountEmpty(mountpoint) if err != nil { log.Fatalf("Fatal error: %v", err) } - } else if AllowNonEmpty && runtime.GOOS == "windows" { + } else if Opt.AllowNonEmpty && runtime.GOOS == "windows" { fs.Logf(nil, "--allow-non-empty flag does nothing on Windows") } // Work out the volume name, removing special // characters from it if necessary + VolumeName := Opt.VolumeName if VolumeName == "" { VolumeName = fdst.Name() + ":" + fdst.Root() } @@ -343,7 +383,7 @@ be copied to the vfs cache before opening with --vfs-cache-mode full. } // Start background task if --background is specified - if Daemon { + if Opt.Daemon { daemonized := startBackgroundMode() if daemonized { return @@ -351,7 +391,7 @@ be copied to the vfs cache before opening with --vfs-cache-mode full. } VFS := vfs.New(fdst, &vfsflags.Opt) - err := Mount(VFS, mountpoint, mount) + err := Mount(VFS, mountpoint, mount, &Opt) if err != nil { log.Fatalf("Fatal error: %v", err) } @@ -363,28 +403,7 @@ be copied to the vfs cache before opening with --vfs-cache-mode full. // Add flags cmdFlags := commandDefinition.Flags() - flags.BoolVarP(cmdFlags, &DebugFUSE, "debug-fuse", "", DebugFUSE, "Debug the FUSE internals - needs -v.") - // mount options - flags.BoolVarP(cmdFlags, &AllowNonEmpty, "allow-non-empty", "", AllowNonEmpty, "Allow mounting over a non-empty directory (not Windows).") - flags.BoolVarP(cmdFlags, &AllowRoot, "allow-root", "", AllowRoot, "Allow access to root user.") - flags.BoolVarP(cmdFlags, &AllowOther, "allow-other", "", AllowOther, "Allow access to other users.") - flags.BoolVarP(cmdFlags, &DefaultPermissions, "default-permissions", "", DefaultPermissions, "Makes kernel enforce access control based on the file mode.") - flags.BoolVarP(cmdFlags, &WritebackCache, "write-back-cache", "", WritebackCache, "Makes kernel buffer writes before sending them to rclone. Without this, writethrough caching is used.") - flags.FVarP(cmdFlags, &MaxReadAhead, "max-read-ahead", "", "The number of bytes that can be prefetched for sequential reads.") - flags.DurationVarP(cmdFlags, &AttrTimeout, "attr-timeout", "", AttrTimeout, "Time for which file/directory attributes are cached.") - flags.StringArrayVarP(cmdFlags, &ExtraOptions, "option", "o", []string{}, "Option for libfuse/WinFsp. Repeat if required.") - flags.StringArrayVarP(cmdFlags, &ExtraFlags, "fuse-flag", "", []string{}, "Flags or arguments to be passed direct to libfuse/WinFsp. Repeat if required.") - flags.BoolVarP(cmdFlags, &Daemon, "daemon", "", Daemon, "Run mount as a daemon (background mode).") - flags.StringVarP(cmdFlags, &VolumeName, "volname", "", VolumeName, "Set the volume name (not supported by all OSes).") - flags.DurationVarP(cmdFlags, &DaemonTimeout, "daemon-timeout", "", DaemonTimeout, "Time limit for rclone to respond to kernel (not supported by all OSes).") - flags.BoolVarP(cmdFlags, &AsyncRead, "async-read", "", AsyncRead, "Use asynchronous reads.") - - if runtime.GOOS == "darwin" { - flags.BoolVarP(cmdFlags, &NoAppleDouble, "noappledouble", "", NoAppleDouble, "Sets the OSXFUSE option noappledouble.") - flags.BoolVarP(cmdFlags, &NoAppleXattr, "noapplexattr", "", NoAppleXattr, "Sets the OSXFUSE option noapplexattr.") - } - - // Add in the generic flags + AddFlags(cmdFlags) vfsflags.AddFlags(cmdFlags) return commandDefinition @@ -416,9 +435,13 @@ func ClipBlocks(b *uint64) { // Mount mounts the remote at mountpoint. // // If noModTime is set then it -func Mount(VFS *vfs.VFS, mountpoint string, mount MountFn) error { +func Mount(VFS *vfs.VFS, mountpoint string, mount MountFn, opt *Options) error { + if opt != nil { + opt = &DefaultOpt + } + // Mount it - errChan, unmount, err := mount(VFS, mountpoint) + errChan, unmount, err := mount(VFS, mountpoint, opt) if err != nil { return errors.Wrap(err, "failed to mount FUSE fs") } diff --git a/cmd/mountlib/rc.go b/cmd/mountlib/rc.go index 9876fa82f..34128d8d1 100644 --- a/cmd/mountlib/rc.go +++ b/cmd/mountlib/rc.go @@ -107,7 +107,7 @@ func mountRc(_ context.Context, in rc.Params) (out rc.Params, err error) { if mountFns[mountType] != nil { VFS := vfs.New(fdst, &vfsOpt) - _, unmountFn, err := mountFns[mountType](VFS, mountPoint) + _, unmountFn, err := mountFns[mountType](VFS, mountPoint, &Opt) if err != nil { log.Printf("mount FAILED: %v", err) diff --git a/vfs/vfstest/fs.go b/vfs/vfstest/fs.go index cd90e199d..fefd52d41 100644 --- a/vfs/vfstest/fs.go +++ b/vfs/vfstest/fs.go @@ -20,6 +20,7 @@ import ( "time" _ "github.com/rclone/rclone/backend/all" // import all the backends + "github.com/rclone/rclone/cmd/mountlib" "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/walk" "github.com/rclone/rclone/fstest" @@ -30,21 +31,14 @@ import ( "github.com/stretchr/testify/require" ) -type ( - // UnmountFn is called to unmount the file system - UnmountFn func() error - // MountFn is called to mount the file system - MountFn func(VFS *vfs.VFS, mountpoint string) (unmountResult <-chan error, unmount func() error, err error) -) - var ( - mountFn MountFn + mountFn mountlib.MountFn ) // RunTests runs all the tests against all the VFS cache modes // // If useVFS is set then it runs the tests against a VFS rather than amount -func RunTests(t *testing.T, useVFS bool, fn MountFn) { +func RunTests(t *testing.T, useVFS bool, fn mountlib.MountFn) { mountFn = fn flag.Parse() tests := []struct { @@ -111,7 +105,7 @@ type Run struct { fremoteName string cleanRemote func() umountResult <-chan error - umountFn UnmountFn + umountFn mountlib.UnmountFn skip bool } @@ -178,7 +172,7 @@ func (r *Run) mount() { log.Printf("mount %q %q", r.fremote, r.mountPath) var err error r.vfs = vfs.New(r.fremote, &vfsflags.Opt) - r.umountResult, r.umountFn, err = mountFn(r.vfs, r.mountPath) + r.umountResult, r.umountFn, err = mountFn(r.vfs, r.mountPath, &mountlib.Opt) if err != nil { log.Printf("mount FAILED: %v", err) r.skip = true diff --git a/vfs/vfstest_test.go b/vfs/vfstest_test.go index fbf17f3b2..babcdb4ff 100644 --- a/vfs/vfstest_test.go +++ b/vfs/vfstest_test.go @@ -6,6 +6,7 @@ import ( "testing" _ "github.com/rclone/rclone/backend/all" // import all the backends + "github.com/rclone/rclone/cmd/mountlib" "github.com/rclone/rclone/fstest" "github.com/rclone/rclone/vfs" "github.com/rclone/rclone/vfs/vfstest" @@ -17,7 +18,7 @@ func TestFunctional(t *testing.T) { if *fstest.RemoteName != "" { t.Skip("Skip on non local") } - vfstest.RunTests(t, true, func(VFS *vfs.VFS, mountpoint string) (unmountResult <-chan error, unmount func() error, err error) { + vfstest.RunTests(t, true, func(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (unmountResult <-chan error, unmount func() error, err error) { unmountResultChan := make(chan (error), 1) unmount = func() error { unmountResultChan <- nil