buildah/copier/xattrs.go

108 lines
3.0 KiB
Go
Raw Normal View History

//go:build linux || netbsd || freebsd || darwin
package copier
import (
"errors"
"fmt"
"path/filepath"
"strings"
"syscall"
"github.com/sirupsen/logrus"
"go.podman.io/storage/pkg/unshare"
"golang.org/x/sys/unix"
)
const (
xattrsSupported = true
imaXattr = "security.ima"
)
var (
relevantAttributes = []string{"security.capability", imaXattr, "user.*"} // the attributes that we preserve - we discard others
initialXattrListSize = 64 * 1024
initialXattrValueSize = 64 * 1024
)
copier: add Mkdir() Add a function for doing Mkdir-possibly-in-a-chroot, for ensuring that a directory exists without having to possibly create it directly from outside of a chroot. Make use of filepath.ToSlash() and filepath.FromSlash() where it's appropriate. Add more unit tests. Address some review comments: * Check for ERANGE instead of E2BIG errors from llistxattr() and lgetxattr() as indicators that the buffer we passed to them is too small. * Factor an `isRelevantXattr` helper out of Lgetxattrs() and Lsetxattrs(). * Drop our getcwd() function in favor of using os.Getwd(). * Adjust the comment describing the GetOptions.KeepDirectoryNames field. * Clean hdr.Name before attempting to compute where an item being extracted should go. * When writing items to the filesystem, create with 0?00 permissions, set ownership, then set the correct permissions. * Merge StatResponse.Error, GetResponse.Error, and PutResponse.Error into Response.Error. * When reading items from the filesystem, if a glob matches multiple items, and one of the items is excluded, continue checking the other items. * Make sure we always Wait() on a child process if we spawned one. * Clean up the cleanup logic for pipes that we use to communicate with a child process. * Clean up the kill-the-child-process logic we call when we encounter an error communicating with the child process. * Drop the separate Options structure, use helper methods to simplify pulling out the right ID maps and exclusions list for the request. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2020-07-31 02:00:25 +08:00
// 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 := initialXattrListSize
var list []byte
for listSize < maxSize {
list = make([]byte, listSize)
size, err := unix.Llistxattr(path, list)
if err != nil {
if errors.Is(err, syscall.ERANGE) {
listSize *= 2
continue
}
if errors.Is(err, syscall.ENOTSUP) || errors.Is(err, syscall.ENOSYS) {
// treat these errors listing xattrs as equivalent to "no xattrs"
list = list[:0]
break
}
return nil, fmt.Errorf("listing extended attributes of %q: %w", path, err)
}
list = list[:size]
break
}
if listSize >= maxSize {
return nil, fmt.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')) {
copier: add Mkdir() Add a function for doing Mkdir-possibly-in-a-chroot, for ensuring that a directory exists without having to possibly create it directly from outside of a chroot. Make use of filepath.ToSlash() and filepath.FromSlash() where it's appropriate. Add more unit tests. Address some review comments: * Check for ERANGE instead of E2BIG errors from llistxattr() and lgetxattr() as indicators that the buffer we passed to them is too small. * Factor an `isRelevantXattr` helper out of Lgetxattrs() and Lsetxattrs(). * Drop our getcwd() function in favor of using os.Getwd(). * Adjust the comment describing the GetOptions.KeepDirectoryNames field. * Clean hdr.Name before attempting to compute where an item being extracted should go. * When writing items to the filesystem, create with 0?00 permissions, set ownership, then set the correct permissions. * Merge StatResponse.Error, GetResponse.Error, and PutResponse.Error into Response.Error. * When reading items from the filesystem, if a glob matches multiple items, and one of the items is excluded, continue checking the other items. * Make sure we always Wait() on a child process if we spawned one. * Clean up the cleanup logic for pipes that we use to communicate with a child process. * Clean up the kill-the-child-process logic we call when we encounter an error communicating with the child process. * Drop the separate Options structure, use helper methods to simplify pulling out the right ID maps and exclusions list for the request. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2020-07-31 02:00:25 +08:00
if isRelevantXattr(attribute) {
attributeSize := initialXattrValueSize
var attributeValue []byte
for attributeSize < maxSize {
attributeValue = make([]byte, attributeSize)
size, err := unix.Lgetxattr(path, attribute, attributeValue)
if err != nil {
if errors.Is(err, syscall.ERANGE) {
attributeSize *= 2
continue
}
return nil, fmt.Errorf("getting value of extended attribute %q on %q: %w", attribute, path, err)
}
m[attribute] = string(attributeValue[:size])
break
}
if attributeSize >= maxSize {
return nil, fmt.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 {
copier: add Mkdir() Add a function for doing Mkdir-possibly-in-a-chroot, for ensuring that a directory exists without having to possibly create it directly from outside of a chroot. Make use of filepath.ToSlash() and filepath.FromSlash() where it's appropriate. Add more unit tests. Address some review comments: * Check for ERANGE instead of E2BIG errors from llistxattr() and lgetxattr() as indicators that the buffer we passed to them is too small. * Factor an `isRelevantXattr` helper out of Lgetxattrs() and Lsetxattrs(). * Drop our getcwd() function in favor of using os.Getwd(). * Adjust the comment describing the GetOptions.KeepDirectoryNames field. * Clean hdr.Name before attempting to compute where an item being extracted should go. * When writing items to the filesystem, create with 0?00 permissions, set ownership, then set the correct permissions. * Merge StatResponse.Error, GetResponse.Error, and PutResponse.Error into Response.Error. * When reading items from the filesystem, if a glob matches multiple items, and one of the items is excluded, continue checking the other items. * Make sure we always Wait() on a child process if we spawned one. * Clean up the cleanup logic for pipes that we use to communicate with a child process. * Clean up the kill-the-child-process logic we call when we encounter an error communicating with the child process. * Drop the separate Options structure, use helper methods to simplify pulling out the right ID maps and exclusions list for the request. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2020-07-31 02:00:25 +08:00
if isRelevantXattr(attribute) {
if err := unix.Lsetxattr(path, attribute, []byte(value), 0); err != nil {
if unshare.IsRootless() && attribute == imaXattr {
logrus.Warnf("Unable to set %q xattr on %q: %v", attribute, path, err)
} else {
return fmt.Errorf("setting value of extended attribute %q on %q: %w", attribute, path, err)
}
}
}
}
return nil
}