2022-07-06 17:14:06 +08:00
|
|
|
//go:build linux || netbsd || freebsd || darwin
|
2020-07-15 04:34:07 +08:00
|
|
|
|
|
|
|
package copier
|
|
|
|
|
|
|
|
import (
|
2022-08-18 02:44:08 +08:00
|
|
|
"errors"
|
2022-07-06 17:14:06 +08:00
|
|
|
"fmt"
|
2020-07-15 04:34:07 +08:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"syscall"
|
|
|
|
|
2024-09-17 20:45:27 +08:00
|
|
|
"github.com/sirupsen/logrus"
|
2025-08-29 20:55:12 +08:00
|
|
|
"go.podman.io/storage/pkg/unshare"
|
2020-07-15 04:34:07 +08:00
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
xattrsSupported = true
|
2024-09-17 20:45:27 +08:00
|
|
|
imaXattr = "security.ima"
|
2020-07-15 04:34:07 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2024-09-17 20:45:27 +08:00
|
|
|
relevantAttributes = []string{"security.capability", imaXattr, "user.*"} // the attributes that we preserve - we discard others
|
2022-04-27 06:02:35 +08:00
|
|
|
initialXattrListSize = 64 * 1024
|
|
|
|
initialXattrValueSize = 64 * 1024
|
2020-07-15 04:34:07 +08:00
|
|
|
)
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-07-15 04:34:07 +08:00
|
|
|
// 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
|
2022-04-27 06:02:35 +08:00
|
|
|
listSize := initialXattrListSize
|
2020-07-15 04:34:07 +08:00
|
|
|
var list []byte
|
|
|
|
for listSize < maxSize {
|
|
|
|
list = make([]byte, listSize)
|
|
|
|
size, err := unix.Llistxattr(path, list)
|
|
|
|
if err != nil {
|
2022-08-18 02:44:08 +08:00
|
|
|
if errors.Is(err, syscall.ERANGE) {
|
2020-07-15 04:34:07 +08:00
|
|
|
listSize *= 2
|
|
|
|
continue
|
|
|
|
}
|
2022-08-18 02:44:08 +08:00
|
|
|
if errors.Is(err, syscall.ENOTSUP) || errors.Is(err, syscall.ENOSYS) {
|
2020-11-13 03:57:35 +08:00
|
|
|
// treat these errors listing xattrs as equivalent to "no xattrs"
|
|
|
|
list = list[:0]
|
|
|
|
break
|
|
|
|
}
|
2022-09-18 18:36:08 +08:00
|
|
|
return nil, fmt.Errorf("listing extended attributes of %q: %w", path, err)
|
2020-07-15 04:34:07 +08:00
|
|
|
}
|
|
|
|
list = list[:size]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if listSize >= maxSize {
|
2022-07-06 17:14:06 +08:00
|
|
|
return nil, fmt.Errorf("unable to read list of attributes for %q: size would have been too big", path)
|
2020-07-15 04:34:07 +08:00
|
|
|
}
|
|
|
|
m := make(map[string]string)
|
2025-09-11 01:11:00 +08:00
|
|
|
for attribute := range strings.SplitSeq(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) {
|
2022-04-27 06:02:35 +08:00
|
|
|
attributeSize := initialXattrValueSize
|
2020-07-15 04:34:07 +08:00
|
|
|
var attributeValue []byte
|
|
|
|
for attributeSize < maxSize {
|
|
|
|
attributeValue = make([]byte, attributeSize)
|
|
|
|
size, err := unix.Lgetxattr(path, attribute, attributeValue)
|
|
|
|
if err != nil {
|
2022-08-18 02:44:08 +08:00
|
|
|
if errors.Is(err, syscall.ERANGE) {
|
2020-07-15 04:34:07 +08:00
|
|
|
attributeSize *= 2
|
|
|
|
continue
|
|
|
|
}
|
2022-09-18 18:36:08 +08:00
|
|
|
return nil, fmt.Errorf("getting value of extended attribute %q on %q: %w", attribute, path, err)
|
2020-07-15 04:34:07 +08:00
|
|
|
}
|
|
|
|
m[attribute] = string(attributeValue[:size])
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if attributeSize >= maxSize {
|
2022-07-06 17:14:06 +08:00
|
|
|
return nil, fmt.Errorf("unable to read attribute %q of %q: size would have been too big", attribute, path)
|
2020-07-15 04:34:07 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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 {
|
2024-09-17 20:45:27 +08:00
|
|
|
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)
|
|
|
|
}
|
2020-07-15 04:34:07 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|