]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fs: introduce file_getattr and file_setattr syscalls
authorAndrey Albershteyn <aalbersh@redhat.com>
Mon, 30 Jun 2025 16:20:16 +0000 (18:20 +0200)
committerChristian Brauner <brauner@kernel.org>
Wed, 2 Jul 2025 15:05:17 +0000 (17:05 +0200)
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>
21 files changed:
arch/alpha/kernel/syscalls/syscall.tbl
arch/arm/tools/syscall.tbl
arch/arm64/tools/syscall_32.tbl
arch/m68k/kernel/syscalls/syscall.tbl
arch/microblaze/kernel/syscalls/syscall.tbl
arch/mips/kernel/syscalls/syscall_n32.tbl
arch/mips/kernel/syscalls/syscall_n64.tbl
arch/mips/kernel/syscalls/syscall_o32.tbl
arch/parisc/kernel/syscalls/syscall.tbl
arch/powerpc/kernel/syscalls/syscall.tbl
arch/s390/kernel/syscalls/syscall.tbl
arch/sh/kernel/syscalls/syscall.tbl
arch/sparc/kernel/syscalls/syscall.tbl
arch/x86/entry/syscalls/syscall_32.tbl
arch/x86/entry/syscalls/syscall_64.tbl
arch/xtensa/kernel/syscalls/syscall.tbl
fs/file_attr.c
include/linux/syscalls.h
include/uapi/asm-generic/unistd.h
include/uapi/linux/fs.h
scripts/syscall.tbl

index 2dd6340de6b4efddc406f0c235701c15cf02f650..16dca28ebf17e5df2230f640f3686835dc5a1f76 100644 (file)
 575    common  listxattrat                     sys_listxattrat
 576    common  removexattrat                   sys_removexattrat
 577    common  open_tree_attr                  sys_open_tree_attr
+578    common  file_getattr                    sys_file_getattr
+579    common  file_setattr                    sys_file_setattr
index 27c1d5ebcd91c8c296dc6676307f66bfdf4ab78d..b07e699aaa3c2840452109c0004529fc68e153ac 100644 (file)
 465    common  listxattrat                     sys_listxattrat
 466    common  removexattrat                   sys_removexattrat
 467    common  open_tree_attr                  sys_open_tree_attr
+468    common  file_getattr                    sys_file_getattr
+469    common  file_setattr                    sys_file_setattr
index 0765b3a8d6d600d7b9f83182d401d9f80c03476c..8d9088bc577df0c6dba6d5d0c227585283789a3f 100644 (file)
 465    common  listxattrat                     sys_listxattrat
 466    common  removexattrat                   sys_removexattrat
 467    common  open_tree_attr                  sys_open_tree_attr
+468    common  file_getattr                    sys_file_getattr
+469    common  file_setattr                    sys_file_setattr
index 9fe47112c586f152662af38a9a7f90957cb96cf8..f41d38dfbf1382339316f4e3e6e79c75dc2804ec 100644 (file)
 465    common  listxattrat                     sys_listxattrat
 466    common  removexattrat                   sys_removexattrat
 467    common  open_tree_attr                  sys_open_tree_attr
+468    common  file_getattr                    sys_file_getattr
+469    common  file_setattr                    sys_file_setattr
index 7b6e97828e552d4da90046ddfcd4a55723e522bb..580af574fe733af29ea69f40ae6e902ad2166ab2 100644 (file)
 465    common  listxattrat                     sys_listxattrat
 466    common  removexattrat                   sys_removexattrat
 467    common  open_tree_attr                  sys_open_tree_attr
+468    common  file_getattr                    sys_file_getattr
+469    common  file_setattr                    sys_file_setattr
index aa70e371bb54ab5d9c8dd8923b6ecf9693ee914d..d824ffe9a01496a13194710d5278dcc443e40894 100644 (file)
 465    n32     listxattrat                     sys_listxattrat
 466    n32     removexattrat                   sys_removexattrat
 467    n32     open_tree_attr                  sys_open_tree_attr
+468    n32     file_getattr                    sys_file_getattr
+469    n32     file_setattr                    sys_file_setattr
index 1e8c44c7b61492eabf00c777831e457a7a6e579c..7a7049c2c307885fe8be33aedb503cfe8afbfb24 100644 (file)
 465    n64     listxattrat                     sys_listxattrat
 466    n64     removexattrat                   sys_removexattrat
 467    n64     open_tree_attr                  sys_open_tree_attr
+468    n64     file_getattr                    sys_file_getattr
+469    n64     file_setattr                    sys_file_setattr
index 114a5a1a62302e32dd74d1679ff423a2d57c3c6b..d330274f06010c9f9bf80d55898d0bd38994ef58 100644 (file)
 465    o32     listxattrat                     sys_listxattrat
 466    o32     removexattrat                   sys_removexattrat
 467    o32     open_tree_attr                  sys_open_tree_attr
+468    o32     file_getattr                    sys_file_getattr
+469    o32     file_setattr                    sys_file_setattr
index 94df3cb957e9d547d192e8732c0cf23ef2b5ce5d..88a788a7b18d17967019098b33d2be48a430aed3 100644 (file)
 465    common  listxattrat                     sys_listxattrat
 466    common  removexattrat                   sys_removexattrat
 467    common  open_tree_attr                  sys_open_tree_attr
+468    common  file_getattr                    sys_file_getattr
+469    common  file_setattr                    sys_file_setattr
index 9a084bdb892694bc562f514b55212d167cbac12f..b453e80dfc003796a08f022f6bd44cf76a5f82ab 100644 (file)
 465    common  listxattrat                     sys_listxattrat
 466    common  removexattrat                   sys_removexattrat
 467    common  open_tree_attr                  sys_open_tree_attr
+468    common  file_getattr                    sys_file_getattr
+469    common  file_setattr                    sys_file_setattr
index a4569b96ef06c54ce7aa795d039541c90a38284f..8a6744d658db3986f0ac0fd3c90592cdbd27249c 100644 (file)
 465  common    listxattrat             sys_listxattrat                 sys_listxattrat
 466  common    removexattrat           sys_removexattrat               sys_removexattrat
 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
index 52a7652fcff6394b96ace1f3b0ed72250ee5e669..5e9c9eff5539e24113bb757f1e5b51bd7c32864e 100644 (file)
 465    common  listxattrat                     sys_listxattrat
 466    common  removexattrat                   sys_removexattrat
 467    common  open_tree_attr                  sys_open_tree_attr
+468    common  file_getattr                    sys_file_getattr
+469    common  file_setattr                    sys_file_setattr
index 83e45eb6c095a36baaf749927628e6052fe900e6..ebb7d06d1044fa9b113b0a3fe0316c77f48fc3d0 100644 (file)
 465    common  listxattrat                     sys_listxattrat
 466    common  removexattrat                   sys_removexattrat
 467    common  open_tree_attr                  sys_open_tree_attr
+468    common  file_getattr                    sys_file_getattr
+469    common  file_setattr                    sys_file_setattr
index ac007ea00979dc28b0ef7c002a0615ce86dd3101..4877e16da69a50f2225088e556661051dbe3c7a3 100644 (file)
 465    i386    listxattrat             sys_listxattrat
 466    i386    removexattrat           sys_removexattrat
 467    i386    open_tree_attr          sys_open_tree_attr
+468    i386    file_getattr            sys_file_getattr
+469    i386    file_setattr            sys_file_setattr
index cfb5ca41e30de1a4e073750096f5b51a2ec137d2..92cf0fe2291eb99b536e4de4201aec16b3472094 100644 (file)
 465    common  listxattrat             sys_listxattrat
 466    common  removexattrat           sys_removexattrat
 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
index f657a77314f8667fa019a01e10c84ea270024adc..374e4cb788d8a6d4e1f3358b949ea8b600013e1c 100644 (file)
 465    common  listxattrat                     sys_listxattrat
 466    common  removexattrat                   sys_removexattrat
 467    common  open_tree_attr                  sys_open_tree_attr
+468    common  file_getattr                    sys_file_getattr
+469    common  file_setattr                    sys_file_setattr
index 775f43fc96872012c03037134b3e64dd7d0d0100..21d6a0607345940f53eb9a8c1166c0230ae0c60c 100644 (file)
@@ -4,6 +4,10 @@
 #include <linux/fscrypt.h>
 #include <linux/fileattr.h>
 #include <linux/export.h>
+#include <linux/syscalls.h>
+#include <linux/namei.h>
+
+#include "internal.h"
 
 /**
  * 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);
 
+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.
  * @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);
 
+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,
                                  struct fsxattr __user *ufa)
 {
@@ -344,3 +378,121 @@ int ioctl_fssetxattr(struct file *file, void __user *argp)
        return err;
 }
 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;
+}
index e5603cc91963dabb79324d6e9e2274f327311245..77f45e5d44139da36a5dacbf9db7b65261d13398 100644 (file)
@@ -78,6 +78,7 @@ struct cachestat;
 struct statmount;
 struct mnt_id_req;
 struct xattr_args;
+struct file_attr;
 
 #include <linux/types.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,
                                 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_eventfd2(unsigned int count, int flags);
 asmlinkage long sys_epoll_create1(int flags);
index 2892a45023af6d3eb941623d4fed04841ab07e02..04e0077fb4c97a4d565da86d3d0d1d243f32336e 100644 (file)
@@ -852,8 +852,14 @@ __SYSCALL(__NR_removexattrat, sys_removexattrat)
 #define __NR_open_tree_attr 467
 __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
-#define __NR_syscalls 468
+#define __NR_syscalls 470
 
 /*
  * 32 bit systems traditionally used different
index 0098b0ce8ccb1f194f67972b31265c8e82f7bf58..9663dbdda1811a000fa9bbb6aa66de16afdc9f0a 100644 (file)
@@ -148,6 +148,24 @@ struct fsxattr {
        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
  */
index 580b4e246aecd5f07d542943ba68fc4ed5961660..d1ae5e92c615b58ea64eb793e3b615127c2769ce 100644 (file)
 465    common  listxattrat                     sys_listxattrat
 466    common  removexattrat                   sys_removexattrat
 467    common  open_tree_attr                  sys_open_tree_attr
+468    common  file_getattr                    sys_file_getattr
+469    common  file_setattr                    sys_file_setattr