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  }