fs: introduce file_getattr and file_setattr syscalls

Introduce file_getattr() and file_setattr() syscalls to manipulate inode
extended attributes. The syscalls takes pair of file descriptor and
pathname. Then it operates on inode opened accroding to openat()
semantics. The struct file_attr is passed to obtain/change extended
attributes.

This is an alternative to FS_IOC_FSSETXATTR ioctl with a difference
that file don't need to be open as we can reference it with a path
instead of fd. By having this we can manipulated inode extended
attributes not only on regular files but also on special ones. This
is not possible with FS_IOC_FSSETXATTR ioctl as with special files
we can not call ioctl() directly on the filesystem inode using fd.

This patch adds two new syscalls which allows userspace to get/set
extended inode attributes on special files by using parent directory
and a path - *at() like syscall.

CC: linux-api@vger.kernel.org
CC: linux-fsdevel@vger.kernel.org
CC: linux-xfs@vger.kernel.org
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Link: https://lore.kernel.org/20250630-xattrat-syscall-v6-6-c4e3bc35227b@kernel.org
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Andrey Albershteyn 2025-06-30 18:20:16 +02:00 committed by Christian Brauner
parent 276e136bff
commit be7efb2d20
No known key found for this signature in database
GPG key ID: 91C61BC06578DCA2
21 changed files with 218 additions and 1 deletions

View file

@ -507,3 +507,5 @@
575 common listxattrat sys_listxattrat 575 common listxattrat sys_listxattrat
576 common removexattrat sys_removexattrat 576 common removexattrat sys_removexattrat
577 common open_tree_attr sys_open_tree_attr 577 common open_tree_attr sys_open_tree_attr
578 common file_getattr sys_file_getattr
579 common file_setattr sys_file_setattr

View file

@ -482,3 +482,5 @@
465 common listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr

View file

@ -479,3 +479,5 @@
465 common listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr

View file

@ -467,3 +467,5 @@
465 common listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr

View file

@ -473,3 +473,5 @@
465 common listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr

View file

@ -406,3 +406,5 @@
465 n32 listxattrat sys_listxattrat 465 n32 listxattrat sys_listxattrat
466 n32 removexattrat sys_removexattrat 466 n32 removexattrat sys_removexattrat
467 n32 open_tree_attr sys_open_tree_attr 467 n32 open_tree_attr sys_open_tree_attr
468 n32 file_getattr sys_file_getattr
469 n32 file_setattr sys_file_setattr

View file

@ -382,3 +382,5 @@
465 n64 listxattrat sys_listxattrat 465 n64 listxattrat sys_listxattrat
466 n64 removexattrat sys_removexattrat 466 n64 removexattrat sys_removexattrat
467 n64 open_tree_attr sys_open_tree_attr 467 n64 open_tree_attr sys_open_tree_attr
468 n64 file_getattr sys_file_getattr
469 n64 file_setattr sys_file_setattr

View file

@ -455,3 +455,5 @@
465 o32 listxattrat sys_listxattrat 465 o32 listxattrat sys_listxattrat
466 o32 removexattrat sys_removexattrat 466 o32 removexattrat sys_removexattrat
467 o32 open_tree_attr sys_open_tree_attr 467 o32 open_tree_attr sys_open_tree_attr
468 o32 file_getattr sys_file_getattr
469 o32 file_setattr sys_file_setattr

View file

@ -466,3 +466,5 @@
465 common listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr

View file

@ -558,3 +558,5 @@
465 common listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr

View file

@ -470,3 +470,5 @@
465 common listxattrat sys_listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr sys_file_setattr

View file

@ -471,3 +471,5 @@
465 common listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr

View file

@ -513,3 +513,5 @@
465 common listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr

View file

@ -473,3 +473,5 @@
465 i386 listxattrat sys_listxattrat 465 i386 listxattrat sys_listxattrat
466 i386 removexattrat sys_removexattrat 466 i386 removexattrat sys_removexattrat
467 i386 open_tree_attr sys_open_tree_attr 467 i386 open_tree_attr sys_open_tree_attr
468 i386 file_getattr sys_file_getattr
469 i386 file_setattr sys_file_setattr

View file

@ -391,6 +391,8 @@
465 common listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
# #
# Due to a historical design error, certain syscalls are numbered differently # Due to a historical design error, certain syscalls are numbered differently

View file

@ -438,3 +438,5 @@
465 common listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr

View file

@ -4,6 +4,10 @@
#include <linux/fscrypt.h> #include <linux/fscrypt.h>
#include <linux/fileattr.h> #include <linux/fileattr.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/syscalls.h>
#include <linux/namei.h>
#include "internal.h"
/** /**
* fileattr_fill_xflags - initialize fileattr with xflags * fileattr_fill_xflags - initialize fileattr with xflags
@ -90,6 +94,19 @@ int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
} }
EXPORT_SYMBOL(vfs_fileattr_get); EXPORT_SYMBOL(vfs_fileattr_get);
static void fileattr_to_file_attr(const struct fileattr *fa,
struct file_attr *fattr)
{
__u32 mask = FS_XFLAGS_MASK;
memset(fattr, 0, sizeof(struct file_attr));
fattr->fa_xflags = fa->fsx_xflags & mask;
fattr->fa_extsize = fa->fsx_extsize;
fattr->fa_nextents = fa->fsx_nextents;
fattr->fa_projid = fa->fsx_projid;
fattr->fa_cowextsize = fa->fsx_cowextsize;
}
/** /**
* copy_fsxattr_to_user - copy fsxattr to userspace. * copy_fsxattr_to_user - copy fsxattr to userspace.
* @fa: fileattr pointer * @fa: fileattr pointer
@ -116,6 +133,23 @@ int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
} }
EXPORT_SYMBOL(copy_fsxattr_to_user); EXPORT_SYMBOL(copy_fsxattr_to_user);
static int file_attr_to_fileattr(const struct file_attr *fattr,
struct fileattr *fa)
{
__u32 mask = FS_XFLAGS_MASK;
if (fattr->fa_xflags & ~mask)
return -EINVAL;
fileattr_fill_xflags(fa, fattr->fa_xflags);
fa->fsx_xflags &= ~FS_XFLAG_RDONLY_MASK;
fa->fsx_extsize = fattr->fa_extsize;
fa->fsx_projid = fattr->fa_projid;
fa->fsx_cowextsize = fattr->fa_cowextsize;
return 0;
}
static int copy_fsxattr_from_user(struct fileattr *fa, static int copy_fsxattr_from_user(struct fileattr *fa,
struct fsxattr __user *ufa) struct fsxattr __user *ufa)
{ {
@ -344,3 +378,121 @@ int ioctl_fssetxattr(struct file *file, void __user *argp)
return err; return err;
} }
EXPORT_SYMBOL(ioctl_fssetxattr); EXPORT_SYMBOL(ioctl_fssetxattr);
SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename,
struct file_attr __user *, ufattr, size_t, usize,
unsigned int, at_flags)
{
struct path filepath __free(path_put) = {};
struct filename *name __free(putname) = NULL;
unsigned int lookup_flags = 0;
struct file_attr fattr;
struct fileattr fa;
int error;
BUILD_BUG_ON(sizeof(struct file_attr) < FILE_ATTR_SIZE_VER0);
BUILD_BUG_ON(sizeof(struct file_attr) != FILE_ATTR_SIZE_LATEST);
if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
return -EINVAL;
if (!(at_flags & AT_SYMLINK_NOFOLLOW))
lookup_flags |= LOOKUP_FOLLOW;
if (usize > PAGE_SIZE)
return -E2BIG;
if (usize < FILE_ATTR_SIZE_VER0)
return -EINVAL;
name = getname_maybe_null(filename, at_flags);
if (IS_ERR(name))
return PTR_ERR(name);
if (!name && dfd >= 0) {
CLASS(fd, f)(dfd);
if (fd_empty(f))
return -EBADF;
filepath = fd_file(f)->f_path;
path_get(&filepath);
} else {
error = filename_lookup(dfd, name, lookup_flags, &filepath,
NULL);
if (error)
return error;
}
error = vfs_fileattr_get(filepath.dentry, &fa);
if (error)
return error;
fileattr_to_file_attr(&fa, &fattr);
error = copy_struct_to_user(ufattr, usize, &fattr,
sizeof(struct file_attr), NULL);
return error;
}
SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename,
struct file_attr __user *, ufattr, size_t, usize,
unsigned int, at_flags)
{
struct path filepath __free(path_put) = {};
struct filename *name __free(putname) = NULL;
unsigned int lookup_flags = 0;
struct file_attr fattr;
struct fileattr fa;
int error;
BUILD_BUG_ON(sizeof(struct file_attr) < FILE_ATTR_SIZE_VER0);
BUILD_BUG_ON(sizeof(struct file_attr) != FILE_ATTR_SIZE_LATEST);
if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
return -EINVAL;
if (!(at_flags & AT_SYMLINK_NOFOLLOW))
lookup_flags |= LOOKUP_FOLLOW;
if (usize > PAGE_SIZE)
return -E2BIG;
if (usize < FILE_ATTR_SIZE_VER0)
return -EINVAL;
error = copy_struct_from_user(&fattr, sizeof(struct file_attr), ufattr,
usize);
if (error)
return error;
error = file_attr_to_fileattr(&fattr, &fa);
if (error)
return error;
name = getname_maybe_null(filename, at_flags);
if (IS_ERR(name))
return PTR_ERR(name);
if (!name && dfd >= 0) {
CLASS(fd, f)(dfd);
if (fd_empty(f))
return -EBADF;
filepath = fd_file(f)->f_path;
path_get(&filepath);
} else {
error = filename_lookup(dfd, name, lookup_flags, &filepath,
NULL);
if (error)
return error;
}
error = mnt_want_write(filepath.mnt);
if (!error) {
error = vfs_fileattr_set(mnt_idmap(filepath.mnt),
filepath.dentry, &fa);
mnt_drop_write(filepath.mnt);
}
return error;
}

View file

@ -78,6 +78,7 @@ struct cachestat;
struct statmount; struct statmount;
struct mnt_id_req; struct mnt_id_req;
struct xattr_args; struct xattr_args;
struct file_attr;
#include <linux/types.h> #include <linux/types.h>
#include <linux/aio_abi.h> #include <linux/aio_abi.h>
@ -371,6 +372,12 @@ asmlinkage long sys_removexattrat(int dfd, const char __user *path,
asmlinkage long sys_lremovexattr(const char __user *path, asmlinkage long sys_lremovexattr(const char __user *path,
const char __user *name); const char __user *name);
asmlinkage long sys_fremovexattr(int fd, const char __user *name); asmlinkage long sys_fremovexattr(int fd, const char __user *name);
asmlinkage long sys_file_getattr(int dfd, const char __user *filename,
struct file_attr __user *attr, size_t usize,
unsigned int at_flags);
asmlinkage long sys_file_setattr(int dfd, const char __user *filename,
struct file_attr __user *attr, size_t usize,
unsigned int at_flags);
asmlinkage long sys_getcwd(char __user *buf, unsigned long size); asmlinkage long sys_getcwd(char __user *buf, unsigned long size);
asmlinkage long sys_eventfd2(unsigned int count, int flags); asmlinkage long sys_eventfd2(unsigned int count, int flags);
asmlinkage long sys_epoll_create1(int flags); asmlinkage long sys_epoll_create1(int flags);

View file

@ -852,8 +852,14 @@ __SYSCALL(__NR_removexattrat, sys_removexattrat)
#define __NR_open_tree_attr 467 #define __NR_open_tree_attr 467
__SYSCALL(__NR_open_tree_attr, sys_open_tree_attr) __SYSCALL(__NR_open_tree_attr, sys_open_tree_attr)
/* fs/inode.c */
#define __NR_file_getattr 468
__SYSCALL(__NR_file_getattr, sys_file_getattr)
#define __NR_file_setattr 469
__SYSCALL(__NR_file_setattr, sys_file_setattr)
#undef __NR_syscalls #undef __NR_syscalls
#define __NR_syscalls 468 #define __NR_syscalls 470
/* /*
* 32 bit systems traditionally used different * 32 bit systems traditionally used different

View file

@ -148,6 +148,24 @@ struct fsxattr {
unsigned char fsx_pad[8]; unsigned char fsx_pad[8];
}; };
/*
* Variable size structure for file_[sg]et_attr().
*
* Note. This is alternative to the structure 'struct fileattr'/'struct fsxattr'.
* As this structure is passed to/from userspace with its size, this can
* be versioned based on the size.
*/
struct file_attr {
__u64 fa_xflags; /* xflags field value (get/set) */
__u32 fa_extsize; /* extsize field value (get/set)*/
__u32 fa_nextents; /* nextents field value (get) */
__u32 fa_projid; /* project identifier (get/set) */
__u32 fa_cowextsize; /* CoW extsize field value (get/set) */
};
#define FILE_ATTR_SIZE_VER0 24
#define FILE_ATTR_SIZE_LATEST FILE_ATTR_SIZE_VER0
/* /*
* Flags for the fsx_xflags field * Flags for the fsx_xflags field
*/ */

View file

@ -408,3 +408,5 @@
465 common listxattrat sys_listxattrat 465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat 466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr 467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr