mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add syscalls for lgetxattr, fgetxattr, lsetxattr, and fsetxattr.
Note that these simply will use the same logic as getxattr and setxattr, which is not yet implemented for most filesystems. PiperOrigin-RevId: 290800960
This commit is contained in:
@@ -229,11 +229,11 @@ var AMD64 = &kernel.SyscallTable{
|
||||
186: syscalls.Supported("gettid", Gettid),
|
||||
187: syscalls.Supported("readahead", Readahead),
|
||||
188: syscalls.PartiallySupported("setxattr", SetXattr, "Only supported for tmpfs.", nil),
|
||||
189: syscalls.Error("lsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
190: syscalls.Error("fsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
189: syscalls.PartiallySupported("lsetxattr", LSetXattr, "Only supported for tmpfs.", nil),
|
||||
190: syscalls.PartiallySupported("fsetxattr", FSetXattr, "Only supported for tmpfs.", nil),
|
||||
191: syscalls.PartiallySupported("getxattr", GetXattr, "Only supported for tmpfs.", nil),
|
||||
192: syscalls.ErrorWithEvent("lgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
193: syscalls.ErrorWithEvent("fgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
192: syscalls.PartiallySupported("lgetxattr", LGetXattr, "Only supported for tmpfs.", nil),
|
||||
193: syscalls.PartiallySupported("fgetxattr", FGetXattr, "Only supported for tmpfs.", nil),
|
||||
194: syscalls.ErrorWithEvent("listxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
195: syscalls.ErrorWithEvent("llistxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
196: syscalls.ErrorWithEvent("flistxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
|
||||
@@ -42,11 +42,11 @@ var ARM64 = &kernel.SyscallTable{
|
||||
3: syscalls.PartiallySupported("io_cancel", IoCancel, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}),
|
||||
4: syscalls.PartiallySupported("io_getevents", IoGetevents, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}),
|
||||
5: syscalls.PartiallySupported("setxattr", SetXattr, "Only supported for tmpfs.", nil),
|
||||
6: syscalls.Error("lsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
7: syscalls.Error("fsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
6: syscalls.PartiallySupported("lsetxattr", LSetXattr, "Only supported for tmpfs.", nil),
|
||||
7: syscalls.PartiallySupported("fsetxattr", FSetXattr, "Only supported for tmpfs.", nil),
|
||||
8: syscalls.PartiallySupported("getxattr", GetXattr, "Only supported for tmpfs.", nil),
|
||||
9: syscalls.ErrorWithEvent("lgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
10: syscalls.ErrorWithEvent("fgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
9: syscalls.PartiallySupported("lgetxattr", LGetXattr, "Only supported for tmpfs.", nil),
|
||||
10: syscalls.PartiallySupported("fgetxattr", FGetXattr, "Only supported for tmpfs.", nil),
|
||||
11: syscalls.ErrorWithEvent("listxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
12: syscalls.ErrorWithEvent("llistxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
13: syscalls.ErrorWithEvent("flistxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
|
||||
|
||||
@@ -27,6 +27,40 @@ import (
|
||||
|
||||
// GetXattr implements linux syscall getxattr(2).
|
||||
func GetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
return getXattrFromPath(t, args, true)
|
||||
}
|
||||
|
||||
// LGetXattr implements linux syscall lgetxattr(2).
|
||||
func LGetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
return getXattrFromPath(t, args, false)
|
||||
}
|
||||
|
||||
// FGetXattr implements linux syscall fgetxattr(2).
|
||||
func FGetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
fd := args[0].Int()
|
||||
nameAddr := args[1].Pointer()
|
||||
valueAddr := args[2].Pointer()
|
||||
size := uint64(args[3].SizeT())
|
||||
|
||||
// TODO(b/113957122): Return EBADF if the fd was opened with O_PATH.
|
||||
f := t.GetFile(fd)
|
||||
if f == nil {
|
||||
return 0, nil, syserror.EBADF
|
||||
}
|
||||
defer f.DecRef()
|
||||
|
||||
n, value, err := getXattr(t, f.Dirent, nameAddr, size)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if _, err := t.CopyOutBytes(valueAddr, []byte(value)); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return uintptr(n), nil, nil
|
||||
}
|
||||
|
||||
func getXattrFromPath(t *kernel.Task, args arch.SyscallArguments, resolveSymlink bool) (uintptr, *kernel.SyscallControl, error) {
|
||||
pathAddr := args[0].Pointer()
|
||||
nameAddr := args[1].Pointer()
|
||||
valueAddr := args[2].Pointer()
|
||||
@@ -38,29 +72,17 @@ func GetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
}
|
||||
|
||||
valueLen := 0
|
||||
err = fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
|
||||
// If getxattr(2) is called with size 0, the size of the value will be
|
||||
// returned successfully even if it is nonzero. In that case, we need to
|
||||
// retrieve the entire attribute value so we can return the correct size.
|
||||
requestedSize := size
|
||||
if size == 0 || size > linux.XATTR_SIZE_MAX {
|
||||
requestedSize = linux.XATTR_SIZE_MAX
|
||||
err = fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
|
||||
if dirPath && !fs.IsDir(d.Inode.StableAttr) {
|
||||
return syserror.ENOTDIR
|
||||
}
|
||||
|
||||
value, err := getXattr(t, d, dirPath, nameAddr, uint64(requestedSize))
|
||||
n, value, err := getXattr(t, d, nameAddr, size)
|
||||
valueLen = n
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
valueLen = len(value)
|
||||
if uint64(valueLen) > requestedSize {
|
||||
return syserror.ERANGE
|
||||
}
|
||||
|
||||
// Skip copying out the attribute value if size is 0.
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
_, err = t.CopyOutBytes(valueAddr, []byte(value))
|
||||
return err
|
||||
})
|
||||
@@ -71,29 +93,73 @@ func GetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
}
|
||||
|
||||
// getXattr implements getxattr(2) from the given *fs.Dirent.
|
||||
func getXattr(t *kernel.Task, d *fs.Dirent, dirPath bool, nameAddr usermem.Addr, size uint64) (string, error) {
|
||||
if dirPath && !fs.IsDir(d.Inode.StableAttr) {
|
||||
return "", syserror.ENOTDIR
|
||||
}
|
||||
|
||||
func getXattr(t *kernel.Task, d *fs.Dirent, nameAddr usermem.Addr, size uint64) (int, string, error) {
|
||||
if err := checkXattrPermissions(t, d.Inode, fs.PermMask{Read: true}); err != nil {
|
||||
return "", err
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
name, err := copyInXattrName(t, nameAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(name, linux.XATTR_USER_PREFIX) {
|
||||
return "", syserror.EOPNOTSUPP
|
||||
return 0, "", syserror.EOPNOTSUPP
|
||||
}
|
||||
|
||||
return d.Inode.GetXattr(t, name, size)
|
||||
// If getxattr(2) is called with size 0, the size of the value will be
|
||||
// returned successfully even if it is nonzero. In that case, we need to
|
||||
// retrieve the entire attribute value so we can return the correct size.
|
||||
requestedSize := size
|
||||
if size == 0 || size > linux.XATTR_SIZE_MAX {
|
||||
requestedSize = linux.XATTR_SIZE_MAX
|
||||
}
|
||||
|
||||
value, err := d.Inode.GetXattr(t, name, requestedSize)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
n := len(value)
|
||||
if uint64(n) > requestedSize {
|
||||
return 0, "", syserror.ERANGE
|
||||
}
|
||||
|
||||
// Don't copy out the attribute value if size is 0.
|
||||
if size == 0 {
|
||||
return n, "", nil
|
||||
}
|
||||
return n, value, nil
|
||||
}
|
||||
|
||||
// SetXattr implements linux syscall setxattr(2).
|
||||
func SetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
return setXattrFromPath(t, args, true)
|
||||
}
|
||||
|
||||
// LSetXattr implements linux syscall lsetxattr(2).
|
||||
func LSetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
return setXattrFromPath(t, args, false)
|
||||
}
|
||||
|
||||
// FSetXattr implements linux syscall fsetxattr(2).
|
||||
func FSetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
fd := args[0].Int()
|
||||
nameAddr := args[1].Pointer()
|
||||
valueAddr := args[2].Pointer()
|
||||
size := uint64(args[3].SizeT())
|
||||
flags := args[4].Uint()
|
||||
|
||||
// TODO(b/113957122): Return EBADF if the fd was opened with O_PATH.
|
||||
f := t.GetFile(fd)
|
||||
if f == nil {
|
||||
return 0, nil, syserror.EBADF
|
||||
}
|
||||
defer f.DecRef()
|
||||
|
||||
return 0, nil, setXattr(t, f.Dirent, nameAddr, valueAddr, uint64(size), flags)
|
||||
}
|
||||
|
||||
func setXattrFromPath(t *kernel.Task, args arch.SyscallArguments, resolveSymlink bool) (uintptr, *kernel.SyscallControl, error) {
|
||||
pathAddr := args[0].Pointer()
|
||||
nameAddr := args[1].Pointer()
|
||||
valueAddr := args[2].Pointer()
|
||||
@@ -105,19 +171,19 @@ func SetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if flags&^(linux.XATTR_CREATE|linux.XATTR_REPLACE) != 0 {
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
|
||||
if dirPath && !fs.IsDir(d.Inode.StableAttr) {
|
||||
return syserror.ENOTDIR
|
||||
}
|
||||
|
||||
return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
|
||||
return setXattr(t, d, dirPath, nameAddr, valueAddr, uint64(size), flags)
|
||||
return setXattr(t, d, nameAddr, valueAddr, uint64(size), flags)
|
||||
})
|
||||
}
|
||||
|
||||
// setXattr implements setxattr(2) from the given *fs.Dirent.
|
||||
func setXattr(t *kernel.Task, d *fs.Dirent, dirPath bool, nameAddr, valueAddr usermem.Addr, size uint64, flags uint32) error {
|
||||
if dirPath && !fs.IsDir(d.Inode.StableAttr) {
|
||||
return syserror.ENOTDIR
|
||||
func setXattr(t *kernel.Task, d *fs.Dirent, nameAddr, valueAddr usermem.Addr, size uint64, flags uint32) error {
|
||||
if flags&^(linux.XATTR_CREATE|linux.XATTR_REPLACE) != 0 {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
|
||||
if err := checkXattrPermissions(t, d.Inode, fs.PermMask{Write: true}); err != nil {
|
||||
@@ -133,7 +199,7 @@ func setXattr(t *kernel.Task, d *fs.Dirent, dirPath bool, nameAddr, valueAddr us
|
||||
return syserror.E2BIG
|
||||
}
|
||||
buf := make([]byte, size)
|
||||
if _, err = t.CopyInBytes(valueAddr, buf); err != nil {
|
||||
if _, err := t.CopyInBytes(valueAddr, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
value := string(buf)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/syscalls/linux/file_base.h"
|
||||
#include "test/util/capability_util.h"
|
||||
#include "test/util/file_descriptor.h"
|
||||
#include "test/util/posix_error.h"
|
||||
#include "test/util/temp_path.h"
|
||||
#include "test/util/test_util.h"
|
||||
@@ -414,6 +415,46 @@ TEST_F(XattrTest, GetxattrNonexistentName) {
|
||||
EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA));
|
||||
}
|
||||
|
||||
TEST_F(XattrTest, LGetSetxattrOnSymlink) {
|
||||
TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
TempPath link = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
TempPath::CreateSymlinkTo(dir.path(), test_file_name_));
|
||||
|
||||
EXPECT_THAT(lsetxattr(link.path().c_str(), nullptr, nullptr, 0, 0),
|
||||
SyscallFailsWithErrno(EPERM));
|
||||
EXPECT_THAT(lgetxattr(link.path().c_str(), nullptr, nullptr, 0),
|
||||
SyscallFailsWithErrno(ENODATA));
|
||||
}
|
||||
|
||||
TEST_F(XattrTest, LGetSetxattrOnNonsymlink) {
|
||||
const char* path = test_file_name_.c_str();
|
||||
const char name[] = "user.test";
|
||||
int val = 1234;
|
||||
size_t size = sizeof(val);
|
||||
EXPECT_THAT(lsetxattr(path, name, &val, size, /*flags=*/0),
|
||||
SyscallSucceeds());
|
||||
|
||||
int buf = 0;
|
||||
EXPECT_THAT(lgetxattr(path, name, &buf, size),
|
||||
SyscallSucceedsWithValue(size));
|
||||
EXPECT_EQ(buf, val);
|
||||
}
|
||||
|
||||
TEST_F(XattrTest, FGetSetxattr) {
|
||||
const FileDescriptor fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_.c_str(), 0));
|
||||
const char name[] = "user.test";
|
||||
int val = 1234;
|
||||
size_t size = sizeof(val);
|
||||
EXPECT_THAT(fsetxattr(fd.get(), name, &val, size, /*flags=*/0),
|
||||
SyscallSucceeds());
|
||||
|
||||
int buf = 0;
|
||||
EXPECT_THAT(fgetxattr(fd.get(), name, &buf, size),
|
||||
SyscallSucceedsWithValue(size));
|
||||
EXPECT_EQ(buf, val);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace testing
|
||||
|
||||
Reference in New Issue
Block a user