mount: change interface of mount commands to take VFS

This is in preparation of being able to pass options to the rc command
"mount/mount"
This commit is contained in:
Nick Craig-Wood 2020-07-22 17:58:49 +01:00
parent 744828a4de
commit 2871268505
12 changed files with 72 additions and 62 deletions

View File

@ -13,6 +13,8 @@ import (
"github.com/rclone/rclone/cmd/mount" "github.com/rclone/rclone/cmd/mount"
"github.com/rclone/rclone/cmd/mountlib" "github.com/rclone/rclone/cmd/mountlib"
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/vfs"
"github.com/rclone/rclone/vfs/vfsflags"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -30,7 +32,8 @@ func (r *run) mountFs(t *testing.T, f fs.Fs) {
require.NoError(t, err) require.NoError(t, err)
c, err := fuse.Mount(r.mntDir, options...) c, err := fuse.Mount(r.mntDir, options...)
require.NoError(t, err) require.NoError(t, err)
filesys := mount.NewFS(f) VFS := vfs.New(f, &vfsflags.Opt)
filesys := mount.NewFS(VFS)
server := fusefs.New(c, nil) server := fusefs.New(c, nil)
// Serve the mount point in the background returning error to errChan // Serve the mount point in the background returning error to errChan

View File

@ -14,6 +14,8 @@ import (
"github.com/rclone/rclone/cmd/cmount" "github.com/rclone/rclone/cmd/cmount"
"github.com/rclone/rclone/cmd/mountlib" "github.com/rclone/rclone/cmd/mountlib"
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/vfs"
"github.com/rclone/rclone/vfs/vfsflags"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -51,7 +53,8 @@ func (r *run) mountFs(t *testing.T, f fs.Fs) {
"--FileSystemName=rclone", "--FileSystemName=rclone",
} }
fsys := cmount.NewFS(f) VFS := vfs.New(f, &vfsflags.Opt)
fsys := cmount.NewFS(VFS)
host := fuse.NewFileSystemHost(fsys) host := fuse.NewFileSystemHost(fsys)
// Serve the mount point in the background returning error to errChan // Serve the mount point in the background returning error to errChan

View File

@ -17,7 +17,6 @@ import (
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/log" "github.com/rclone/rclone/fs/log"
"github.com/rclone/rclone/vfs" "github.com/rclone/rclone/vfs"
"github.com/rclone/rclone/vfs/vfsflags"
) )
const fhUnset = ^uint64(0) const fhUnset = ^uint64(0)
@ -32,10 +31,10 @@ type FS struct {
} }
// NewFS makes a new FS // NewFS makes a new FS
func NewFS(f fs.Fs) *FS { func NewFS(VFS *vfs.VFS) *FS {
fsys := &FS{ fsys := &FS{
VFS: vfs.New(f, &vfsflags.Opt), VFS: VFS,
f: f, f: VFS.Fs(),
ready: make(chan (struct{})), ready: make(chan (struct{})),
} }
return fsys return fsys

View File

@ -23,7 +23,6 @@ import (
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/lib/atexit" "github.com/rclone/rclone/lib/atexit"
"github.com/rclone/rclone/vfs" "github.com/rclone/rclone/vfs"
"github.com/rclone/rclone/vfs/vfsflags"
) )
const ( const (
@ -45,7 +44,7 @@ func init() {
} }
// mountOptions configures the options from the command line flags // mountOptions configures the options from the command line flags
func mountOptions(device string, mountpoint string) (options []string) { func mountOptions(VFS *vfs.VFS, device string, mountpoint string) (options []string) {
// Options // Options
options = []string{ options = []string{
"-o", "fsname=" + device, "-o", "fsname=" + device,
@ -97,7 +96,7 @@ func mountOptions(device string, mountpoint string) (options []string) {
if mountlib.DefaultPermissions { if mountlib.DefaultPermissions {
options = append(options, "-o", "default_permissions") options = append(options, "-o", "default_permissions")
} }
if vfsflags.Opt.ReadOnly { if VFS.Opt.ReadOnly {
options = append(options, "-o", "ro") options = append(options, "-o", "ro")
} }
if mountlib.WritebackCache { if mountlib.WritebackCache {
@ -135,22 +134,23 @@ func waitFor(fn func() bool) (ok bool) {
// //
// returns an error, and an error channel for the serve process to // returns an error, and an error channel for the serve process to
// report an error when fusermount is called. // report an error when fusermount is called.
func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, error) { func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) {
f := VFS.Fs()
fs.Debugf(f, "Mounting on %q", mountpoint) fs.Debugf(f, "Mounting on %q", mountpoint)
// Check the mountpoint - in Windows the mountpoint mustn't exist before the mount // Check the mountpoint - in Windows the mountpoint mustn't exist before the mount
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
fi, err := os.Stat(mountpoint) fi, err := os.Stat(mountpoint)
if err != nil { if err != nil {
return nil, nil, nil, errors.Wrap(err, "mountpoint") return nil, nil, errors.Wrap(err, "mountpoint")
} }
if !fi.IsDir() { if !fi.IsDir() {
return nil, nil, nil, errors.New("mountpoint is not a directory") return nil, nil, errors.New("mountpoint is not a directory")
} }
} }
// Create underlying FS // Create underlying FS
fsys := NewFS(f) fsys := NewFS(VFS)
host := fuse.NewFileSystemHost(fsys) host := fuse.NewFileSystemHost(fsys)
if usingReaddirPlus { if usingReaddirPlus {
host.SetCapReaddirPlus(true) host.SetCapReaddirPlus(true)
@ -158,7 +158,7 @@ func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, er
host.SetCapCaseInsensitive(f.Features().CaseInsensitive) host.SetCapCaseInsensitive(f.Features().CaseInsensitive)
// Create options // Create options
options := mountOptions(f.Name()+":"+f.Root(), mountpoint) options := mountOptions(VFS, f.Name()+":"+f.Root(), mountpoint)
fs.Debugf(f, "Mounting with options: %q", options) fs.Debugf(f, "Mounting with options: %q", options)
// Serve the mount point in the background returning error to errChan // Serve the mount point in the background returning error to errChan
@ -199,7 +199,7 @@ func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, er
select { select {
case err := <-errChan: case err := <-errChan:
err = errors.Wrap(err, "mount stopped before calling Init") err = errors.Wrap(err, "mount stopped before calling Init")
return nil, nil, nil, err return nil, nil, err
case <-fsys.ready: case <-fsys.ready:
} }
@ -214,15 +214,15 @@ func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, er
} }
} }
return fsys.VFS, errChan, unmount, nil return errChan, unmount, nil
} }
// Mount mounts the remote at mountpoint. // Mount mounts the remote at mountpoint.
// //
// If noModTime is set then it // If noModTime is set then it
func Mount(f fs.Fs, mountpoint string) error { func Mount(VFS *vfs.VFS, mountpoint string) error {
// Mount it // Mount it
FS, errChan, unmount, err := mount(f, mountpoint) errChan, unmount, err := mount(VFS, mountpoint)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to mount FUSE fs") return errors.Wrap(err, "failed to mount FUSE fs")
} }
@ -248,9 +248,9 @@ waitloop:
break waitloop break waitloop
// user sent SIGHUP to clear the cache // user sent SIGHUP to clear the cache
case <-sigHup: case <-sigHup:
root, err := FS.Root() root, err := VFS.Root()
if err != nil { if err != nil {
fs.Errorf(f, "Error reading root: %v", err) fs.Errorf(VFS.Fs(), "Error reading root: %v", err)
} else { } else {
root.ForgetAll() root.ForgetAll()
} }

View File

@ -15,7 +15,6 @@ import (
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/log" "github.com/rclone/rclone/fs/log"
"github.com/rclone/rclone/vfs" "github.com/rclone/rclone/vfs"
"github.com/rclone/rclone/vfs/vfsflags"
) )
// FS represents the top level filing system // FS represents the top level filing system
@ -28,10 +27,10 @@ type FS struct {
var _ fusefs.FS = (*FS)(nil) var _ fusefs.FS = (*FS)(nil)
// NewFS makes a new FS // NewFS makes a new FS
func NewFS(f fs.Fs) *FS { func NewFS(VFS *vfs.VFS) *FS {
fsys := &FS{ fsys := &FS{
VFS: vfs.New(f, &vfsflags.Opt), VFS: VFS,
f: f, f: VFS.Fs(),
} }
return fsys return fsys
} }

View File

@ -18,7 +18,6 @@ import (
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/lib/atexit" "github.com/rclone/rclone/lib/atexit"
"github.com/rclone/rclone/vfs" "github.com/rclone/rclone/vfs"
"github.com/rclone/rclone/vfs/vfsflags"
) )
func init() { func init() {
@ -28,7 +27,7 @@ func init() {
} }
// mountOptions configures the options from the command line flags // mountOptions configures the options from the command line flags
func mountOptions(device string) (options []fuse.MountOption) { func mountOptions(VFS *vfs.VFS, device string) (options []fuse.MountOption) {
options = []fuse.MountOption{ options = []fuse.MountOption{
fuse.MaxReadahead(uint32(mountlib.MaxReadAhead)), fuse.MaxReadahead(uint32(mountlib.MaxReadAhead)),
fuse.Subtype("rclone"), fuse.Subtype("rclone"),
@ -61,7 +60,7 @@ func mountOptions(device string) (options []fuse.MountOption) {
if mountlib.DefaultPermissions { if mountlib.DefaultPermissions {
options = append(options, fuse.DefaultPermissions()) options = append(options, fuse.DefaultPermissions())
} }
if vfsflags.Opt.ReadOnly { if VFS.Opt.ReadOnly {
options = append(options, fuse.ReadOnly()) options = append(options, fuse.ReadOnly())
} }
if mountlib.WritebackCache { if mountlib.WritebackCache {
@ -85,14 +84,15 @@ func mountOptions(device string) (options []fuse.MountOption) {
// //
// returns an error, and an error channel for the serve process to // returns an error, and an error channel for the serve process to
// report an error when fusermount is called. // report an error when fusermount is called.
func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, error) { func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) {
f := VFS.Fs()
fs.Debugf(f, "Mounting on %q", mountpoint) fs.Debugf(f, "Mounting on %q", mountpoint)
c, err := fuse.Mount(mountpoint, mountOptions(f.Name()+":"+f.Root())...) c, err := fuse.Mount(mountpoint, mountOptions(VFS, f.Name()+":"+f.Root())...)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, err
} }
filesys := NewFS(f) filesys := NewFS(VFS)
server := fusefs.New(c, nil) server := fusefs.New(c, nil)
// Serve the mount point in the background returning error to errChan // Serve the mount point in the background returning error to errChan
@ -109,7 +109,7 @@ func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, er
// check if the mount process has an error to report // check if the mount process has an error to report
<-c.Ready <-c.Ready
if err := c.MountError; err != nil { if err := c.MountError; err != nil {
return nil, nil, nil, err return nil, nil, err
} }
unmount := func() error { unmount := func() error {
@ -118,13 +118,13 @@ func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, er
return fuse.Unmount(mountpoint) return fuse.Unmount(mountpoint)
} }
return filesys.VFS, errChan, unmount, nil return errChan, unmount, nil
} }
// Mount mounts the remote at mountpoint. // Mount mounts the remote at mountpoint.
// //
// If noModTime is set then it // If noModTime is set then it
func Mount(f fs.Fs, mountpoint string) error { func Mount(VFS *vfs.VFS, mountpoint string) error {
if mountlib.DebugFUSE { if mountlib.DebugFUSE {
fuse.Debug = func(msg interface{}) { fuse.Debug = func(msg interface{}) {
fs.Debugf("fuse", "%v", msg) fs.Debugf("fuse", "%v", msg)
@ -132,7 +132,7 @@ func Mount(f fs.Fs, mountpoint string) error {
} }
// Mount it // Mount it
FS, errChan, unmount, err := mount(f, mountpoint) errChan, unmount, err := mount(VFS, mountpoint)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to mount FUSE fs") return errors.Wrap(err, "failed to mount FUSE fs")
} }
@ -162,9 +162,9 @@ waitloop:
break waitloop break waitloop
// user sent SIGHUP to clear the cache // user sent SIGHUP to clear the cache
case <-sigHup: case <-sigHup:
root, err := FS.Root() root, err := VFS.Root()
if err != nil { if err != nil {
fs.Errorf(f, "Error reading root: %v", err) fs.Errorf(VFS.Fs(), "Error reading root: %v", err)
} else { } else {
root.ForgetAll() root.ForgetAll()
} }

View File

@ -14,7 +14,6 @@ import (
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/log" "github.com/rclone/rclone/fs/log"
"github.com/rclone/rclone/vfs" "github.com/rclone/rclone/vfs"
"github.com/rclone/rclone/vfs/vfsflags"
) )
// FS represents the top level filing system // FS represents the top level filing system
@ -24,10 +23,10 @@ type FS struct {
} }
// NewFS creates a pathfs.FileSystem from the fs.Fs passed in // NewFS creates a pathfs.FileSystem from the fs.Fs passed in
func NewFS(f fs.Fs) *FS { func NewFS(VFS *vfs.VFS) *FS {
fsys := &FS{ fsys := &FS{
VFS: vfs.New(f, &vfsflags.Opt), VFS: VFS,
f: f, f: VFS.Fs(),
} }
return fsys return fsys
} }

View File

@ -156,10 +156,11 @@ func mountOptions(fsys *FS, f fs.Fs) (mountOpts *fuse.MountOptions) {
// //
// returns an error, and an error channel for the serve process to // returns an error, and an error channel for the serve process to
// report an error when fusermount is called. // report an error when fusermount is called.
func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, error) { func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) {
f := VFS.Fs()
fs.Debugf(f, "Mounting on %q", mountpoint) fs.Debugf(f, "Mounting on %q", mountpoint)
fsys := NewFS(f) fsys := NewFS(VFS)
// nodeFsOpts := &fusefs.PathNodeFsOptions{ // nodeFsOpts := &fusefs.PathNodeFsOptions{
// ClientInodes: false, // ClientInodes: false,
// Debug: mountlib.DebugFUSE, // Debug: mountlib.DebugFUSE,
@ -187,20 +188,20 @@ func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, er
root, err := fsys.Root() root, err := fsys.Root()
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, err
} }
rawFS := fusefs.NewNodeFS(root, &opts) rawFS := fusefs.NewNodeFS(root, &opts)
server, err := fuse.NewServer(rawFS, mountpoint, &opts.MountOptions) server, err := fuse.NewServer(rawFS, mountpoint, &opts.MountOptions)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, err
} }
//mountOpts := &fuse.MountOptions{} //mountOpts := &fuse.MountOptions{}
//server, err := fusefs.Mount(mountpoint, fsys, &opts) //server, err := fusefs.Mount(mountpoint, fsys, &opts)
// server, err := fusefs.Mount(mountpoint, root, &opts) // server, err := fusefs.Mount(mountpoint, root, &opts)
// if err != nil { // if err != nil {
// return nil, nil, nil, err // return nil, nil, err
// } // }
umount := func() error { umount := func() error {
@ -222,19 +223,19 @@ func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, er
fs.Debugf(f, "Waiting for the mount to start...") fs.Debugf(f, "Waiting for the mount to start...")
err = server.WaitMount() err = server.WaitMount()
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, err
} }
fs.Debugf(f, "Mount started") fs.Debugf(f, "Mount started")
return fsys.VFS, errs, umount, nil return errs, umount, nil
} }
// Mount mounts the remote at mountpoint. // Mount mounts the remote at mountpoint.
// //
// If noModTime is set then it // If noModTime is set then it
func Mount(f fs.Fs, mountpoint string) error { func Mount(VFS *vfs.VFS, mountpoint string) error {
// Mount it // Mount it
vfs, errChan, unmount, err := mount(f, mountpoint) errChan, unmount, err := mount(VFS, mountpoint)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to mount FUSE fs") return errors.Wrap(err, "failed to mount FUSE fs")
} }
@ -263,9 +264,9 @@ waitloop:
break waitloop break waitloop
// user sent SIGHUP to clear the cache // user sent SIGHUP to clear the cache
case <-sigHup: case <-sigHup:
root, err := vfs.Root() root, err := VFS.Root()
if err != nil { if err != nil {
fs.Errorf(f, "Error reading root: %v", err) fs.Errorf(VFS.Fs(), "Error reading root: %v", err)
} else { } else {
root.ForgetAll() root.ForgetAll()
} }

View File

@ -43,7 +43,9 @@ type (
// UnmountFn is called to unmount the file system // UnmountFn is called to unmount the file system
UnmountFn func() error UnmountFn func() error
// MountFn is called to mount the file system // MountFn is called to mount the file system
MountFn func(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, error) MountFn func(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error)
// MountBlockingFn is called to mount the file system and block
MountBlockingFn func(VFS *vfs.VFS, mountpoint string) error
) )
// Global constants // Global constants
@ -106,7 +108,7 @@ func checkMountpointOverlap(root, mountpoint string) error {
} }
// NewMountCommand makes a mount command with the given name and Mount function // NewMountCommand makes a mount command with the given name and Mount function
func NewMountCommand(commandName string, hidden bool, Mount func(f fs.Fs, mountpoint string) error) *cobra.Command { func NewMountCommand(commandName string, hidden bool, Mount MountBlockingFn) *cobra.Command {
var commandDefinition = &cobra.Command{ var commandDefinition = &cobra.Command{
Use: commandName + " remote:path /path/to/mountpoint", Use: commandName + " remote:path /path/to/mountpoint",
Hidden: hidden, Hidden: hidden,
@ -346,7 +348,8 @@ be copied to the vfs cache before opening with --vfs-cache-mode full.
} }
} }
err := Mount(fdst, mountpoint) VFS := vfs.New(fdst, &vfsflags.Opt)
err := Mount(VFS, mountpoint)
if err != nil { if err != nil {
log.Fatalf("Fatal error: %v", err) log.Fatalf("Fatal error: %v", err)
} }

View File

@ -10,6 +10,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/rc" "github.com/rclone/rclone/fs/rc"
"github.com/rclone/rclone/vfs"
"github.com/rclone/rclone/vfs/vfsflags"
) )
// MountInfo defines the configuration for a mount // MountInfo defines the configuration for a mount
@ -91,7 +93,8 @@ func mountRc(_ context.Context, in rc.Params) (out rc.Params, err error) {
} }
if mountFns[mountType] != nil { if mountFns[mountType] != nil {
_, _, unmountFn, err := mountFns[mountType](fdst, mountPoint) VFS := vfs.New(fdst, &vfsflags.Opt)
_, unmountFn, err := mountFns[mountType](VFS, mountPoint)
if err != nil { if err != nil {
log.Printf("mount FAILED: %v", err) log.Printf("mount FAILED: %v", err)

View File

@ -25,6 +25,7 @@ import (
"github.com/rclone/rclone/fstest" "github.com/rclone/rclone/fstest"
"github.com/rclone/rclone/vfs" "github.com/rclone/rclone/vfs"
"github.com/rclone/rclone/vfs/vfscommon" "github.com/rclone/rclone/vfs/vfscommon"
"github.com/rclone/rclone/vfs/vfsflags"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -33,7 +34,7 @@ type (
// UnmountFn is called to unmount the file system // UnmountFn is called to unmount the file system
UnmountFn func() error UnmountFn func() error
// MountFn is called to mount the file system // MountFn is called to mount the file system
MountFn func(f fs.Fs, mountpoint string) (vfs *vfs.VFS, unmountResult <-chan error, unmount func() error, err error) MountFn func(VFS *vfs.VFS, mountpoint string) (unmountResult <-chan error, unmount func() error, err error)
) )
var ( var (
@ -176,7 +177,8 @@ found:
func (r *Run) mount() { func (r *Run) mount() {
log.Printf("mount %q %q", r.fremote, r.mountPath) log.Printf("mount %q %q", r.fremote, r.mountPath)
var err error var err error
r.vfs, r.umountResult, r.umountFn, err = mountFn(r.fremote, r.mountPath) r.vfs = vfs.New(r.fremote, &vfsflags.Opt)
r.umountResult, r.umountFn, err = mountFn(r.vfs, r.mountPath)
if err != nil { if err != nil {
log.Printf("mount FAILED: %v", err) log.Printf("mount FAILED: %v", err)
r.skip = true r.skip = true

View File

@ -6,7 +6,6 @@ import (
"testing" "testing"
_ "github.com/rclone/rclone/backend/all" // import all the backends _ "github.com/rclone/rclone/backend/all" // import all the backends
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fstest" "github.com/rclone/rclone/fstest"
"github.com/rclone/rclone/vfs" "github.com/rclone/rclone/vfs"
"github.com/rclone/rclone/vfs/vfstest" "github.com/rclone/rclone/vfs/vfstest"
@ -18,13 +17,12 @@ func TestFunctional(t *testing.T) {
if *fstest.RemoteName != "" { if *fstest.RemoteName != "" {
t.Skip("Skip on non local") t.Skip("Skip on non local")
} }
vfstest.RunTests(t, true, func(f fs.Fs, mountpoint string) (VFS *vfs.VFS, unmountResult <-chan error, unmount func() error, err error) { vfstest.RunTests(t, true, func(VFS *vfs.VFS, mountpoint string) (unmountResult <-chan error, unmount func() error, err error) {
unmountResultChan := make(chan (error), 1) unmountResultChan := make(chan (error), 1)
unmount = func() error { unmount = func() error {
unmountResultChan <- nil unmountResultChan <- nil
return nil return nil
} }
VFS = vfs.New(f, nil) return unmountResultChan, unmount, nil
return VFS, unmountResultChan, unmount, nil
}) })
} }