From 831c79b11d12eca00a4bf7551033e4aaf9b905e0 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 8 Aug 2022 10:03:25 +0100 Subject: [PATCH] local: disable xattr support if the filesystems indicates it is not supported Before this change, if rclone was run with `-M` on a filesystem without xattr support, it would error out. This patch makes rclone detect the not supported errors and disable xattrs from then on. It prints one ERROR level message about this. See: https://forum.rclone.org/t/metadata-update-local-s3/32277/7 --- backend/local/local.go | 22 +++++++++++++--------- backend/local/xattr.go | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/backend/local/local.go b/backend/local/local.go index 3a83bd262..d6e0448ff 100644 --- a/backend/local/local.go +++ b/backend/local/local.go @@ -234,15 +234,16 @@ type Options struct { // Fs represents a local filesystem rooted at root type Fs struct { - name string // the name of the remote - root string // The root directory (OS path) - opt Options // parsed config options - features *fs.Features // optional features - dev uint64 // device number of root node - precisionOk sync.Once // Whether we need to read the precision - precision time.Duration // precision of local filesystem - warnedMu sync.Mutex // used for locking access to 'warned'. - warned map[string]struct{} // whether we have warned about this string + name string // the name of the remote + root string // The root directory (OS path) + opt Options // parsed config options + features *fs.Features // optional features + dev uint64 // device number of root node + precisionOk sync.Once // Whether we need to read the precision + precision time.Duration // precision of local filesystem + warnedMu sync.Mutex // used for locking access to 'warned'. + warned map[string]struct{} // whether we have warned about this string + xattrSupported int32 // whether xattrs are supported (atomic access) // do os.Lstat or os.Stat lstat func(name string) (os.FileInfo, error) @@ -286,6 +287,9 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e dev: devUnset, lstat: os.Lstat, } + if xattrSupported { + f.xattrSupported = 1 + } f.root = cleanRootPath(root, f.opt.NoUNC, f.opt.Enc) f.features = (&fs.Features{ CaseInsensitive: f.caseInsensitive(), diff --git a/backend/local/xattr.go b/backend/local/xattr.go index a2c0cc5e8..81e86d8a5 100644 --- a/backend/local/xattr.go +++ b/backend/local/xattr.go @@ -6,6 +6,8 @@ package local import ( "fmt" "strings" + "sync/atomic" + "syscall" "github.com/pkg/xattr" "github.com/rclone/rclone/fs" @@ -16,12 +18,30 @@ const ( xattrSupported = xattr.XATTR_SUPPORTED ) +// Check to see if the error supplied is a not supported error, and if +// so, disable xattrs +func (f *Fs) xattrIsNotSupported(err error) bool { + xattrErr, ok := err.(*xattr.Error) + if !ok { + return false + } + // Xattrs not supported can be ENOTSUP or ENOATTR or EINVAL (on Solaris) + if xattrErr.Err == syscall.EINVAL || xattrErr.Err == syscall.ENOTSUP || xattrErr.Err == xattr.ENOATTR { + // Show xattrs not supported + if atomic.CompareAndSwapInt32(&f.xattrSupported, 1, 0) { + fs.Errorf(f, "xattrs not supported - disabling: %v", err) + } + return true + } + return false +} + // getXattr returns the extended attributes for an object // // It doesn't return any attributes owned by this backend in // metadataKeys func (o *Object) getXattr() (metadata fs.Metadata, err error) { - if !xattrSupported { + if !xattrSupported || atomic.LoadInt32(&o.fs.xattrSupported) == 0 { return nil, nil } var list []string @@ -31,6 +51,9 @@ func (o *Object) getXattr() (metadata fs.Metadata, err error) { list, err = xattr.LList(o.path) } if err != nil { + if o.fs.xattrIsNotSupported(err) { + return nil, nil + } return nil, fmt.Errorf("failed to read xattr: %w", err) } if len(list) == 0 { @@ -45,6 +68,9 @@ func (o *Object) getXattr() (metadata fs.Metadata, err error) { v, err = xattr.LGet(o.path, k) } if err != nil { + if o.fs.xattrIsNotSupported(err) { + return nil, nil + } return nil, fmt.Errorf("failed to read xattr key %q: %w", k, err) } k = strings.ToLower(k) @@ -64,7 +90,7 @@ func (o *Object) getXattr() (metadata fs.Metadata, err error) { // // It doesn't set any attributes owned by this backend in metadataKeys func (o *Object) setXattr(metadata fs.Metadata) (err error) { - if !xattrSupported { + if !xattrSupported || atomic.LoadInt32(&o.fs.xattrSupported) == 0 { return nil } for k, value := range metadata { @@ -80,6 +106,9 @@ func (o *Object) setXattr(metadata fs.Metadata) (err error) { err = xattr.LSet(o.path, k, v) } if err != nil { + if o.fs.xattrIsNotSupported(err) { + return nil + } return fmt.Errorf("failed to set xattr key %q: %w", k, err) } }