d347ac0154
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
116 lines
2.7 KiB
Go
116 lines
2.7 KiB
Go
//go:build !openbsd && !plan9
|
|
// +build !openbsd,!plan9
|
|
|
|
package local
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"sync/atomic"
|
|
"syscall"
|
|
|
|
"github.com/pkg/xattr"
|
|
"github.com/rclone/rclone/fs"
|
|
)
|
|
|
|
const (
|
|
xattrPrefix = "user." // FIXME is this correct for all unixes?
|
|
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 || atomic.LoadInt32(&o.fs.xattrSupported) == 0 {
|
|
return nil, nil
|
|
}
|
|
var list []string
|
|
if o.fs.opt.FollowSymlinks {
|
|
list, err = xattr.List(o.path)
|
|
} else {
|
|
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 {
|
|
return nil, nil
|
|
}
|
|
metadata = make(fs.Metadata, len(list))
|
|
for _, k := range list {
|
|
var v []byte
|
|
if o.fs.opt.FollowSymlinks {
|
|
v, err = xattr.Get(o.path, k)
|
|
} else {
|
|
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)
|
|
if !strings.HasPrefix(k, xattrPrefix) {
|
|
continue
|
|
}
|
|
k = k[len(xattrPrefix):]
|
|
if _, found := systemMetadataInfo[k]; found {
|
|
continue
|
|
}
|
|
metadata[k] = string(v)
|
|
}
|
|
return metadata, nil
|
|
}
|
|
|
|
// setXattr sets the metadata on the file Xattrs
|
|
//
|
|
// It doesn't set any attributes owned by this backend in metadataKeys
|
|
func (o *Object) setXattr(metadata fs.Metadata) (err error) {
|
|
if !xattrSupported || atomic.LoadInt32(&o.fs.xattrSupported) == 0 {
|
|
return nil
|
|
}
|
|
for k, value := range metadata {
|
|
k = strings.ToLower(k)
|
|
if _, found := systemMetadataInfo[k]; found {
|
|
continue
|
|
}
|
|
k = xattrPrefix + k
|
|
v := []byte(value)
|
|
if o.fs.opt.FollowSymlinks {
|
|
err = xattr.Set(o.path, k, v)
|
|
} else {
|
|
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)
|
|
}
|
|
}
|
|
return nil
|
|
}
|