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) } }