From d347ac0154a824c25dcd8c4dd982be7703a2495d 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 c625b4260..7f08be673 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) } }