1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
// +build linux netbsd freebsd darwin
package copier
import (
"path/filepath"
"strings"
"syscall"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
const (
xattrsSupported = true
)
var (
relevantAttributes = []string{"security.capability", "security.ima", "user.*"} // the attributes that we preserve - we discard others
)
// isRelevantXattr checks if "attribute" matches one of the attribute patterns
// listed in the "relevantAttributes" list.
func isRelevantXattr(attribute string) bool {
for _, relevant := range relevantAttributes {
matched, err := filepath.Match(relevant, attribute)
if err != nil || !matched {
continue
}
return true
}
return false
}
// Lgetxattrs returns a map of the relevant extended attributes set on the given file.
func Lgetxattrs(path string) (map[string]string, error) {
maxSize := 64 * 1024 * 1024
listSize := 64 * 1024
var list []byte
for listSize < maxSize {
list = make([]byte, listSize)
size, err := unix.Llistxattr(path, list)
if err != nil {
if unwrapError(err) == syscall.ERANGE {
listSize *= 2
continue
}
if (unwrapError(err) == syscall.ENOTSUP) || (unwrapError(err) == syscall.ENOSYS) {
// treat these errors listing xattrs as equivalent to "no xattrs"
list = list[:0]
break
}
return nil, errors.Wrapf(err, "error listing extended attributes of %q", path)
}
list = list[:size]
break
}
if listSize >= maxSize {
return nil, errors.Errorf("unable to read list of attributes for %q: size would have been too big", path)
}
m := make(map[string]string)
for _, attribute := range strings.Split(string(list), string('\000')) {
if isRelevantXattr(attribute) {
attributeSize := 64 * 1024
var attributeValue []byte
for attributeSize < maxSize {
attributeValue = make([]byte, attributeSize)
size, err := unix.Lgetxattr(path, attribute, attributeValue)
if err != nil {
if unwrapError(err) == syscall.ERANGE {
attributeSize *= 2
continue
}
return nil, errors.Wrapf(err, "error getting value of extended attribute %q on %q", attribute, path)
}
m[attribute] = string(attributeValue[:size])
break
}
if attributeSize >= maxSize {
return nil, errors.Errorf("unable to read attribute %q of %q: size would have been too big", attribute, path)
}
}
}
return m, nil
}
// Lsetxattrs sets the relevant members of the specified extended attributes on the given file.
func Lsetxattrs(path string, xattrs map[string]string) error {
for attribute, value := range xattrs {
if isRelevantXattr(attribute) {
if err := unix.Lsetxattr(path, attribute, []byte(value), 0); err != nil {
return errors.Wrapf(err, "error setting value of extended attribute %q on %q", attribute, path)
}
}
}
return nil
}
|