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"
This commit is contained in:
Nick Craig-Wood 2020-07-23 17:17:01 +01:00
parent e1d34ef427
commit 0272a7f405
15 changed files with 166 additions and 141 deletions

View File

@ -21,7 +21,7 @@ import (
func (r *run) mountFs(t *testing.T, f fs.Fs) { func (r *run) mountFs(t *testing.T, f fs.Fs) {
device := f.Name() + ":" + f.Root() device := f.Name() + ":" + f.Root()
var options = []fuse.MountOption{ var options = []fuse.MountOption{
fuse.MaxReadahead(uint32(mountlib.MaxReadAhead)), fuse.MaxReadahead(uint32(mountlib.Opt.MaxReadAhead)),
fuse.Subtype("rclone"), fuse.Subtype("rclone"),
fuse.FSName(device), fuse.VolumeName(device), fuse.FSName(device), fuse.VolumeName(device),
fuse.NoAppleDouble(), fuse.NoAppleDouble(),
@ -33,7 +33,7 @@ func (r *run) mountFs(t *testing.T, f fs.Fs) {
c, err := fuse.Mount(r.mntDir, options...) c, err := fuse.Mount(r.mntDir, options...)
require.NoError(t, err) require.NoError(t, err)
VFS := vfs.New(f, &vfsflags.Opt) VFS := vfs.New(f, &vfsflags.Opt)
filesys := mount.NewFS(VFS) filesys := mount.NewFS(VFS, &mountlib.Opt)
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

@ -41,7 +41,7 @@ func (r *run) mountFs(t *testing.T, f fs.Fs) {
options := []string{ options := []string{
"-o", "fsname=" + device, "-o", "fsname=" + device,
"-o", "subtype=rclone", "-o", "subtype=rclone",
"-o", fmt.Sprintf("max_readahead=%d", mountlib.MaxReadAhead), "-o", fmt.Sprintf("max_readahead=%d", mountlib.Opt.MaxReadAhead),
"-o", "uid=-1", "-o", "uid=-1",
"-o", "gid=-1", "-o", "gid=-1",
"-o", "allow_other", "-o", "allow_other",

View File

@ -38,29 +38,29 @@ func init() {
} }
// mountOptions configures the options from the command line flags // 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
options = []string{ options = []string{
"-o", "fsname=" + device, "-o", "fsname=" + device,
"-o", "subtype=rclone", "-o", "subtype=rclone",
"-o", fmt.Sprintf("max_readahead=%d", mountlib.MaxReadAhead), "-o", fmt.Sprintf("max_readahead=%d", opt.MaxReadAhead),
"-o", fmt.Sprintf("attr_timeout=%g", mountlib.AttrTimeout.Seconds()), "-o", fmt.Sprintf("attr_timeout=%g", opt.AttrTimeout.Seconds()),
// This causes FUSE to supply O_TRUNC with the Open // This causes FUSE to supply O_TRUNC with the Open
// call which is more efficient for cmount. However // call which is more efficient for cmount. However
// it does not work with cgofuse on Windows with // it does not work with cgofuse on Windows with
// WinFSP so cmount must work with or without it. // WinFSP so cmount must work with or without it.
"-o", "atomic_o_trunc", "-o", "atomic_o_trunc",
} }
if mountlib.DebugFUSE { if opt.DebugFUSE {
options = append(options, "-o", "debug") options = append(options, "-o", "debug")
} }
// OSX options // OSX options
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
if mountlib.NoAppleDouble { if opt.NoAppleDouble {
options = append(options, "-o", "noappledouble") options = append(options, "-o", "noappledouble")
} }
if mountlib.NoAppleXattr { if opt.NoAppleXattr {
options = append(options, "-o", "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 runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
if mountlib.VolumeName != "" { if opt.VolumeName != "" {
options = append(options, "-o", "volname="+mountlib.VolumeName) options = append(options, "-o", "volname="+opt.VolumeName)
} }
} }
if mountlib.AllowNonEmpty { if opt.AllowNonEmpty {
options = append(options, "-o", "nonempty") options = append(options, "-o", "nonempty")
} }
if mountlib.AllowOther { if opt.AllowOther {
options = append(options, "-o", "allow_other") options = append(options, "-o", "allow_other")
} }
if mountlib.AllowRoot { if opt.AllowRoot {
options = append(options, "-o", "allow_root") options = append(options, "-o", "allow_root")
} }
if mountlib.DefaultPermissions { if opt.DefaultPermissions {
options = append(options, "-o", "default_permissions") options = append(options, "-o", "default_permissions")
} }
if VFS.Opt.ReadOnly { if VFS.Opt.ReadOnly {
options = append(options, "-o", "ro") options = append(options, "-o", "ro")
} }
if mountlib.WritebackCache { if opt.WritebackCache {
// FIXME? options = append(options, "-o", WritebackCache()) // FIXME? options = append(options, "-o", WritebackCache())
} }
if mountlib.DaemonTimeout != 0 { if opt.DaemonTimeout != 0 {
options = append(options, "-o", fmt.Sprintf("daemon_timeout=%d", int(mountlib.DaemonTimeout.Seconds()))) 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) options = append(options, "-o", option)
} }
for _, option := range mountlib.ExtraFlags { for _, option := range opt.ExtraFlags {
options = append(options, option) options = append(options, option)
} }
return options 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 // 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(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() f := VFS.Fs()
fs.Debugf(f, "Mounting on %q", mountpoint) 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) host.SetCapCaseInsensitive(f.Features().CaseInsensitive)
// Create options // 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) 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

View File

@ -19,6 +19,7 @@ import (
// Dir represents a directory entry // Dir represents a directory entry
type Dir struct { type Dir struct {
*vfs.Dir *vfs.Dir
fsys *FS
} }
// Check interface satisfied // Check interface satisfied
@ -27,7 +28,7 @@ var _ fusefs.Node = (*Dir)(nil)
// Attr updates the attributes of a directory // Attr updates the attributes of a directory
func (d *Dir) Attr(ctx context.Context, a *fuse.Attr) (err error) { func (d *Dir) Attr(ctx context.Context, a *fuse.Attr) (err error) {
defer log.Trace(d, "")("attr=%+v, err=%v", a, &err) 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.Gid = d.VFS().Opt.GID
a.Uid = d.VFS().Opt.UID a.Uid = d.VFS().Opt.UID
a.Mode = os.ModeDir | d.VFS().Opt.DirPerms 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 { if err != nil {
return nil, translateError(err) 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 // Check the mnode to see if it has a fuse Node cached
// We must return the same fuse nodes for vfs Nodes // We must return the same fuse nodes for vfs Nodes
node, ok := mnode.Sys().(fusefs.Node) 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) { switch x := mnode.(type) {
case *vfs.File: case *vfs.File:
node = &File{x} node = &File{x, d.fsys}
case *vfs.Dir: case *vfs.Dir:
node = &Dir{x} node = &Dir{x, d.fsys}
default: default:
panic("bad type") panic("bad type")
} }
@ -139,7 +140,7 @@ func (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.Cr
if err != nil { if err != nil {
return nil, nil, translateError(err) return nil, nil, translateError(err)
} }
node = &File{file} node = &File{file, d.fsys}
file.SetSys(node) // cache the FUSE node for later file.SetSys(node) // cache the FUSE node for later
return node, &FileHandle{fh}, err 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 { if err != nil {
return nil, translateError(err) return nil, translateError(err)
} }
node = &Dir{dir} node = &Dir{dir, d.fsys}
dir.SetSys(node) // cache the FUSE node for later dir.SetSys(node) // cache the FUSE node for later
return node, nil return node, nil
} }

View File

@ -8,7 +8,6 @@ import (
"bazil.org/fuse" "bazil.org/fuse"
fusefs "bazil.org/fuse/fs" fusefs "bazil.org/fuse/fs"
"github.com/rclone/rclone/cmd/mountlib"
"github.com/rclone/rclone/fs/log" "github.com/rclone/rclone/fs/log"
"github.com/rclone/rclone/vfs" "github.com/rclone/rclone/vfs"
) )
@ -16,6 +15,7 @@ import (
// File represents a file // File represents a file
type File struct { type File struct {
*vfs.File *vfs.File
fsys *FS
} }
// Check interface satisfied // Check interface satisfied
@ -24,7 +24,7 @@ var _ fusefs.Node = (*File)(nil)
// Attr fills out the attributes for the file // Attr fills out the attributes for the file
func (f *File) Attr(ctx context.Context, a *fuse.Attr) (err error) { func (f *File) Attr(ctx context.Context, a *fuse.Attr) (err error) {
defer log.Trace(f, "")("a=%+v, err=%v", a, &err) defer log.Trace(f, "")("a=%+v, err=%v", a, &err)
a.Valid = mountlib.AttrTimeout a.Valid = f.fsys.opt.AttrTimeout
modTime := f.File.ModTime() modTime := f.File.ModTime()
Size := uint64(f.File.Size()) Size := uint64(f.File.Size())
Blocks := (Size + 511) / 512 Blocks := (Size + 511) / 512

View File

@ -21,16 +21,18 @@ import (
type FS struct { type FS struct {
*vfs.VFS *vfs.VFS
f fs.Fs f fs.Fs
opt *mountlib.Options
} }
// Check interface satisfied // Check interface satisfied
var _ fusefs.FS = (*FS)(nil) var _ fusefs.FS = (*FS)(nil)
// NewFS makes a new FS // NewFS makes a new FS
func NewFS(VFS *vfs.VFS) *FS { func NewFS(VFS *vfs.VFS, opt *mountlib.Options) *FS {
fsys := &FS{ fsys := &FS{
VFS: VFS, VFS: VFS,
f: VFS.Fs(), f: VFS.Fs(),
opt: opt,
} }
return fsys return fsys
} }
@ -42,7 +44,7 @@ func (f *FS) Root() (node fusefs.Node, err error) {
if err != nil { if err != nil {
return nil, translateError(err) return nil, translateError(err)
} }
return &Dir{root}, nil return &Dir{root, f}, nil
} }
// Check interface satisfied // Check interface satisfied

View File

@ -20,52 +20,52 @@ func init() {
} }
// mountOptions configures the options from the command line flags // 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{ options = []fuse.MountOption{
fuse.MaxReadahead(uint32(mountlib.MaxReadAhead)), fuse.MaxReadahead(uint32(opt.MaxReadAhead)),
fuse.Subtype("rclone"), fuse.Subtype("rclone"),
fuse.FSName(device), fuse.FSName(device),
fuse.VolumeName(mountlib.VolumeName), fuse.VolumeName(opt.VolumeName),
// Options from benchmarking in the fuse module // Options from benchmarking in the fuse module
//fuse.MaxReadahead(64 * 1024 * 1024), //fuse.MaxReadahead(64 * 1024 * 1024),
//fuse.WritebackCache(), //fuse.WritebackCache(),
} }
if mountlib.AsyncRead { if opt.AsyncRead {
options = append(options, fuse.AsyncRead()) options = append(options, fuse.AsyncRead())
} }
if mountlib.NoAppleDouble { if opt.NoAppleDouble {
options = append(options, fuse.NoAppleDouble()) options = append(options, fuse.NoAppleDouble())
} }
if mountlib.NoAppleXattr { if opt.NoAppleXattr {
options = append(options, fuse.NoAppleXattr()) options = append(options, fuse.NoAppleXattr())
} }
if mountlib.AllowNonEmpty { if opt.AllowNonEmpty {
options = append(options, fuse.AllowNonEmptyMount()) options = append(options, fuse.AllowNonEmptyMount())
} }
if mountlib.AllowOther { if opt.AllowOther {
options = append(options, fuse.AllowOther()) options = append(options, fuse.AllowOther())
} }
if mountlib.AllowRoot { if opt.AllowRoot {
// options = append(options, fuse.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") 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()) options = append(options, fuse.DefaultPermissions())
} }
if VFS.Opt.ReadOnly { if VFS.Opt.ReadOnly {
options = append(options, fuse.ReadOnly()) options = append(options, fuse.ReadOnly())
} }
if mountlib.WritebackCache { if opt.WritebackCache {
options = append(options, fuse.WritebackCache()) options = append(options, fuse.WritebackCache())
} }
if mountlib.DaemonTimeout != 0 { if opt.DaemonTimeout != 0 {
options = append(options, fuse.DaemonTimeout(fmt.Sprint(int(mountlib.DaemonTimeout.Seconds())))) 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") 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") fs.Errorf(nil, "--fuse-flag not supported with this FUSE backend")
} }
return options 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 // 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(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) {
if mountlib.DebugFUSE { if opt.DebugFUSE {
fuse.Debug = func(msg interface{}) { fuse.Debug = func(msg interface{}) {
fs.Debugf("fuse", "%v", msg) fs.Debugf("fuse", "%v", msg)
} }
@ -86,12 +86,12 @@ func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error)
f := VFS.Fs() f := VFS.Fs()
fs.Debugf(f, "Mounting on %q", mountpoint) 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 { if err != nil {
return nil, nil, err return nil, nil, err
} }
filesys := NewFS(VFS) filesys := NewFS(VFS, opt)
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

@ -34,12 +34,14 @@ import (
// for an example. // for an example.
type FileHandle struct { type FileHandle struct {
h vfs.Handle h vfs.Handle
fsys *FS
} }
// Create a new FileHandle // Create a new FileHandle
func newFileHandle(h vfs.Handle) *FileHandle { func newFileHandle(h vfs.Handle, fsys *FS) *FileHandle {
return &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. // is assumed, and the 'blocks' field is set accordingly.
func (f *FileHandle) Getattr(ctx context.Context, out *fuse.AttrOut) (errno syscall.Errno) { func (f *FileHandle) Getattr(ctx context.Context, out *fuse.AttrOut) (errno syscall.Errno) {
defer log.Trace(f, "")("attr=%v, errno=%v", &out, &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 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) { 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) defer log.Trace(f, "in=%v", in)("attr=%v, errno=%v", &out, &errno)
var err error var err error
setAttrOut(f.h.Node(), out) f.fsys.setAttrOut(f.h.Node(), out)
size, ok := in.GetSize() size, ok := in.GetSize()
if ok { if ok {
err = f.h.Truncate(int64(size)) err = f.h.Truncate(int64(size))

View File

@ -20,13 +20,15 @@ import (
type FS struct { type FS struct {
VFS *vfs.VFS VFS *vfs.VFS
f fs.Fs f fs.Fs
opt *mountlib.Options
} }
// NewFS creates a pathfs.FileSystem from the fs.Fs passed in // 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{ fsys := &FS{
VFS: VFS, VFS: VFS,
f: VFS.Fs(), f: VFS.Fs(),
opt: opt,
} }
return fsys return fsys
} }
@ -84,16 +86,16 @@ func setAttr(node vfs.Node, attr *fuse.Attr) {
} }
// fill in AttrOut from node // 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) setAttr(node, &out.Attr)
out.SetTimeout(mountlib.AttrTimeout) out.SetTimeout(f.opt.AttrTimeout)
} }
// fill in EntryOut from node // 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) setAttr(node, &out.Attr)
out.SetEntryTimeout(mountlib.AttrTimeout) out.SetEntryTimeout(f.opt.AttrTimeout)
out.SetAttrTimeout(mountlib.AttrTimeout) out.SetAttrTimeout(f.opt.AttrTimeout)
} }
// Translate errors from mountlib into Syscall error numbers // Translate errors from mountlib into Syscall error numbers

View File

@ -27,12 +27,12 @@ func init() {
func mountOptions(fsys *FS, f fs.Fs) (mountOpts *fuse.MountOptions) { func mountOptions(fsys *FS, f fs.Fs) (mountOpts *fuse.MountOptions) {
device := f.Name() + ":" + f.Root() device := f.Name() + ":" + f.Root()
mountOpts = &fuse.MountOptions{ mountOpts = &fuse.MountOptions{
AllowOther: mountlib.AllowOther, AllowOther: fsys.opt.AllowOther,
FsName: device, FsName: device,
Name: "rclone", Name: "rclone",
DisableXAttrs: true, DisableXAttrs: true,
Debug: mountlib.DebugFUSE, Debug: fsys.opt.DebugFUSE,
MaxReadAhead: int(mountlib.MaxReadAhead), MaxReadAhead: int(fsys.opt.MaxReadAhead),
// RememberInodes: true, // RememberInodes: true,
// SingleThreaded: true, // SingleThreaded: true,
@ -96,22 +96,22 @@ func mountOptions(fsys *FS, f fs.Fs) (mountOpts *fuse.MountOptions) {
} }
var opts []string var opts []string
// FIXME doesn't work opts = append(opts, fmt.Sprintf("max_readahead=%d", maxReadAhead)) // FIXME doesn't work opts = append(opts, fmt.Sprintf("max_readahead=%d", maxReadAhead))
if mountlib.AllowNonEmpty { if fsys.opt.AllowNonEmpty {
opts = append(opts, "nonempty") opts = append(opts, "nonempty")
} }
if mountlib.AllowOther { if fsys.opt.AllowOther {
opts = append(opts, "allow_other") opts = append(opts, "allow_other")
} }
if mountlib.AllowRoot { if fsys.opt.AllowRoot {
opts = append(opts, "allow_root") opts = append(opts, "allow_root")
} }
if mountlib.DefaultPermissions { if fsys.opt.DefaultPermissions {
opts = append(opts, "default_permissions") opts = append(opts, "default_permissions")
} }
if fsys.VFS.Opt.ReadOnly { if fsys.VFS.Opt.ReadOnly {
opts = append(opts, "ro") opts = append(opts, "ro")
} }
if mountlib.WritebackCache { if fsys.opt.WritebackCache {
log.Printf("FIXME --write-back-cache not supported") log.Printf("FIXME --write-back-cache not supported")
// FIXME opts = append(opts,fuse.WritebackCache()) // 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 // 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(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() f := VFS.Fs()
fs.Debugf(f, "Mounting on %q", mountpoint) fs.Debugf(f, "Mounting on %q", mountpoint)
fsys := NewFS(VFS) fsys := NewFS(VFS, opt)
// nodeFsOpts := &fusefs.PathNodeFsOptions{ // nodeFsOpts := &fusefs.PathNodeFsOptions{
// ClientInodes: false, // ClientInodes: false,
// Debug: mountlib.DebugFUSE, // Debug: mountlib.DebugFUSE,
@ -171,8 +171,8 @@ func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error)
// FIXME fill out // FIXME fill out
opts := fusefs.Options{ opts := fusefs.Options{
MountOptions: *mountOpts, MountOptions: *mountOpts,
EntryTimeout: &mountlib.AttrTimeout, EntryTimeout: &opt.AttrTimeout,
AttrTimeout: &mountlib.AttrTimeout, AttrTimeout: &opt.AttrTimeout,
// UID // UID
// GID // GID
} }

View File

@ -111,7 +111,7 @@ var _ = (fusefs.NodeStatfser)((*Node)(nil))
// with the Options.NullPermissions setting. If blksize is unset, 4096 // with the Options.NullPermissions setting. If blksize is unset, 4096
// is assumed, and the 'blocks' field is set accordingly. // is assumed, and the 'blocks' field is set accordingly.
func (n *Node) Getattr(ctx context.Context, f fusefs.FileHandle, out *fuse.AttrOut) syscall.Errno { 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 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) { 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) defer log.Trace(n, "in=%v", in)("out=%#v, errno=%v", &out, &errno)
var err error var err error
setAttrOut(n.node, out) n.fsys.setAttrOut(n.node, out)
size, ok := in.GetSize() size, ok := in.GetSize()
if ok { if ok {
err = n.node.Truncate(int64(size)) 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 { if entry := n.node.DirEntry(); entry != nil && entry.Size() < 0 {
fuseFlags |= fuse.FOPEN_DIRECT_IO fuseFlags |= fuse.FOPEN_DIRECT_IO
} }
return newFileHandle(handle), fuseFlags, 0 return newFileHandle(handle, n.fsys), fuseFlags, 0
} }
var _ = (fusefs.NodeOpener)((*Node)(nil)) var _ = (fusefs.NodeOpener)((*Node)(nil))
@ -197,7 +197,7 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ino
// FIXME // FIXME
// out.SetEntryTimeout(dt time.Duration) // out.SetEntryTimeout(dt time.Duration)
// out.SetAttrTimeout(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 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) return nil, translateError(err)
} }
newNode := newNode(n.fsys, newDir) 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}) newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode})
return newInode, 0 return newInode, 0
} }
@ -333,7 +333,7 @@ func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint3
if err != nil { if err != nil {
return nil, nil, 0, translateError(err) return nil, nil, 0, translateError(err)
} }
fh = newFileHandle(handle) fh = newFileHandle(handle, n.fsys)
// FIXME // FIXME
// fh = &fusefs.WithFlags{ // fh = &fusefs.WithFlags{
// File: fh, // File: fh,
@ -346,7 +346,7 @@ func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint3
if errno != 0 { if errno != 0 {
return nil, nil, 0, errno return nil, nil, 0, errno
} }
setEntryOut(vfsNode, out) n.fsys.setEntryOut(vfsNode, out)
newNode := newNode(n.fsys, vfsNode) newNode := newNode(n.fsys, vfsNode)
fs.Debugf(nil, "attr=%#v", out.Attr) fs.Debugf(nil, "attr=%#v", out.Attr)
newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode}) newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode})

View File

@ -17,37 +17,48 @@ import (
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/config" "github.com/rclone/rclone/fs/config"
"github.com/rclone/rclone/fs/config/flags" "github.com/rclone/rclone/fs/config/flags"
"github.com/rclone/rclone/fs/rc"
"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" "github.com/rclone/rclone/vfs/vfsflags"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag"
) )
// Options set by command line flags // Options for creating the mount
var ( type Options struct {
DebugFUSE = false DebugFUSE bool
AllowNonEmpty = false AllowNonEmpty bool
AllowRoot = false AllowRoot bool
AllowOther = false AllowOther bool
DefaultPermissions = false DefaultPermissions bool
WritebackCache = false WritebackCache bool
Daemon = false Daemon bool
MaxReadAhead fs.SizeSuffix = 128 * 1024 MaxReadAhead fs.SizeSuffix
ExtraOptions []string ExtraOptions []string
ExtraFlags []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 VolumeName string
NoAppleDouble = true // use noappledouble by default NoAppleDouble bool
NoAppleXattr = false // do not use noapplexattr by default NoAppleXattr bool
DaemonTimeout time.Duration // OSXFUSE only 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 ( 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(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 // Global constants
@ -58,7 +69,35 @@ const (
func init() { func init() {
// DaemonTimeout defaults to non zero for macOS // DaemonTimeout defaults to non zero for macOS
if runtime.GOOS == "darwin" { 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) { Run: func(command *cobra.Command, args []string) {
cmd.CheckArgs(2, 2, command, args) cmd.CheckArgs(2, 2, command, args)
if Daemon { if Opt.Daemon {
config.PassConfigKeyForDaemonization = true 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 // Skip checkMountEmpty if --allow-non-empty flag is used or if
// the Operating System is Windows // the Operating System is Windows
if !AllowNonEmpty && runtime.GOOS != "windows" { if !Opt.AllowNonEmpty && runtime.GOOS != "windows" {
err := checkMountEmpty(mountpoint) err := checkMountEmpty(mountpoint)
if err != nil { if err != nil {
log.Fatalf("Fatal error: %v", err) 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") fs.Logf(nil, "--allow-non-empty flag does nothing on Windows")
} }
// Work out the volume name, removing special // Work out the volume name, removing special
// characters from it if necessary // characters from it if necessary
VolumeName := Opt.VolumeName
if VolumeName == "" { if VolumeName == "" {
VolumeName = fdst.Name() + ":" + fdst.Root() 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 // Start background task if --background is specified
if Daemon { if Opt.Daemon {
daemonized := startBackgroundMode() daemonized := startBackgroundMode()
if daemonized { if daemonized {
return return
@ -351,7 +391,7 @@ be copied to the vfs cache before opening with --vfs-cache-mode full.
} }
VFS := vfs.New(fdst, &vfsflags.Opt) VFS := vfs.New(fdst, &vfsflags.Opt)
err := Mount(VFS, mountpoint, mount) err := Mount(VFS, mountpoint, mount, &Opt)
if err != nil { if err != nil {
log.Fatalf("Fatal error: %v", err) 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 // Add flags
cmdFlags := commandDefinition.Flags() cmdFlags := commandDefinition.Flags()
flags.BoolVarP(cmdFlags, &DebugFUSE, "debug-fuse", "", DebugFUSE, "Debug the FUSE internals - needs -v.") AddFlags(cmdFlags)
// 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
vfsflags.AddFlags(cmdFlags) vfsflags.AddFlags(cmdFlags)
return commandDefinition return commandDefinition
@ -416,9 +435,13 @@ func ClipBlocks(b *uint64) {
// 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(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 // Mount it
errChan, unmount, err := mount(VFS, mountpoint) errChan, unmount, err := mount(VFS, mountpoint, opt)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to mount FUSE fs") return errors.Wrap(err, "failed to mount FUSE fs")
} }

View File

@ -107,7 +107,7 @@ func mountRc(_ context.Context, in rc.Params) (out rc.Params, err error) {
if mountFns[mountType] != nil { if mountFns[mountType] != nil {
VFS := vfs.New(fdst, &vfsOpt) VFS := vfs.New(fdst, &vfsOpt)
_, unmountFn, err := mountFns[mountType](VFS, mountPoint) _, unmountFn, err := mountFns[mountType](VFS, mountPoint, &Opt)
if err != nil { if err != nil {
log.Printf("mount FAILED: %v", err) log.Printf("mount FAILED: %v", err)

View File

@ -20,6 +20,7 @@ import (
"time" "time"
_ "github.com/rclone/rclone/backend/all" // import all the backends _ "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"
"github.com/rclone/rclone/fs/walk" "github.com/rclone/rclone/fs/walk"
"github.com/rclone/rclone/fstest" "github.com/rclone/rclone/fstest"
@ -30,21 +31,14 @@ import (
"github.com/stretchr/testify/require" "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 ( var (
mountFn MountFn mountFn mountlib.MountFn
) )
// RunTests runs all the tests against all the VFS cache modes // 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 // 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 mountFn = fn
flag.Parse() flag.Parse()
tests := []struct { tests := []struct {
@ -111,7 +105,7 @@ type Run struct {
fremoteName string fremoteName string
cleanRemote func() cleanRemote func()
umountResult <-chan error umountResult <-chan error
umountFn UnmountFn umountFn mountlib.UnmountFn
skip bool skip bool
} }
@ -178,7 +172,7 @@ 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 = vfs.New(r.fremote, &vfsflags.Opt) 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 { if err != nil {
log.Printf("mount FAILED: %v", err) log.Printf("mount FAILED: %v", err)
r.skip = true r.skip = true

View File

@ -6,6 +6,7 @@ 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/cmd/mountlib"
"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"
@ -17,7 +18,7 @@ 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(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) unmountResultChan := make(chan (error), 1)
unmount = func() error { unmount = func() error {
unmountResultChan <- nil unmountResultChan <- nil