github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/backend/local/xattr.go (about) 1 //go:build !openbsd && !plan9 2 3 package local 4 5 import ( 6 "fmt" 7 "strings" 8 "syscall" 9 10 "github.com/pkg/xattr" 11 "github.com/rclone/rclone/fs" 12 ) 13 14 const ( 15 xattrPrefix = "user." // FIXME is this correct for all unixes? 16 xattrSupported = xattr.XATTR_SUPPORTED 17 ) 18 19 // Check to see if the error supplied is a not supported error, and if 20 // so, disable xattrs 21 func (f *Fs) xattrIsNotSupported(err error) bool { 22 xattrErr, ok := err.(*xattr.Error) 23 if !ok { 24 return false 25 } 26 // Xattrs not supported can be ENOTSUP or ENOATTR or EINVAL (on Solaris) 27 if xattrErr.Err == syscall.EINVAL || xattrErr.Err == syscall.ENOTSUP || xattrErr.Err == xattr.ENOATTR { 28 // Show xattrs not supported 29 if f.xattrSupported.CompareAndSwap(1, 0) { 30 fs.Errorf(f, "xattrs not supported - disabling: %v", err) 31 } 32 return true 33 } 34 return false 35 } 36 37 // getXattr returns the extended attributes for an object 38 // 39 // It doesn't return any attributes owned by this backend in 40 // metadataKeys 41 func (o *Object) getXattr() (metadata fs.Metadata, err error) { 42 if !xattrSupported || o.fs.xattrSupported.Load() == 0 { 43 return nil, nil 44 } 45 var list []string 46 if o.fs.opt.FollowSymlinks { 47 list, err = xattr.List(o.path) 48 } else { 49 list, err = xattr.LList(o.path) 50 } 51 if err != nil { 52 if o.fs.xattrIsNotSupported(err) { 53 return nil, nil 54 } 55 return nil, fmt.Errorf("failed to read xattr: %w", err) 56 } 57 if len(list) == 0 { 58 return nil, nil 59 } 60 metadata = make(fs.Metadata, len(list)) 61 for _, k := range list { 62 var v []byte 63 if o.fs.opt.FollowSymlinks { 64 v, err = xattr.Get(o.path, k) 65 } else { 66 v, err = xattr.LGet(o.path, k) 67 } 68 if err != nil { 69 if o.fs.xattrIsNotSupported(err) { 70 return nil, nil 71 } 72 return nil, fmt.Errorf("failed to read xattr key %q: %w", k, err) 73 } 74 k = strings.ToLower(k) 75 if !strings.HasPrefix(k, xattrPrefix) { 76 continue 77 } 78 k = k[len(xattrPrefix):] 79 if _, found := systemMetadataInfo[k]; found { 80 continue 81 } 82 metadata[k] = string(v) 83 } 84 return metadata, nil 85 } 86 87 // setXattr sets the metadata on the file Xattrs 88 // 89 // It doesn't set any attributes owned by this backend in metadataKeys 90 func (o *Object) setXattr(metadata fs.Metadata) (err error) { 91 if !xattrSupported || o.fs.xattrSupported.Load() == 0 { 92 return nil 93 } 94 for k, value := range metadata { 95 k = strings.ToLower(k) 96 if _, found := systemMetadataInfo[k]; found { 97 continue 98 } 99 k = xattrPrefix + k 100 v := []byte(value) 101 if o.fs.opt.FollowSymlinks { 102 err = xattr.Set(o.path, k, v) 103 } else { 104 err = xattr.LSet(o.path, k, v) 105 } 106 if err != nil { 107 if o.fs.xattrIsNotSupported(err) { 108 return nil 109 } 110 return fmt.Errorf("failed to set xattr key %q: %w", k, err) 111 } 112 } 113 return nil 114 }