* [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls
@ 2025-06-30 16:20 Andrey Albershteyn
2025-06-30 16:20 ` [PATCH v6 1/6] fs: split fileattr related helpers into separate file Andrey Albershteyn
` (8 more replies)
0 siblings, 9 replies; 47+ messages in thread
From: Andrey Albershteyn @ 2025-06-30 16:20 UTC (permalink / raw)
To: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore
Cc: linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn, Andrey Albershteyn
This patchset introduced two new syscalls file_getattr() and
file_setattr(). These syscalls are similar to FS_IOC_FSSETXATTR ioctl()
except they use *at() semantics. Therefore, there's no need to open the
file to get a fd.
These syscalls allow userspace to set filesystem inode attributes on
special files. One of the usage examples is XFS quota projects.
XFS has project quotas which could be attached to a directory. All
new inodes in these directories inherit project ID set on parent
directory.
The project is created from userspace by opening and calling
FS_IOC_FSSETXATTR on each inode. This is not possible for special
files such as FIFO, SOCK, BLK etc. Therefore, some inodes are left
with empty project ID. Those inodes then are not shown in the quota
accounting but still exist in the directory. This is not critical but in
the case when special files are created in the directory with already
existing project quota, these new inodes inherit extended attributes.
This creates a mix of special files with and without attributes.
Moreover, special files with attributes don't have a possibility to
become clear or change the attributes. This, in turn, prevents userspace
from re-creating quota project on these existing files.
An xfstests test generic/766 with basic coverage is at:
https://github.com/alberand/xfstests/commits/b4/file-attr/
NAME
file_getattr/file_setattr - get/set filesystem inode attributes
SYNOPSIS
#include <sys/syscall.h> /* Definition of SYS_* constants */
#include <unistd.h>
long syscall(SYS_file_getattr, int dirfd, const char *pathname,
struct fsx_fileattr *fsx, size_t size,
unsigned int at_flags);
long syscall(SYS_file_setattr, int dirfd, const char *pathname,
struct fsx_fileattr *fsx, size_t size,
unsigned int at_flags);
Note: glibc doesn't provide for file_getattr()/file_setattr(),
use syscall(2) instead.
DESCRIPTION
The file_getattr()/file_setattr() are used to set extended file
attributes. These syscalls take dirfd in conjunction with the
pathname argument. The syscall then operates on inode opened
according to openat(2) semantics.
This is an alternative to FS_IOC_FSGETXATTR/FS_IOC_FSSETXATTR
ioctl with a difference that file don't need to be open as file
can be referenced with a path instead of fd. By having this one
can manipulated filesystem inode attributes not only on regular
files but also on special ones. This is not possible with
FS_IOC_FSSETXATTR ioctl as ioctl() can not be called on special
files directly for the filesystem inode.
at_flags can be set to AT_SYMLINK_NOFOLLOW or AT_EMPTY_PATH.
RETURN VALUE
On success, 0 is returned. On error, -1 is returned, and errno
is set to indicate the error.
ERRORS
EINVAL Invalid at_flag specified (only
AT_SYMLINK_NOFOLLOW and AT_EMPTY_PATH is
supported).
EINVAL Size was smaller than any known version of
struct fsx_fileattr.
EINVAL Invalid combination of parameters provided in
fsx_fileattr for this type of file.
E2BIG Size of input argument struct fsx_fileattr
is too big.
EBADF Invalid file descriptor was provided.
EPERM No permission to change this file.
EOPNOTSUPP Filesystem does not support setting attributes
on this type of inode
HISTORY
Added in Linux 6.16.
EXAMPLE
Create directory and file "mkdir ./dir && touch ./dir/foo" and then
execute the following program:
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <linux/fs.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#if !defined(SYS_file_getattr) && defined(__x86_64__)
#define SYS_file_getattr 468
#define SYS_file_setattr 469
struct fsx_fileattr {
__u32 fsx_xflags;
__u32 fsx_extsize;
__u32 fsx_nextents;
__u32 fsx_projid;
__u32 fsx_cowextsize;
};
#endif
int
main(int argc, char **argv) {
int dfd;
int error;
struct fsx_fileattr fsx;
dfd = open("./dir", O_RDONLY);
if (dfd == -1) {
printf("can not open ./dir");
return dfd;
}
error = syscall(SYS_file_getattr, dfd, "./foo", &fsx,
sizeof(struct fsx_fileattr), 0);
if (error) {
printf("can not call SYS_file_getattr: %s",
strerror(errno));
return error;
}
printf("./dir/foo flags: %d\n", fsx.fsx_xflags);
fsx.fsx_xflags |= FS_XFLAG_NODUMP;
error = syscall(SYS_file_setattr, dfd, "./foo", &fsx,
sizeof(struct fsx_fileattr), 0);
if (error) {
printf("can not call SYS_file_setattr: %s",
strerror(errno));
return error;
}
printf("./dir/foo flags: %d\n", fsx.fsx_xflags);
return error;
}
SEE ALSO
ioctl(2), ioctl_iflags(2), ioctl_xfs_fsgetxattr(2), openat(2)
---
Changes in v6:
- Update cover letter example and docs
- Applied __free() attribute for syscall stack objects
- Introduced struct fsx_fileattr
- Replace 'struct fsxattr' with 'struct fsx_fileattr'
- Add helper to fill in fsx_fileattr from fileattr
- Dropped copy_fsx_to_user() header declaration
- Link to v5: https://lore.kernel.org/r/20250513-xattrat-syscall-v5-0-22bb9c6c767f@kernel.org
Changes in v5:
- Remove setting of LOOKUP_EMPTY flags which does not have any effect
- Return -ENOSUPP from vfs_fileattr_set()
- Add fsxattr masking (by Amir)
- Fix UAF issue dentry
- Fix getname_maybe_null() issue with NULL path
- Implement file_getattr/file_setattr hooks
- Return LSM return code from file_setattr
- Rename from getfsxattrat/setfsxattrat to file_getattr/file_setattr
- Link to v4: https://lore.kernel.org/r/20250321-xattrat-syscall-v4-0-3e82e6fb3264@kernel.org
Changes in v4:
- Use getname_maybe_null() for correct handling of dfd + path semantic
- Remove restriction for special files on which flags are allowed
- Utilize copy_struct_from_user() for better future compatibility
- Add draft man page to cover letter
- Convert -ENOIOCTLCMD to -EOPNOSUPP as more appropriate for syscall
- Add missing __user to header declaration of syscalls
- Link to v3: https://lore.kernel.org/r/20250211-xattrat-syscall-v3-1-a07d15f898b2@kernel.org
Changes in v3:
- Remove unnecessary "dfd is dir" check as it checked in user_path_at()
- Remove unnecessary "same filesystem" check
- Use CLASS() instead of directly calling fdget/fdput
- Link to v2: https://lore.kernel.org/r/20250122-xattrat-syscall-v2-1-5b360d4fbcb2@kernel.org
v1:
https://lore.kernel.org/linuxppc-dev/20250109174540.893098-1-aalbersh@kernel.org/
Previous discussion:
https://lore.kernel.org/linux-xfs/20240520164624.665269-2-aalbersh@redhat.com/
---
Amir Goldstein (1):
fs: prepare for extending file_get/setattr()
Andrey Albershteyn (5):
fs: split fileattr related helpers into separate file
lsm: introduce new hooks for setting/getting inode fsxattr
selinux: implement inode_file_[g|s]etattr hooks
fs: make vfs_fileattr_[get|set] return -EOPNOSUPP
fs: introduce file_getattr and file_setattr syscalls
arch/alpha/kernel/syscalls/syscall.tbl | 2 +
arch/arm/tools/syscall.tbl | 2 +
arch/arm64/tools/syscall_32.tbl | 2 +
arch/m68k/kernel/syscalls/syscall.tbl | 2 +
arch/microblaze/kernel/syscalls/syscall.tbl | 2 +
arch/mips/kernel/syscalls/syscall_n32.tbl | 2 +
arch/mips/kernel/syscalls/syscall_n64.tbl | 2 +
arch/mips/kernel/syscalls/syscall_o32.tbl | 2 +
arch/parisc/kernel/syscalls/syscall.tbl | 2 +
arch/powerpc/kernel/syscalls/syscall.tbl | 2 +
arch/s390/kernel/syscalls/syscall.tbl | 2 +
arch/sh/kernel/syscalls/syscall.tbl | 2 +
arch/sparc/kernel/syscalls/syscall.tbl | 2 +
arch/x86/entry/syscalls/syscall_32.tbl | 2 +
arch/x86/entry/syscalls/syscall_64.tbl | 2 +
arch/xtensa/kernel/syscalls/syscall.tbl | 2 +
fs/Makefile | 3 +-
fs/ecryptfs/inode.c | 8 +-
fs/file_attr.c | 493 ++++++++++++++++++++++++++++
fs/ioctl.c | 309 -----------------
fs/overlayfs/inode.c | 2 +-
include/linux/fileattr.h | 24 ++
include/linux/lsm_hook_defs.h | 2 +
include/linux/security.h | 16 +
include/linux/syscalls.h | 6 +
include/uapi/asm-generic/unistd.h | 8 +-
include/uapi/linux/fs.h | 18 +
scripts/syscall.tbl | 2 +
security/security.c | 30 ++
security/selinux/hooks.c | 14 +
30 files changed, 654 insertions(+), 313 deletions(-)
---
base-commit: d0b3b7b22dfa1f4b515fd3a295b3fd958f9e81af
change-id: 20250114-xattrat-syscall-6a1136d2db59
Best regards,
--
Andrey Albershteyn <aalbersh@kernel.org>
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v6 1/6] fs: split fileattr related helpers into separate file
2025-06-30 16:20 [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
@ 2025-06-30 16:20 ` Andrey Albershteyn
2025-07-01 5:39 ` Amir Goldstein
` (2 more replies)
2025-06-30 16:20 ` [PATCH v6 2/6] lsm: introduce new hooks for setting/getting inode fsxattr Andrey Albershteyn
` (7 subsequent siblings)
8 siblings, 3 replies; 47+ messages in thread
From: Andrey Albershteyn @ 2025-06-30 16:20 UTC (permalink / raw)
To: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore
Cc: linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
From: Andrey Albershteyn <aalbersh@kernel.org>
This patch moves function related to file extended attributes
manipulations to separate file. Refactoring only.
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
fs/Makefile | 3 +-
fs/file_attr.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++
fs/ioctl.c | 309 ---------------------------------------------
include/linux/fileattr.h | 4 +
4 files changed, 324 insertions(+), 310 deletions(-)
diff --git a/fs/Makefile b/fs/Makefile
index 79c08b914c47..334654f9584b 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -15,7 +15,8 @@ obj-y := open.o read_write.o file_table.o super.o \
pnode.o splice.o sync.o utimes.o d_path.o \
stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
fs_types.o fs_context.o fs_parser.o fsopen.o init.o \
- kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o
+ kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o \
+ file_attr.o
obj-$(CONFIG_BUFFER_HEAD) += buffer.o mpage.o
obj-$(CONFIG_PROC_FS) += proc_namespace.o
diff --git a/fs/file_attr.c b/fs/file_attr.c
new file mode 100644
index 000000000000..2910b7047721
--- /dev/null
+++ b/fs/file_attr.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/fs.h>
+#include <linux/security.h>
+#include <linux/fscrypt.h>
+#include <linux/fileattr.h>
+
+/**
+ * fileattr_fill_xflags - initialize fileattr with xflags
+ * @fa: fileattr pointer
+ * @xflags: FS_XFLAG_* flags
+ *
+ * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All
+ * other fields are zeroed.
+ */
+void fileattr_fill_xflags(struct fileattr *fa, u32 xflags)
+{
+ memset(fa, 0, sizeof(*fa));
+ fa->fsx_valid = true;
+ fa->fsx_xflags = xflags;
+ if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
+ fa->flags |= FS_IMMUTABLE_FL;
+ if (fa->fsx_xflags & FS_XFLAG_APPEND)
+ fa->flags |= FS_APPEND_FL;
+ if (fa->fsx_xflags & FS_XFLAG_SYNC)
+ fa->flags |= FS_SYNC_FL;
+ if (fa->fsx_xflags & FS_XFLAG_NOATIME)
+ fa->flags |= FS_NOATIME_FL;
+ if (fa->fsx_xflags & FS_XFLAG_NODUMP)
+ fa->flags |= FS_NODUMP_FL;
+ if (fa->fsx_xflags & FS_XFLAG_DAX)
+ fa->flags |= FS_DAX_FL;
+ if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
+ fa->flags |= FS_PROJINHERIT_FL;
+}
+EXPORT_SYMBOL(fileattr_fill_xflags);
+
+/**
+ * fileattr_fill_flags - initialize fileattr with flags
+ * @fa: fileattr pointer
+ * @flags: FS_*_FL flags
+ *
+ * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
+ * All other fields are zeroed.
+ */
+void fileattr_fill_flags(struct fileattr *fa, u32 flags)
+{
+ memset(fa, 0, sizeof(*fa));
+ fa->flags_valid = true;
+ fa->flags = flags;
+ if (fa->flags & FS_SYNC_FL)
+ fa->fsx_xflags |= FS_XFLAG_SYNC;
+ if (fa->flags & FS_IMMUTABLE_FL)
+ fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
+ if (fa->flags & FS_APPEND_FL)
+ fa->fsx_xflags |= FS_XFLAG_APPEND;
+ if (fa->flags & FS_NODUMP_FL)
+ fa->fsx_xflags |= FS_XFLAG_NODUMP;
+ if (fa->flags & FS_NOATIME_FL)
+ fa->fsx_xflags |= FS_XFLAG_NOATIME;
+ if (fa->flags & FS_DAX_FL)
+ fa->fsx_xflags |= FS_XFLAG_DAX;
+ if (fa->flags & FS_PROJINHERIT_FL)
+ fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
+}
+EXPORT_SYMBOL(fileattr_fill_flags);
+
+/**
+ * vfs_fileattr_get - retrieve miscellaneous file attributes
+ * @dentry: the object to retrieve from
+ * @fa: fileattr pointer
+ *
+ * Call i_op->fileattr_get() callback, if exists.
+ *
+ * Return: 0 on success, or a negative error on failure.
+ */
+int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+{
+ struct inode *inode = d_inode(dentry);
+
+ if (!inode->i_op->fileattr_get)
+ return -ENOIOCTLCMD;
+
+ return inode->i_op->fileattr_get(dentry, fa);
+}
+EXPORT_SYMBOL(vfs_fileattr_get);
+
+/**
+ * copy_fsxattr_to_user - copy fsxattr to userspace.
+ * @fa: fileattr pointer
+ * @ufa: fsxattr user pointer
+ *
+ * Return: 0 on success, or -EFAULT on failure.
+ */
+int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
+{
+ struct fsxattr xfa;
+
+ memset(&xfa, 0, sizeof(xfa));
+ xfa.fsx_xflags = fa->fsx_xflags;
+ xfa.fsx_extsize = fa->fsx_extsize;
+ xfa.fsx_nextents = fa->fsx_nextents;
+ xfa.fsx_projid = fa->fsx_projid;
+ xfa.fsx_cowextsize = fa->fsx_cowextsize;
+
+ if (copy_to_user(ufa, &xfa, sizeof(xfa)))
+ return -EFAULT;
+
+ return 0;
+}
+EXPORT_SYMBOL(copy_fsxattr_to_user);
+
+static int copy_fsxattr_from_user(struct fileattr *fa,
+ struct fsxattr __user *ufa)
+{
+ struct fsxattr xfa;
+
+ if (copy_from_user(&xfa, ufa, sizeof(xfa)))
+ return -EFAULT;
+
+ fileattr_fill_xflags(fa, xfa.fsx_xflags);
+ fa->fsx_extsize = xfa.fsx_extsize;
+ fa->fsx_nextents = xfa.fsx_nextents;
+ fa->fsx_projid = xfa.fsx_projid;
+ fa->fsx_cowextsize = xfa.fsx_cowextsize;
+
+ return 0;
+}
+
+/*
+ * Generic function to check FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS values and reject
+ * any invalid configurations.
+ *
+ * Note: must be called with inode lock held.
+ */
+static int fileattr_set_prepare(struct inode *inode,
+ const struct fileattr *old_ma,
+ struct fileattr *fa)
+{
+ int err;
+
+ /*
+ * The IMMUTABLE and APPEND_ONLY flags can only be changed by
+ * the relevant capability.
+ */
+ if ((fa->flags ^ old_ma->flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
+ !capable(CAP_LINUX_IMMUTABLE))
+ return -EPERM;
+
+ err = fscrypt_prepare_setflags(inode, old_ma->flags, fa->flags);
+ if (err)
+ return err;
+
+ /*
+ * Project Quota ID state is only allowed to change from within the init
+ * namespace. Enforce that restriction only if we are trying to change
+ * the quota ID state. Everything else is allowed in user namespaces.
+ */
+ if (current_user_ns() != &init_user_ns) {
+ if (old_ma->fsx_projid != fa->fsx_projid)
+ return -EINVAL;
+ if ((old_ma->fsx_xflags ^ fa->fsx_xflags) &
+ FS_XFLAG_PROJINHERIT)
+ return -EINVAL;
+ } else {
+ /*
+ * Caller is allowed to change the project ID. If it is being
+ * changed, make sure that the new value is valid.
+ */
+ if (old_ma->fsx_projid != fa->fsx_projid &&
+ !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid)))
+ return -EINVAL;
+ }
+
+ /* Check extent size hints. */
+ if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode))
+ return -EINVAL;
+
+ if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
+ !S_ISDIR(inode->i_mode))
+ return -EINVAL;
+
+ if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
+ !S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
+ return -EINVAL;
+
+ /*
+ * It is only valid to set the DAX flag on regular files and
+ * directories on filesystems.
+ */
+ if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
+ !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
+ return -EINVAL;
+
+ /* Extent size hints of zero turn off the flags. */
+ if (fa->fsx_extsize == 0)
+ fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
+ if (fa->fsx_cowextsize == 0)
+ fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
+
+ return 0;
+}
+
+/**
+ * vfs_fileattr_set - change miscellaneous file attributes
+ * @idmap: idmap of the mount
+ * @dentry: the object to change
+ * @fa: fileattr pointer
+ *
+ * After verifying permissions, call i_op->fileattr_set() callback, if
+ * exists.
+ *
+ * Verifying attributes involves retrieving current attributes with
+ * i_op->fileattr_get(), this also allows initializing attributes that have
+ * not been set by the caller to current values. Inode lock is held
+ * thoughout to prevent racing with another instance.
+ *
+ * Return: 0 on success, or a negative error on failure.
+ */
+int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct fileattr *fa)
+{
+ struct inode *inode = d_inode(dentry);
+ struct fileattr old_ma = {};
+ int err;
+
+ if (!inode->i_op->fileattr_set)
+ return -ENOIOCTLCMD;
+
+ if (!inode_owner_or_capable(idmap, inode))
+ return -EPERM;
+
+ inode_lock(inode);
+ err = vfs_fileattr_get(dentry, &old_ma);
+ if (!err) {
+ /* initialize missing bits from old_ma */
+ if (fa->flags_valid) {
+ fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON;
+ fa->fsx_extsize = old_ma.fsx_extsize;
+ fa->fsx_nextents = old_ma.fsx_nextents;
+ fa->fsx_projid = old_ma.fsx_projid;
+ fa->fsx_cowextsize = old_ma.fsx_cowextsize;
+ } else {
+ fa->flags |= old_ma.flags & ~FS_COMMON_FL;
+ }
+ err = fileattr_set_prepare(inode, &old_ma, fa);
+ if (!err)
+ err = inode->i_op->fileattr_set(idmap, dentry, fa);
+ }
+ inode_unlock(inode);
+
+ return err;
+}
+EXPORT_SYMBOL(vfs_fileattr_set);
+
+int ioctl_getflags(struct file *file, unsigned int __user *argp)
+{
+ struct fileattr fa = { .flags_valid = true }; /* hint only */
+ int err;
+
+ err = vfs_fileattr_get(file->f_path.dentry, &fa);
+ if (!err)
+ err = put_user(fa.flags, argp);
+ return err;
+}
+EXPORT_SYMBOL(ioctl_getflags);
+
+int ioctl_setflags(struct file *file, unsigned int __user *argp)
+{
+ struct mnt_idmap *idmap = file_mnt_idmap(file);
+ struct dentry *dentry = file->f_path.dentry;
+ struct fileattr fa;
+ unsigned int flags;
+ int err;
+
+ err = get_user(flags, argp);
+ if (!err) {
+ err = mnt_want_write_file(file);
+ if (!err) {
+ fileattr_fill_flags(&fa, flags);
+ err = vfs_fileattr_set(idmap, dentry, &fa);
+ mnt_drop_write_file(file);
+ }
+ }
+ return err;
+}
+EXPORT_SYMBOL(ioctl_setflags);
+
+int ioctl_fsgetxattr(struct file *file, void __user *argp)
+{
+ struct fileattr fa = { .fsx_valid = true }; /* hint only */
+ int err;
+
+ err = vfs_fileattr_get(file->f_path.dentry, &fa);
+ if (!err)
+ err = copy_fsxattr_to_user(&fa, argp);
+
+ return err;
+}
+EXPORT_SYMBOL(ioctl_fsgetxattr);
+
+int ioctl_fssetxattr(struct file *file, void __user *argp)
+{
+ struct mnt_idmap *idmap = file_mnt_idmap(file);
+ struct dentry *dentry = file->f_path.dentry;
+ struct fileattr fa;
+ int err;
+
+ err = copy_fsxattr_from_user(&fa, argp);
+ if (!err) {
+ err = mnt_want_write_file(file);
+ if (!err) {
+ err = vfs_fileattr_set(idmap, dentry, &fa);
+ mnt_drop_write_file(file);
+ }
+ }
+ return err;
+}
+EXPORT_SYMBOL(ioctl_fssetxattr);
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 69107a245b4c..0248cb8db2d3 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -453,315 +453,6 @@ static int ioctl_file_dedupe_range(struct file *file,
return ret;
}
-/**
- * fileattr_fill_xflags - initialize fileattr with xflags
- * @fa: fileattr pointer
- * @xflags: FS_XFLAG_* flags
- *
- * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All
- * other fields are zeroed.
- */
-void fileattr_fill_xflags(struct fileattr *fa, u32 xflags)
-{
- memset(fa, 0, sizeof(*fa));
- fa->fsx_valid = true;
- fa->fsx_xflags = xflags;
- if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
- fa->flags |= FS_IMMUTABLE_FL;
- if (fa->fsx_xflags & FS_XFLAG_APPEND)
- fa->flags |= FS_APPEND_FL;
- if (fa->fsx_xflags & FS_XFLAG_SYNC)
- fa->flags |= FS_SYNC_FL;
- if (fa->fsx_xflags & FS_XFLAG_NOATIME)
- fa->flags |= FS_NOATIME_FL;
- if (fa->fsx_xflags & FS_XFLAG_NODUMP)
- fa->flags |= FS_NODUMP_FL;
- if (fa->fsx_xflags & FS_XFLAG_DAX)
- fa->flags |= FS_DAX_FL;
- if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
- fa->flags |= FS_PROJINHERIT_FL;
-}
-EXPORT_SYMBOL(fileattr_fill_xflags);
-
-/**
- * fileattr_fill_flags - initialize fileattr with flags
- * @fa: fileattr pointer
- * @flags: FS_*_FL flags
- *
- * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
- * All other fields are zeroed.
- */
-void fileattr_fill_flags(struct fileattr *fa, u32 flags)
-{
- memset(fa, 0, sizeof(*fa));
- fa->flags_valid = true;
- fa->flags = flags;
- if (fa->flags & FS_SYNC_FL)
- fa->fsx_xflags |= FS_XFLAG_SYNC;
- if (fa->flags & FS_IMMUTABLE_FL)
- fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
- if (fa->flags & FS_APPEND_FL)
- fa->fsx_xflags |= FS_XFLAG_APPEND;
- if (fa->flags & FS_NODUMP_FL)
- fa->fsx_xflags |= FS_XFLAG_NODUMP;
- if (fa->flags & FS_NOATIME_FL)
- fa->fsx_xflags |= FS_XFLAG_NOATIME;
- if (fa->flags & FS_DAX_FL)
- fa->fsx_xflags |= FS_XFLAG_DAX;
- if (fa->flags & FS_PROJINHERIT_FL)
- fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
-}
-EXPORT_SYMBOL(fileattr_fill_flags);
-
-/**
- * vfs_fileattr_get - retrieve miscellaneous file attributes
- * @dentry: the object to retrieve from
- * @fa: fileattr pointer
- *
- * Call i_op->fileattr_get() callback, if exists.
- *
- * Return: 0 on success, or a negative error on failure.
- */
-int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
-{
- struct inode *inode = d_inode(dentry);
-
- if (!inode->i_op->fileattr_get)
- return -ENOIOCTLCMD;
-
- return inode->i_op->fileattr_get(dentry, fa);
-}
-EXPORT_SYMBOL(vfs_fileattr_get);
-
-/**
- * copy_fsxattr_to_user - copy fsxattr to userspace.
- * @fa: fileattr pointer
- * @ufa: fsxattr user pointer
- *
- * Return: 0 on success, or -EFAULT on failure.
- */
-int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
-{
- struct fsxattr xfa;
-
- memset(&xfa, 0, sizeof(xfa));
- xfa.fsx_xflags = fa->fsx_xflags;
- xfa.fsx_extsize = fa->fsx_extsize;
- xfa.fsx_nextents = fa->fsx_nextents;
- xfa.fsx_projid = fa->fsx_projid;
- xfa.fsx_cowextsize = fa->fsx_cowextsize;
-
- if (copy_to_user(ufa, &xfa, sizeof(xfa)))
- return -EFAULT;
-
- return 0;
-}
-EXPORT_SYMBOL(copy_fsxattr_to_user);
-
-static int copy_fsxattr_from_user(struct fileattr *fa,
- struct fsxattr __user *ufa)
-{
- struct fsxattr xfa;
-
- if (copy_from_user(&xfa, ufa, sizeof(xfa)))
- return -EFAULT;
-
- fileattr_fill_xflags(fa, xfa.fsx_xflags);
- fa->fsx_extsize = xfa.fsx_extsize;
- fa->fsx_nextents = xfa.fsx_nextents;
- fa->fsx_projid = xfa.fsx_projid;
- fa->fsx_cowextsize = xfa.fsx_cowextsize;
-
- return 0;
-}
-
-/*
- * Generic function to check FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS values and reject
- * any invalid configurations.
- *
- * Note: must be called with inode lock held.
- */
-static int fileattr_set_prepare(struct inode *inode,
- const struct fileattr *old_ma,
- struct fileattr *fa)
-{
- int err;
-
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- if ((fa->flags ^ old_ma->flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
- !capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
-
- err = fscrypt_prepare_setflags(inode, old_ma->flags, fa->flags);
- if (err)
- return err;
-
- /*
- * Project Quota ID state is only allowed to change from within the init
- * namespace. Enforce that restriction only if we are trying to change
- * the quota ID state. Everything else is allowed in user namespaces.
- */
- if (current_user_ns() != &init_user_ns) {
- if (old_ma->fsx_projid != fa->fsx_projid)
- return -EINVAL;
- if ((old_ma->fsx_xflags ^ fa->fsx_xflags) &
- FS_XFLAG_PROJINHERIT)
- return -EINVAL;
- } else {
- /*
- * Caller is allowed to change the project ID. If it is being
- * changed, make sure that the new value is valid.
- */
- if (old_ma->fsx_projid != fa->fsx_projid &&
- !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid)))
- return -EINVAL;
- }
-
- /* Check extent size hints. */
- if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode))
- return -EINVAL;
-
- if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
- !S_ISDIR(inode->i_mode))
- return -EINVAL;
-
- if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
- !S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
- return -EINVAL;
-
- /*
- * It is only valid to set the DAX flag on regular files and
- * directories on filesystems.
- */
- if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
- !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
- return -EINVAL;
-
- /* Extent size hints of zero turn off the flags. */
- if (fa->fsx_extsize == 0)
- fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
- if (fa->fsx_cowextsize == 0)
- fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
-
- return 0;
-}
-
-/**
- * vfs_fileattr_set - change miscellaneous file attributes
- * @idmap: idmap of the mount
- * @dentry: the object to change
- * @fa: fileattr pointer
- *
- * After verifying permissions, call i_op->fileattr_set() callback, if
- * exists.
- *
- * Verifying attributes involves retrieving current attributes with
- * i_op->fileattr_get(), this also allows initializing attributes that have
- * not been set by the caller to current values. Inode lock is held
- * thoughout to prevent racing with another instance.
- *
- * Return: 0 on success, or a negative error on failure.
- */
-int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
- struct fileattr *fa)
-{
- struct inode *inode = d_inode(dentry);
- struct fileattr old_ma = {};
- int err;
-
- if (!inode->i_op->fileattr_set)
- return -ENOIOCTLCMD;
-
- if (!inode_owner_or_capable(idmap, inode))
- return -EPERM;
-
- inode_lock(inode);
- err = vfs_fileattr_get(dentry, &old_ma);
- if (!err) {
- /* initialize missing bits from old_ma */
- if (fa->flags_valid) {
- fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON;
- fa->fsx_extsize = old_ma.fsx_extsize;
- fa->fsx_nextents = old_ma.fsx_nextents;
- fa->fsx_projid = old_ma.fsx_projid;
- fa->fsx_cowextsize = old_ma.fsx_cowextsize;
- } else {
- fa->flags |= old_ma.flags & ~FS_COMMON_FL;
- }
- err = fileattr_set_prepare(inode, &old_ma, fa);
- if (!err)
- err = inode->i_op->fileattr_set(idmap, dentry, fa);
- }
- inode_unlock(inode);
-
- return err;
-}
-EXPORT_SYMBOL(vfs_fileattr_set);
-
-static int ioctl_getflags(struct file *file, unsigned int __user *argp)
-{
- struct fileattr fa = { .flags_valid = true }; /* hint only */
- int err;
-
- err = vfs_fileattr_get(file->f_path.dentry, &fa);
- if (!err)
- err = put_user(fa.flags, argp);
- return err;
-}
-
-static int ioctl_setflags(struct file *file, unsigned int __user *argp)
-{
- struct mnt_idmap *idmap = file_mnt_idmap(file);
- struct dentry *dentry = file->f_path.dentry;
- struct fileattr fa;
- unsigned int flags;
- int err;
-
- err = get_user(flags, argp);
- if (!err) {
- err = mnt_want_write_file(file);
- if (!err) {
- fileattr_fill_flags(&fa, flags);
- err = vfs_fileattr_set(idmap, dentry, &fa);
- mnt_drop_write_file(file);
- }
- }
- return err;
-}
-
-static int ioctl_fsgetxattr(struct file *file, void __user *argp)
-{
- struct fileattr fa = { .fsx_valid = true }; /* hint only */
- int err;
-
- err = vfs_fileattr_get(file->f_path.dentry, &fa);
- if (!err)
- err = copy_fsxattr_to_user(&fa, argp);
-
- return err;
-}
-
-static int ioctl_fssetxattr(struct file *file, void __user *argp)
-{
- struct mnt_idmap *idmap = file_mnt_idmap(file);
- struct dentry *dentry = file->f_path.dentry;
- struct fileattr fa;
- int err;
-
- err = copy_fsxattr_from_user(&fa, argp);
- if (!err) {
- err = mnt_want_write_file(file);
- if (!err) {
- err = vfs_fileattr_set(idmap, dentry, &fa);
- mnt_drop_write_file(file);
- }
- }
- return err;
-}
-
static int ioctl_getfsuuid(struct file *file, void __user *argp)
{
struct super_block *sb = file_inode(file)->i_sb;
diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h
index 47c05a9851d0..6030d0bf7ad3 100644
--- a/include/linux/fileattr.h
+++ b/include/linux/fileattr.h
@@ -55,5 +55,9 @@ static inline bool fileattr_has_fsx(const struct fileattr *fa)
int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
struct fileattr *fa);
+int ioctl_getflags(struct file *file, unsigned int __user *argp);
+int ioctl_setflags(struct file *file, unsigned int __user *argp);
+int ioctl_fsgetxattr(struct file *file, void __user *argp);
+int ioctl_fssetxattr(struct file *file, void __user *argp);
#endif /* _LINUX_FILEATTR_H */
--
2.47.2
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH v6 2/6] lsm: introduce new hooks for setting/getting inode fsxattr
2025-06-30 16:20 [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
2025-06-30 16:20 ` [PATCH v6 1/6] fs: split fileattr related helpers into separate file Andrey Albershteyn
@ 2025-06-30 16:20 ` Andrey Albershteyn
2025-07-01 12:39 ` Jan Kara
2025-07-01 18:18 ` Darrick J. Wong
2025-06-30 16:20 ` [PATCH v6 3/6] selinux: implement inode_file_[g|s]etattr hooks Andrey Albershteyn
` (6 subsequent siblings)
8 siblings, 2 replies; 47+ messages in thread
From: Andrey Albershteyn @ 2025-06-30 16:20 UTC (permalink / raw)
To: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore
Cc: linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
Introduce new hooks for setting and getting filesystem extended
attributes on inode (FS_IOC_FSGETXATTR).
Cc: selinux@vger.kernel.org
Cc: Paul Moore <paul@paul-moore.com>
Acked-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
fs/file_attr.c | 19 ++++++++++++++++---
include/linux/lsm_hook_defs.h | 2 ++
include/linux/security.h | 16 ++++++++++++++++
security/security.c | 30 ++++++++++++++++++++++++++++++
4 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/fs/file_attr.c b/fs/file_attr.c
index 2910b7047721..be62d97cc444 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -76,10 +76,15 @@ EXPORT_SYMBOL(fileattr_fill_flags);
int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
{
struct inode *inode = d_inode(dentry);
+ int error;
if (!inode->i_op->fileattr_get)
return -ENOIOCTLCMD;
+ error = security_inode_file_getattr(dentry, fa);
+ if (error)
+ return error;
+
return inode->i_op->fileattr_get(dentry, fa);
}
EXPORT_SYMBOL(vfs_fileattr_get);
@@ -242,12 +247,20 @@ int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
} else {
fa->flags |= old_ma.flags & ~FS_COMMON_FL;
}
+
err = fileattr_set_prepare(inode, &old_ma, fa);
- if (!err)
- err = inode->i_op->fileattr_set(idmap, dentry, fa);
+ if (err)
+ goto out;
+ err = security_inode_file_setattr(dentry, fa);
+ if (err)
+ goto out;
+ err = inode->i_op->fileattr_set(idmap, dentry, fa);
+ if (err)
+ goto out;
}
+
+out:
inode_unlock(inode);
-
return err;
}
EXPORT_SYMBOL(vfs_fileattr_set);
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index bf3bbac4e02a..9600a4350e79 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -157,6 +157,8 @@ LSM_HOOK(int, 0, inode_removexattr, struct mnt_idmap *idmap,
struct dentry *dentry, const char *name)
LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
const char *name)
+LSM_HOOK(int, 0, inode_file_setattr, struct dentry *dentry, struct fileattr *fa)
+LSM_HOOK(int, 0, inode_file_getattr, struct dentry *dentry, struct fileattr *fa)
LSM_HOOK(int, 0, inode_set_acl, struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)
LSM_HOOK(void, LSM_RET_VOID, inode_post_set_acl, struct dentry *dentry,
diff --git a/include/linux/security.h b/include/linux/security.h
index dba349629229..9ed0d0e0c81f 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -451,6 +451,10 @@ int security_inode_listxattr(struct dentry *dentry);
int security_inode_removexattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name);
void security_inode_post_removexattr(struct dentry *dentry, const char *name);
+int security_inode_file_setattr(struct dentry *dentry,
+ struct fileattr *fa);
+int security_inode_file_getattr(struct dentry *dentry,
+ struct fileattr *fa);
int security_inode_need_killpriv(struct dentry *dentry);
int security_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry);
int security_inode_getsecurity(struct mnt_idmap *idmap,
@@ -1052,6 +1056,18 @@ static inline void security_inode_post_removexattr(struct dentry *dentry,
const char *name)
{ }
+static inline int security_inode_file_setattr(struct dentry *dentry,
+ struct fileattr *fa)
+{
+ return 0;
+}
+
+static inline int security_inode_file_getattr(struct dentry *dentry,
+ struct fileattr *fa)
+{
+ return 0;
+}
+
static inline int security_inode_need_killpriv(struct dentry *dentry)
{
return cap_inode_need_killpriv(dentry);
diff --git a/security/security.c b/security/security.c
index 596d41818577..711b4de40b8d 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2622,6 +2622,36 @@ void security_inode_post_removexattr(struct dentry *dentry, const char *name)
call_void_hook(inode_post_removexattr, dentry, name);
}
+/**
+ * security_inode_file_setattr() - check if setting fsxattr is allowed
+ * @dentry: file to set filesystem extended attributes on
+ * @fa: extended attributes to set on the inode
+ *
+ * Called when file_setattr() syscall or FS_IOC_FSSETXATTR ioctl() is called on
+ * inode
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_inode_file_setattr(struct dentry *dentry, struct fileattr *fa)
+{
+ return call_int_hook(inode_file_setattr, dentry, fa);
+}
+
+/**
+ * security_inode_file_getattr() - check if retrieving fsxattr is allowed
+ * @dentry: file to retrieve filesystem extended attributes from
+ * @fa: extended attributes to get
+ *
+ * Called when file_getattr() syscall or FS_IOC_FSGETXATTR ioctl() is called on
+ * inode
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_inode_file_getattr(struct dentry *dentry, struct fileattr *fa)
+{
+ return call_int_hook(inode_file_getattr, dentry, fa);
+}
+
/**
* security_inode_need_killpriv() - Check if security_inode_killpriv() required
* @dentry: associated dentry
--
2.47.2
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH v6 3/6] selinux: implement inode_file_[g|s]etattr hooks
2025-06-30 16:20 [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
2025-06-30 16:20 ` [PATCH v6 1/6] fs: split fileattr related helpers into separate file Andrey Albershteyn
2025-06-30 16:20 ` [PATCH v6 2/6] lsm: introduce new hooks for setting/getting inode fsxattr Andrey Albershteyn
@ 2025-06-30 16:20 ` Andrey Albershteyn
2025-06-30 16:20 ` [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP Andrey Albershteyn
` (5 subsequent siblings)
8 siblings, 0 replies; 47+ messages in thread
From: Andrey Albershteyn @ 2025-06-30 16:20 UTC (permalink / raw)
To: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore
Cc: linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
These hooks are called on inode extended attribute retrieval/change.
Cc: selinux@vger.kernel.org
Cc: Paul Moore <paul@paul-moore.com>
Acked-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
security/selinux/hooks.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 595ceb314aeb..be7aca2269fa 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3480,6 +3480,18 @@ static int selinux_inode_removexattr(struct mnt_idmap *idmap,
return -EACCES;
}
+static int selinux_inode_file_setattr(struct dentry *dentry,
+ struct fileattr *fa)
+{
+ return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
+}
+
+static int selinux_inode_file_getattr(struct dentry *dentry,
+ struct fileattr *fa)
+{
+ return dentry_has_perm(current_cred(), dentry, FILE__GETATTR);
+}
+
static int selinux_path_notify(const struct path *path, u64 mask,
unsigned int obj_type)
{
@@ -7350,6 +7362,8 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr),
LSM_HOOK_INIT(inode_listxattr, selinux_inode_listxattr),
LSM_HOOK_INIT(inode_removexattr, selinux_inode_removexattr),
+ LSM_HOOK_INIT(inode_file_getattr, selinux_inode_file_getattr),
+ LSM_HOOK_INIT(inode_file_setattr, selinux_inode_file_setattr),
LSM_HOOK_INIT(inode_set_acl, selinux_inode_set_acl),
LSM_HOOK_INIT(inode_get_acl, selinux_inode_get_acl),
LSM_HOOK_INIT(inode_remove_acl, selinux_inode_remove_acl),
--
2.47.2
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP
2025-06-30 16:20 [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
` (2 preceding siblings ...)
2025-06-30 16:20 ` [PATCH v6 3/6] selinux: implement inode_file_[g|s]etattr hooks Andrey Albershteyn
@ 2025-06-30 16:20 ` Andrey Albershteyn
2025-06-30 18:05 ` Pali Rohár
` (3 more replies)
2025-06-30 16:20 ` [PATCH v6 5/6] fs: prepare for extending file_get/setattr() Andrey Albershteyn
` (4 subsequent siblings)
8 siblings, 4 replies; 47+ messages in thread
From: Andrey Albershteyn @ 2025-06-30 16:20 UTC (permalink / raw)
To: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore
Cc: linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
Future patches will add new syscalls which use these functions. As
this interface won't be used for ioctls only, the EOPNOSUPP is more
appropriate return code.
This patch converts return code from ENOIOCTLCMD to EOPNOSUPP for
vfs_fileattr_get and vfs_fileattr_set. To save old behavior translate
EOPNOSUPP back for current users - overlayfs, encryptfs and fs/ioctl.c.
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
fs/ecryptfs/inode.c | 8 +++++++-
fs/file_attr.c | 12 ++++++++++--
fs/overlayfs/inode.c | 2 +-
3 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 493d7f194956..a55c1375127f 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -1126,7 +1126,13 @@ static int ecryptfs_removexattr(struct dentry *dentry, struct inode *inode,
static int ecryptfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
{
- return vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
+ int rc;
+
+ rc = vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
+ if (rc == -EOPNOTSUPP)
+ rc = -ENOIOCTLCMD;
+
+ return rc;
}
static int ecryptfs_fileattr_set(struct mnt_idmap *idmap,
diff --git a/fs/file_attr.c b/fs/file_attr.c
index be62d97cc444..4e85fa00c092 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -79,7 +79,7 @@ int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
int error;
if (!inode->i_op->fileattr_get)
- return -ENOIOCTLCMD;
+ return -EOPNOTSUPP;
error = security_inode_file_getattr(dentry, fa);
if (error)
@@ -229,7 +229,7 @@ int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
int err;
if (!inode->i_op->fileattr_set)
- return -ENOIOCTLCMD;
+ return -EOPNOTSUPP;
if (!inode_owner_or_capable(idmap, inode))
return -EPERM;
@@ -271,6 +271,8 @@ int ioctl_getflags(struct file *file, unsigned int __user *argp)
int err;
err = vfs_fileattr_get(file->f_path.dentry, &fa);
+ if (err == -EOPNOTSUPP)
+ err = -ENOIOCTLCMD;
if (!err)
err = put_user(fa.flags, argp);
return err;
@@ -292,6 +294,8 @@ int ioctl_setflags(struct file *file, unsigned int __user *argp)
fileattr_fill_flags(&fa, flags);
err = vfs_fileattr_set(idmap, dentry, &fa);
mnt_drop_write_file(file);
+ if (err == -EOPNOTSUPP)
+ err = -ENOIOCTLCMD;
}
}
return err;
@@ -304,6 +308,8 @@ int ioctl_fsgetxattr(struct file *file, void __user *argp)
int err;
err = vfs_fileattr_get(file->f_path.dentry, &fa);
+ if (err == -EOPNOTSUPP)
+ err = -ENOIOCTLCMD;
if (!err)
err = copy_fsxattr_to_user(&fa, argp);
@@ -324,6 +330,8 @@ int ioctl_fssetxattr(struct file *file, void __user *argp)
if (!err) {
err = vfs_fileattr_set(idmap, dentry, &fa);
mnt_drop_write_file(file);
+ if (err == -EOPNOTSUPP)
+ err = -ENOIOCTLCMD;
}
}
return err;
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 6f0e15f86c21..096d44712bb1 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -721,7 +721,7 @@ int ovl_real_fileattr_get(const struct path *realpath, struct fileattr *fa)
return err;
err = vfs_fileattr_get(realpath->dentry, fa);
- if (err == -ENOIOCTLCMD)
+ if (err == -EOPNOTSUPP)
err = -ENOTTY;
return err;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH v6 5/6] fs: prepare for extending file_get/setattr()
2025-06-30 16:20 [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
` (3 preceding siblings ...)
2025-06-30 16:20 ` [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP Andrey Albershteyn
@ 2025-06-30 16:20 ` Andrey Albershteyn
2025-07-01 13:06 ` Jan Kara
2025-07-01 18:31 ` Darrick J. Wong
2025-06-30 16:20 ` [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
` (3 subsequent siblings)
8 siblings, 2 replies; 47+ messages in thread
From: Andrey Albershteyn @ 2025-06-30 16:20 UTC (permalink / raw)
To: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore
Cc: linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn, Andrey Albershteyn
From: Amir Goldstein <amir73il@gmail.com>
We intend to add support for more xflags to selective filesystems and
We cannot rely on copy_struct_from_user() to detect this extension.
In preparation of extending the API, do not allow setting xflags unknown
by this kernel version.
Also do not pass the read-only flags and read-only field fsx_nextents to
filesystem.
These changes should not affect existing chattr programs that use the
ioctl to get fsxattr before setting the new values.
Link: https://lore.kernel.org/linux-fsdevel/20250216164029.20673-4-pali@kernel.org/
Cc: Pali Rohár <pali@kernel.org>
Cc: Andrey Albershteyn <aalbersh@redhat.com>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
fs/file_attr.c | 8 +++++++-
include/linux/fileattr.h | 20 ++++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/fs/file_attr.c b/fs/file_attr.c
index 4e85fa00c092..62f08872d4ad 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -99,9 +99,10 @@ EXPORT_SYMBOL(vfs_fileattr_get);
int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
{
struct fsxattr xfa;
+ __u32 mask = FS_XFLAGS_MASK;
memset(&xfa, 0, sizeof(xfa));
- xfa.fsx_xflags = fa->fsx_xflags;
+ xfa.fsx_xflags = fa->fsx_xflags & mask;
xfa.fsx_extsize = fa->fsx_extsize;
xfa.fsx_nextents = fa->fsx_nextents;
xfa.fsx_projid = fa->fsx_projid;
@@ -118,11 +119,16 @@ static int copy_fsxattr_from_user(struct fileattr *fa,
struct fsxattr __user *ufa)
{
struct fsxattr xfa;
+ __u32 mask = FS_XFLAGS_MASK;
if (copy_from_user(&xfa, ufa, sizeof(xfa)))
return -EFAULT;
+ if (xfa.fsx_xflags & ~mask)
+ return -EINVAL;
+
fileattr_fill_xflags(fa, xfa.fsx_xflags);
+ fa->fsx_xflags &= ~FS_XFLAG_RDONLY_MASK;
fa->fsx_extsize = xfa.fsx_extsize;
fa->fsx_nextents = xfa.fsx_nextents;
fa->fsx_projid = xfa.fsx_projid;
diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h
index 6030d0bf7ad3..e2a2f4ae242d 100644
--- a/include/linux/fileattr.h
+++ b/include/linux/fileattr.h
@@ -14,6 +14,26 @@
FS_XFLAG_NODUMP | FS_XFLAG_NOATIME | FS_XFLAG_DAX | \
FS_XFLAG_PROJINHERIT)
+/* Read-only inode flags */
+#define FS_XFLAG_RDONLY_MASK \
+ (FS_XFLAG_PREALLOC | FS_XFLAG_HASATTR)
+
+/* Flags to indicate valid value of fsx_ fields */
+#define FS_XFLAG_VALUES_MASK \
+ (FS_XFLAG_EXTSIZE | FS_XFLAG_COWEXTSIZE)
+
+/* Flags for directories */
+#define FS_XFLAG_DIRONLY_MASK \
+ (FS_XFLAG_RTINHERIT | FS_XFLAG_NOSYMLINKS | FS_XFLAG_EXTSZINHERIT)
+
+/* Misc settable flags */
+#define FS_XFLAG_MISC_MASK \
+ (FS_XFLAG_REALTIME | FS_XFLAG_NODEFRAG | FS_XFLAG_FILESTREAM)
+
+#define FS_XFLAGS_MASK \
+ (FS_XFLAG_COMMON | FS_XFLAG_RDONLY_MASK | FS_XFLAG_VALUES_MASK | \
+ FS_XFLAG_DIRONLY_MASK | FS_XFLAG_MISC_MASK)
+
/*
* Merged interface for miscellaneous file attributes. 'flags' originates from
* ext* and 'fsx_flags' from xfs. There's some overlap between the two, which
--
2.47.2
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-06-30 16:20 [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
` (4 preceding siblings ...)
2025-06-30 16:20 ` [PATCH v6 5/6] fs: prepare for extending file_get/setattr() Andrey Albershteyn
@ 2025-06-30 16:20 ` Andrey Albershteyn
2025-07-01 12:34 ` Christian Brauner
` (2 more replies)
2025-07-01 6:11 ` [PATCH v6 0/6] " Amir Goldstein
` (2 subsequent siblings)
8 siblings, 3 replies; 47+ messages in thread
From: Andrey Albershteyn @ 2025-06-30 16:20 UTC (permalink / raw)
To: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore
Cc: linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn, Andrey Albershteyn
From: Andrey Albershteyn <aalbersh@redhat.com>
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 fsx_fileattr 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>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
arch/alpha/kernel/syscalls/syscall.tbl | 2 +
arch/arm/tools/syscall.tbl | 2 +
arch/arm64/tools/syscall_32.tbl | 2 +
arch/m68k/kernel/syscalls/syscall.tbl | 2 +
arch/microblaze/kernel/syscalls/syscall.tbl | 2 +
arch/mips/kernel/syscalls/syscall_n32.tbl | 2 +
arch/mips/kernel/syscalls/syscall_n64.tbl | 2 +
arch/mips/kernel/syscalls/syscall_o32.tbl | 2 +
arch/parisc/kernel/syscalls/syscall.tbl | 2 +
arch/powerpc/kernel/syscalls/syscall.tbl | 2 +
arch/s390/kernel/syscalls/syscall.tbl | 2 +
arch/sh/kernel/syscalls/syscall.tbl | 2 +
arch/sparc/kernel/syscalls/syscall.tbl | 2 +
arch/x86/entry/syscalls/syscall_32.tbl | 2 +
arch/x86/entry/syscalls/syscall_64.tbl | 2 +
arch/xtensa/kernel/syscalls/syscall.tbl | 2 +
fs/file_attr.c | 148 ++++++++++++++++++++++++++++
include/linux/syscalls.h | 6 ++
include/uapi/asm-generic/unistd.h | 8 +-
include/uapi/linux/fs.h | 18 ++++
scripts/syscall.tbl | 2 +
21 files changed, 213 insertions(+), 1 deletion(-)
diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index 2dd6340de6b4..16dca28ebf17 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -507,3 +507,5 @@
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
diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
index 27c1d5ebcd91..b07e699aaa3c 100644
--- a/arch/arm/tools/syscall.tbl
+++ b/arch/arm/tools/syscall.tbl
@@ -482,3 +482,5 @@
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
diff --git a/arch/arm64/tools/syscall_32.tbl b/arch/arm64/tools/syscall_32.tbl
index 0765b3a8d6d6..8d9088bc577d 100644
--- a/arch/arm64/tools/syscall_32.tbl
+++ b/arch/arm64/tools/syscall_32.tbl
@@ -479,3 +479,5 @@
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
diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
index 9fe47112c586..f41d38dfbf13 100644
--- a/arch/m68k/kernel/syscalls/syscall.tbl
+++ b/arch/m68k/kernel/syscalls/syscall.tbl
@@ -467,3 +467,5 @@
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
diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
index 7b6e97828e55..580af574fe73 100644
--- a/arch/microblaze/kernel/syscalls/syscall.tbl
+++ b/arch/microblaze/kernel/syscalls/syscall.tbl
@@ -473,3 +473,5 @@
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
diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
index aa70e371bb54..d824ffe9a014 100644
--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
@@ -406,3 +406,5 @@
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
diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
index 1e8c44c7b614..7a7049c2c307 100644
--- a/arch/mips/kernel/syscalls/syscall_n64.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
@@ -382,3 +382,5 @@
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
diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
index 114a5a1a6230..d330274f0601 100644
--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
@@ -455,3 +455,5 @@
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
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index 94df3cb957e9..88a788a7b18d 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -466,3 +466,5 @@
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
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index 9a084bdb8926..b453e80dfc00 100644
--- a/arch/powerpc/kernel/syscalls/syscall.tbl
+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
@@ -558,3 +558,5 @@
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
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index a4569b96ef06..8a6744d658db 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -470,3 +470,5 @@
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
diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index 52a7652fcff6..5e9c9eff5539 100644
--- a/arch/sh/kernel/syscalls/syscall.tbl
+++ b/arch/sh/kernel/syscalls/syscall.tbl
@@ -471,3 +471,5 @@
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
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index 83e45eb6c095..ebb7d06d1044 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -513,3 +513,5 @@
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
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index ac007ea00979..4877e16da69a 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -473,3 +473,5 @@
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
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index cfb5ca41e30d..92cf0fe2291e 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -391,6 +391,8 @@
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
diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
index f657a77314f8..374e4cb788d8 100644
--- a/arch/xtensa/kernel/syscalls/syscall.tbl
+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
@@ -438,3 +438,5 @@
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
diff --git a/fs/file_attr.c b/fs/file_attr.c
index 62f08872d4ad..fda9d847eee5 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -3,6 +3,10 @@
#include <linux/security.h>
#include <linux/fscrypt.h>
#include <linux/fileattr.h>
+#include <linux/syscalls.h>
+#include <linux/namei.h>
+
+#include "internal.h"
/**
* fileattr_fill_xflags - initialize fileattr with xflags
@@ -89,6 +93,19 @@ int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
EXPORT_SYMBOL(vfs_fileattr_get);
+static void fileattr_to_fsx_fileattr(const struct fileattr *fa,
+ struct fsx_fileattr *fsx)
+{
+ __u32 mask = FS_XFLAGS_MASK;
+
+ memset(fsx, 0, sizeof(struct fsx_fileattr));
+ fsx->fsx_xflags = fa->fsx_xflags & mask;
+ fsx->fsx_extsize = fa->fsx_extsize;
+ fsx->fsx_nextents = fa->fsx_nextents;
+ fsx->fsx_projid = fa->fsx_projid;
+ fsx->fsx_cowextsize = fa->fsx_cowextsize;
+}
+
/**
* copy_fsxattr_to_user - copy fsxattr to userspace.
* @fa: fileattr pointer
@@ -115,6 +132,23 @@ int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
}
EXPORT_SYMBOL(copy_fsxattr_to_user);
+static int fsx_fileattr_to_fileattr(const struct fsx_fileattr *fsx,
+ struct fileattr *fa)
+{
+ __u32 mask = FS_XFLAGS_MASK;
+
+ if (fsx->fsx_xflags & ~mask)
+ return -EINVAL;
+
+ fileattr_fill_xflags(fa, fsx->fsx_xflags);
+ fa->fsx_xflags &= ~FS_XFLAG_RDONLY_MASK;
+ fa->fsx_extsize = fsx->fsx_extsize;
+ fa->fsx_projid = fsx->fsx_projid;
+ fa->fsx_cowextsize = fsx->fsx_cowextsize;
+
+ return 0;
+}
+
static int copy_fsxattr_from_user(struct fileattr *fa,
struct fsxattr __user *ufa)
{
@@ -343,3 +377,117 @@ 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 fsx_fileattr __user *, ufsx, size_t, usize,
+ unsigned int, at_flags)
+{
+ struct fileattr fa;
+ struct path filepath __free(path_put) = {};
+ int error;
+ unsigned int lookup_flags = 0;
+ struct filename *name __free(putname) = NULL;
+ struct fsx_fileattr fsx;
+
+ BUILD_BUG_ON(sizeof(struct fsx_fileattr) < FSX_FILEATTR_SIZE_VER0);
+ BUILD_BUG_ON(sizeof(struct fsx_fileattr) != FSX_FILEATTR_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 < FSX_FILEATTR_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);
+
+ 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_fsx_fileattr(&fa, &fsx);
+ error = copy_struct_to_user(ufsx, usize, &fsx,
+ sizeof(struct fsx_fileattr), NULL);
+
+ return error;
+}
+
+SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename,
+ struct fsx_fileattr __user *, ufsx, size_t, usize,
+ unsigned int, at_flags)
+{
+ struct fileattr fa;
+ struct path filepath __free(path_put) = {};
+ int error;
+ unsigned int lookup_flags = 0;
+ struct filename *name __free(putname) = NULL;
+ struct fsx_fileattr fsx;
+
+ BUILD_BUG_ON(sizeof(struct fsx_fileattr) < FSX_FILEATTR_SIZE_VER0);
+ BUILD_BUG_ON(sizeof(struct fsx_fileattr) != FSX_FILEATTR_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 < FSX_FILEATTR_SIZE_VER0)
+ return -EINVAL;
+
+ error = copy_struct_from_user(&fsx, sizeof(struct fsx_fileattr), ufsx,
+ usize);
+ if (error)
+ return error;
+
+ error = fsx_fileattr_to_fileattr(&fsx, &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);
+
+ 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;
+}
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index e5603cc91963..179acbe28fec 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -371,6 +371,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 fsx_fileattr __user *ufsx, size_t usize,
+ unsigned int at_flags);
+asmlinkage long sys_file_setattr(int dfd, const char __user *filename,
+ struct fsx_fileattr __user *ufsx, 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);
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 2892a45023af..04e0077fb4c9 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -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
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 0098b0ce8ccb..0784f2033ba4 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -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 fsx_fileattr {
+ __u32 fsx_xflags; /* xflags field value (get/set) */
+ __u32 fsx_extsize; /* extsize field value (get/set)*/
+ __u32 fsx_nextents; /* nextents field value (get) */
+ __u32 fsx_projid; /* project identifier (get/set) */
+ __u32 fsx_cowextsize; /* CoW extsize field value (get/set) */
+};
+
+#define FSX_FILEATTR_SIZE_VER0 20
+#define FSX_FILEATTR_SIZE_LATEST FSX_FILEATTR_SIZE_VER0
+
/*
* Flags for the fsx_xflags field
*/
diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
index 580b4e246aec..d1ae5e92c615 100644
--- a/scripts/syscall.tbl
+++ b/scripts/syscall.tbl
@@ -408,3 +408,5 @@
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
--
2.47.2
^ permalink raw reply related [flat|nested] 47+ messages in thread
* Re: [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP
2025-06-30 16:20 ` [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP Andrey Albershteyn
@ 2025-06-30 18:05 ` Pali Rohár
2025-07-01 6:05 ` Amir Goldstein
` (2 subsequent siblings)
3 siblings, 0 replies; 47+ messages in thread
From: Pali Rohár @ 2025-06-30 18:05 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Paul Moore, linux-api, linux-fsdevel, linux-kernel,
linux-xfs, selinux, Andrey Albershteyn
nit: typo in commit subject and description: Missing T in EOPNO*T*SUPP.
But please do not resend whole patch series just because of this.
That is not needed.
On Monday 30 June 2025 18:20:14 Andrey Albershteyn wrote:
> Future patches will add new syscalls which use these functions. As
> this interface won't be used for ioctls only, the EOPNOSUPP is more
> appropriate return code.
>
> This patch converts return code from ENOIOCTLCMD to EOPNOSUPP for
> vfs_fileattr_get and vfs_fileattr_set. To save old behavior translate
> EOPNOSUPP back for current users - overlayfs, encryptfs and fs/ioctl.c.
>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> ---
> fs/ecryptfs/inode.c | 8 +++++++-
> fs/file_attr.c | 12 ++++++++++--
> fs/overlayfs/inode.c | 2 +-
> 3 files changed, 18 insertions(+), 4 deletions(-)
>
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 493d7f194956..a55c1375127f 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -1126,7 +1126,13 @@ static int ecryptfs_removexattr(struct dentry *dentry, struct inode *inode,
>
> static int ecryptfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> {
> - return vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
> + int rc;
> +
> + rc = vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
> + if (rc == -EOPNOTSUPP)
> + rc = -ENOIOCTLCMD;
> +
> + return rc;
> }
>
> static int ecryptfs_fileattr_set(struct mnt_idmap *idmap,
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> index be62d97cc444..4e85fa00c092 100644
> --- a/fs/file_attr.c
> +++ b/fs/file_attr.c
> @@ -79,7 +79,7 @@ int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> int error;
>
> if (!inode->i_op->fileattr_get)
> - return -ENOIOCTLCMD;
> + return -EOPNOTSUPP;
>
> error = security_inode_file_getattr(dentry, fa);
> if (error)
> @@ -229,7 +229,7 @@ int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> int err;
>
> if (!inode->i_op->fileattr_set)
> - return -ENOIOCTLCMD;
> + return -EOPNOTSUPP;
>
> if (!inode_owner_or_capable(idmap, inode))
> return -EPERM;
> @@ -271,6 +271,8 @@ int ioctl_getflags(struct file *file, unsigned int __user *argp)
> int err;
>
> err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> if (!err)
> err = put_user(fa.flags, argp);
> return err;
> @@ -292,6 +294,8 @@ int ioctl_setflags(struct file *file, unsigned int __user *argp)
> fileattr_fill_flags(&fa, flags);
> err = vfs_fileattr_set(idmap, dentry, &fa);
> mnt_drop_write_file(file);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> }
> }
> return err;
> @@ -304,6 +308,8 @@ int ioctl_fsgetxattr(struct file *file, void __user *argp)
> int err;
>
> err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> if (!err)
> err = copy_fsxattr_to_user(&fa, argp);
>
> @@ -324,6 +330,8 @@ int ioctl_fssetxattr(struct file *file, void __user *argp)
> if (!err) {
> err = vfs_fileattr_set(idmap, dentry, &fa);
> mnt_drop_write_file(file);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> }
> }
> return err;
> diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
> index 6f0e15f86c21..096d44712bb1 100644
> --- a/fs/overlayfs/inode.c
> +++ b/fs/overlayfs/inode.c
> @@ -721,7 +721,7 @@ int ovl_real_fileattr_get(const struct path *realpath, struct fileattr *fa)
> return err;
>
> err = vfs_fileattr_get(realpath->dentry, fa);
> - if (err == -ENOIOCTLCMD)
> + if (err == -EOPNOTSUPP)
> err = -ENOTTY;
> return err;
> }
>
> --
> 2.47.2
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 1/6] fs: split fileattr related helpers into separate file
2025-06-30 16:20 ` [PATCH v6 1/6] fs: split fileattr related helpers into separate file Andrey Albershteyn
@ 2025-07-01 5:39 ` Amir Goldstein
2025-07-01 12:38 ` Jan Kara
2025-07-01 18:13 ` Darrick J. Wong
2 siblings, 0 replies; 47+ messages in thread
From: Amir Goldstein @ 2025-07-01 5:39 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Arnd Bergmann, Casey Schaufler, Christian Brauner, Jan Kara,
Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon, Jun 30, 2025 at 6:20 PM Andrey Albershteyn <aalbersh@redhat.com> wrote:
>
> From: Andrey Albershteyn <aalbersh@kernel.org>
>
> This patch moves function related to file extended attributes
> manipulations to separate file. Refactoring only.
>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
> ---
> fs/Makefile | 3 +-
> fs/file_attr.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++
> fs/ioctl.c | 309 ---------------------------------------------
> include/linux/fileattr.h | 4 +
> 4 files changed, 324 insertions(+), 310 deletions(-)
>
> diff --git a/fs/Makefile b/fs/Makefile
> index 79c08b914c47..334654f9584b 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -15,7 +15,8 @@ obj-y := open.o read_write.o file_table.o super.o \
> pnode.o splice.o sync.o utimes.o d_path.o \
> stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
> fs_types.o fs_context.o fs_parser.o fsopen.o init.o \
> - kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o
> + kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o \
> + file_attr.o
>
> obj-$(CONFIG_BUFFER_HEAD) += buffer.o mpage.o
> obj-$(CONFIG_PROC_FS) += proc_namespace.o
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> new file mode 100644
> index 000000000000..2910b7047721
> --- /dev/null
> +++ b/fs/file_attr.c
> @@ -0,0 +1,318 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/fs.h>
> +#include <linux/security.h>
> +#include <linux/fscrypt.h>
> +#include <linux/fileattr.h>
> +
> +/**
> + * fileattr_fill_xflags - initialize fileattr with xflags
> + * @fa: fileattr pointer
> + * @xflags: FS_XFLAG_* flags
> + *
> + * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All
> + * other fields are zeroed.
> + */
> +void fileattr_fill_xflags(struct fileattr *fa, u32 xflags)
> +{
> + memset(fa, 0, sizeof(*fa));
> + fa->fsx_valid = true;
> + fa->fsx_xflags = xflags;
> + if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
> + fa->flags |= FS_IMMUTABLE_FL;
> + if (fa->fsx_xflags & FS_XFLAG_APPEND)
> + fa->flags |= FS_APPEND_FL;
> + if (fa->fsx_xflags & FS_XFLAG_SYNC)
> + fa->flags |= FS_SYNC_FL;
> + if (fa->fsx_xflags & FS_XFLAG_NOATIME)
> + fa->flags |= FS_NOATIME_FL;
> + if (fa->fsx_xflags & FS_XFLAG_NODUMP)
> + fa->flags |= FS_NODUMP_FL;
> + if (fa->fsx_xflags & FS_XFLAG_DAX)
> + fa->flags |= FS_DAX_FL;
> + if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
> + fa->flags |= FS_PROJINHERIT_FL;
> +}
> +EXPORT_SYMBOL(fileattr_fill_xflags);
> +
> +/**
> + * fileattr_fill_flags - initialize fileattr with flags
> + * @fa: fileattr pointer
> + * @flags: FS_*_FL flags
> + *
> + * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
> + * All other fields are zeroed.
> + */
> +void fileattr_fill_flags(struct fileattr *fa, u32 flags)
> +{
> + memset(fa, 0, sizeof(*fa));
> + fa->flags_valid = true;
> + fa->flags = flags;
> + if (fa->flags & FS_SYNC_FL)
> + fa->fsx_xflags |= FS_XFLAG_SYNC;
> + if (fa->flags & FS_IMMUTABLE_FL)
> + fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
> + if (fa->flags & FS_APPEND_FL)
> + fa->fsx_xflags |= FS_XFLAG_APPEND;
> + if (fa->flags & FS_NODUMP_FL)
> + fa->fsx_xflags |= FS_XFLAG_NODUMP;
> + if (fa->flags & FS_NOATIME_FL)
> + fa->fsx_xflags |= FS_XFLAG_NOATIME;
> + if (fa->flags & FS_DAX_FL)
> + fa->fsx_xflags |= FS_XFLAG_DAX;
> + if (fa->flags & FS_PROJINHERIT_FL)
> + fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
> +}
> +EXPORT_SYMBOL(fileattr_fill_flags);
> +
> +/**
> + * vfs_fileattr_get - retrieve miscellaneous file attributes
> + * @dentry: the object to retrieve from
> + * @fa: fileattr pointer
> + *
> + * Call i_op->fileattr_get() callback, if exists.
> + *
> + * Return: 0 on success, or a negative error on failure.
> + */
> +int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> +{
> + struct inode *inode = d_inode(dentry);
> +
> + if (!inode->i_op->fileattr_get)
> + return -ENOIOCTLCMD;
> +
> + return inode->i_op->fileattr_get(dentry, fa);
> +}
> +EXPORT_SYMBOL(vfs_fileattr_get);
> +
> +/**
> + * copy_fsxattr_to_user - copy fsxattr to userspace.
> + * @fa: fileattr pointer
> + * @ufa: fsxattr user pointer
> + *
> + * Return: 0 on success, or -EFAULT on failure.
> + */
> +int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> +{
> + struct fsxattr xfa;
> +
> + memset(&xfa, 0, sizeof(xfa));
> + xfa.fsx_xflags = fa->fsx_xflags;
> + xfa.fsx_extsize = fa->fsx_extsize;
> + xfa.fsx_nextents = fa->fsx_nextents;
> + xfa.fsx_projid = fa->fsx_projid;
> + xfa.fsx_cowextsize = fa->fsx_cowextsize;
> +
> + if (copy_to_user(ufa, &xfa, sizeof(xfa)))
> + return -EFAULT;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(copy_fsxattr_to_user);
> +
> +static int copy_fsxattr_from_user(struct fileattr *fa,
> + struct fsxattr __user *ufa)
> +{
> + struct fsxattr xfa;
> +
> + if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> + return -EFAULT;
> +
> + fileattr_fill_xflags(fa, xfa.fsx_xflags);
> + fa->fsx_extsize = xfa.fsx_extsize;
> + fa->fsx_nextents = xfa.fsx_nextents;
> + fa->fsx_projid = xfa.fsx_projid;
> + fa->fsx_cowextsize = xfa.fsx_cowextsize;
> +
> + return 0;
> +}
> +
> +/*
> + * Generic function to check FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS values and reject
> + * any invalid configurations.
> + *
> + * Note: must be called with inode lock held.
> + */
> +static int fileattr_set_prepare(struct inode *inode,
> + const struct fileattr *old_ma,
> + struct fileattr *fa)
> +{
> + int err;
> +
> + /*
> + * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> + * the relevant capability.
> + */
> + if ((fa->flags ^ old_ma->flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
> + !capable(CAP_LINUX_IMMUTABLE))
> + return -EPERM;
> +
> + err = fscrypt_prepare_setflags(inode, old_ma->flags, fa->flags);
> + if (err)
> + return err;
> +
> + /*
> + * Project Quota ID state is only allowed to change from within the init
> + * namespace. Enforce that restriction only if we are trying to change
> + * the quota ID state. Everything else is allowed in user namespaces.
> + */
> + if (current_user_ns() != &init_user_ns) {
> + if (old_ma->fsx_projid != fa->fsx_projid)
> + return -EINVAL;
> + if ((old_ma->fsx_xflags ^ fa->fsx_xflags) &
> + FS_XFLAG_PROJINHERIT)
> + return -EINVAL;
> + } else {
> + /*
> + * Caller is allowed to change the project ID. If it is being
> + * changed, make sure that the new value is valid.
> + */
> + if (old_ma->fsx_projid != fa->fsx_projid &&
> + !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid)))
> + return -EINVAL;
> + }
> +
> + /* Check extent size hints. */
> + if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode))
> + return -EINVAL;
> +
> + if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
> + !S_ISDIR(inode->i_mode))
> + return -EINVAL;
> +
> + if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
> + !S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
> + return -EINVAL;
> +
> + /*
> + * It is only valid to set the DAX flag on regular files and
> + * directories on filesystems.
> + */
> + if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
> + !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
> + return -EINVAL;
> +
> + /* Extent size hints of zero turn off the flags. */
> + if (fa->fsx_extsize == 0)
> + fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
> + if (fa->fsx_cowextsize == 0)
> + fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
> +
> + return 0;
> +}
> +
> +/**
> + * vfs_fileattr_set - change miscellaneous file attributes
> + * @idmap: idmap of the mount
> + * @dentry: the object to change
> + * @fa: fileattr pointer
> + *
> + * After verifying permissions, call i_op->fileattr_set() callback, if
> + * exists.
> + *
> + * Verifying attributes involves retrieving current attributes with
> + * i_op->fileattr_get(), this also allows initializing attributes that have
> + * not been set by the caller to current values. Inode lock is held
> + * thoughout to prevent racing with another instance.
> + *
> + * Return: 0 on success, or a negative error on failure.
> + */
> +int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> + struct fileattr *fa)
> +{
> + struct inode *inode = d_inode(dentry);
> + struct fileattr old_ma = {};
> + int err;
> +
> + if (!inode->i_op->fileattr_set)
> + return -ENOIOCTLCMD;
> +
> + if (!inode_owner_or_capable(idmap, inode))
> + return -EPERM;
> +
> + inode_lock(inode);
> + err = vfs_fileattr_get(dentry, &old_ma);
> + if (!err) {
> + /* initialize missing bits from old_ma */
> + if (fa->flags_valid) {
> + fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON;
> + fa->fsx_extsize = old_ma.fsx_extsize;
> + fa->fsx_nextents = old_ma.fsx_nextents;
> + fa->fsx_projid = old_ma.fsx_projid;
> + fa->fsx_cowextsize = old_ma.fsx_cowextsize;
> + } else {
> + fa->flags |= old_ma.flags & ~FS_COMMON_FL;
> + }
> + err = fileattr_set_prepare(inode, &old_ma, fa);
> + if (!err)
> + err = inode->i_op->fileattr_set(idmap, dentry, fa);
> + }
> + inode_unlock(inode);
> +
> + return err;
> +}
> +EXPORT_SYMBOL(vfs_fileattr_set);
> +
> +int ioctl_getflags(struct file *file, unsigned int __user *argp)
> +{
> + struct fileattr fa = { .flags_valid = true }; /* hint only */
> + int err;
> +
> + err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (!err)
> + err = put_user(fa.flags, argp);
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_getflags);
> +
> +int ioctl_setflags(struct file *file, unsigned int __user *argp)
> +{
> + struct mnt_idmap *idmap = file_mnt_idmap(file);
> + struct dentry *dentry = file->f_path.dentry;
> + struct fileattr fa;
> + unsigned int flags;
> + int err;
> +
> + err = get_user(flags, argp);
> + if (!err) {
> + err = mnt_want_write_file(file);
> + if (!err) {
> + fileattr_fill_flags(&fa, flags);
> + err = vfs_fileattr_set(idmap, dentry, &fa);
> + mnt_drop_write_file(file);
> + }
> + }
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_setflags);
> +
> +int ioctl_fsgetxattr(struct file *file, void __user *argp)
> +{
> + struct fileattr fa = { .fsx_valid = true }; /* hint only */
> + int err;
> +
> + err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (!err)
> + err = copy_fsxattr_to_user(&fa, argp);
> +
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_fsgetxattr);
> +
> +int ioctl_fssetxattr(struct file *file, void __user *argp)
> +{
> + struct mnt_idmap *idmap = file_mnt_idmap(file);
> + struct dentry *dentry = file->f_path.dentry;
> + struct fileattr fa;
> + int err;
> +
> + err = copy_fsxattr_from_user(&fa, argp);
> + if (!err) {
> + err = mnt_want_write_file(file);
> + if (!err) {
> + err = vfs_fileattr_set(idmap, dentry, &fa);
> + mnt_drop_write_file(file);
> + }
> + }
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_fssetxattr);
> diff --git a/fs/ioctl.c b/fs/ioctl.c
> index 69107a245b4c..0248cb8db2d3 100644
> --- a/fs/ioctl.c
> +++ b/fs/ioctl.c
> @@ -453,315 +453,6 @@ static int ioctl_file_dedupe_range(struct file *file,
> return ret;
> }
>
> -/**
> - * fileattr_fill_xflags - initialize fileattr with xflags
> - * @fa: fileattr pointer
> - * @xflags: FS_XFLAG_* flags
> - *
> - * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All
> - * other fields are zeroed.
> - */
> -void fileattr_fill_xflags(struct fileattr *fa, u32 xflags)
> -{
> - memset(fa, 0, sizeof(*fa));
> - fa->fsx_valid = true;
> - fa->fsx_xflags = xflags;
> - if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
> - fa->flags |= FS_IMMUTABLE_FL;
> - if (fa->fsx_xflags & FS_XFLAG_APPEND)
> - fa->flags |= FS_APPEND_FL;
> - if (fa->fsx_xflags & FS_XFLAG_SYNC)
> - fa->flags |= FS_SYNC_FL;
> - if (fa->fsx_xflags & FS_XFLAG_NOATIME)
> - fa->flags |= FS_NOATIME_FL;
> - if (fa->fsx_xflags & FS_XFLAG_NODUMP)
> - fa->flags |= FS_NODUMP_FL;
> - if (fa->fsx_xflags & FS_XFLAG_DAX)
> - fa->flags |= FS_DAX_FL;
> - if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
> - fa->flags |= FS_PROJINHERIT_FL;
> -}
> -EXPORT_SYMBOL(fileattr_fill_xflags);
> -
> -/**
> - * fileattr_fill_flags - initialize fileattr with flags
> - * @fa: fileattr pointer
> - * @flags: FS_*_FL flags
> - *
> - * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
> - * All other fields are zeroed.
> - */
> -void fileattr_fill_flags(struct fileattr *fa, u32 flags)
> -{
> - memset(fa, 0, sizeof(*fa));
> - fa->flags_valid = true;
> - fa->flags = flags;
> - if (fa->flags & FS_SYNC_FL)
> - fa->fsx_xflags |= FS_XFLAG_SYNC;
> - if (fa->flags & FS_IMMUTABLE_FL)
> - fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
> - if (fa->flags & FS_APPEND_FL)
> - fa->fsx_xflags |= FS_XFLAG_APPEND;
> - if (fa->flags & FS_NODUMP_FL)
> - fa->fsx_xflags |= FS_XFLAG_NODUMP;
> - if (fa->flags & FS_NOATIME_FL)
> - fa->fsx_xflags |= FS_XFLAG_NOATIME;
> - if (fa->flags & FS_DAX_FL)
> - fa->fsx_xflags |= FS_XFLAG_DAX;
> - if (fa->flags & FS_PROJINHERIT_FL)
> - fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
> -}
> -EXPORT_SYMBOL(fileattr_fill_flags);
> -
> -/**
> - * vfs_fileattr_get - retrieve miscellaneous file attributes
> - * @dentry: the object to retrieve from
> - * @fa: fileattr pointer
> - *
> - * Call i_op->fileattr_get() callback, if exists.
> - *
> - * Return: 0 on success, or a negative error on failure.
> - */
> -int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> -{
> - struct inode *inode = d_inode(dentry);
> -
> - if (!inode->i_op->fileattr_get)
> - return -ENOIOCTLCMD;
> -
> - return inode->i_op->fileattr_get(dentry, fa);
> -}
> -EXPORT_SYMBOL(vfs_fileattr_get);
> -
> -/**
> - * copy_fsxattr_to_user - copy fsxattr to userspace.
> - * @fa: fileattr pointer
> - * @ufa: fsxattr user pointer
> - *
> - * Return: 0 on success, or -EFAULT on failure.
> - */
> -int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> -{
> - struct fsxattr xfa;
> -
> - memset(&xfa, 0, sizeof(xfa));
> - xfa.fsx_xflags = fa->fsx_xflags;
> - xfa.fsx_extsize = fa->fsx_extsize;
> - xfa.fsx_nextents = fa->fsx_nextents;
> - xfa.fsx_projid = fa->fsx_projid;
> - xfa.fsx_cowextsize = fa->fsx_cowextsize;
> -
> - if (copy_to_user(ufa, &xfa, sizeof(xfa)))
> - return -EFAULT;
> -
> - return 0;
> -}
> -EXPORT_SYMBOL(copy_fsxattr_to_user);
> -
> -static int copy_fsxattr_from_user(struct fileattr *fa,
> - struct fsxattr __user *ufa)
> -{
> - struct fsxattr xfa;
> -
> - if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> - return -EFAULT;
> -
> - fileattr_fill_xflags(fa, xfa.fsx_xflags);
> - fa->fsx_extsize = xfa.fsx_extsize;
> - fa->fsx_nextents = xfa.fsx_nextents;
> - fa->fsx_projid = xfa.fsx_projid;
> - fa->fsx_cowextsize = xfa.fsx_cowextsize;
> -
> - return 0;
> -}
> -
> -/*
> - * Generic function to check FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS values and reject
> - * any invalid configurations.
> - *
> - * Note: must be called with inode lock held.
> - */
> -static int fileattr_set_prepare(struct inode *inode,
> - const struct fileattr *old_ma,
> - struct fileattr *fa)
> -{
> - int err;
> -
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - if ((fa->flags ^ old_ma->flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
> - !capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> -
> - err = fscrypt_prepare_setflags(inode, old_ma->flags, fa->flags);
> - if (err)
> - return err;
> -
> - /*
> - * Project Quota ID state is only allowed to change from within the init
> - * namespace. Enforce that restriction only if we are trying to change
> - * the quota ID state. Everything else is allowed in user namespaces.
> - */
> - if (current_user_ns() != &init_user_ns) {
> - if (old_ma->fsx_projid != fa->fsx_projid)
> - return -EINVAL;
> - if ((old_ma->fsx_xflags ^ fa->fsx_xflags) &
> - FS_XFLAG_PROJINHERIT)
> - return -EINVAL;
> - } else {
> - /*
> - * Caller is allowed to change the project ID. If it is being
> - * changed, make sure that the new value is valid.
> - */
> - if (old_ma->fsx_projid != fa->fsx_projid &&
> - !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid)))
> - return -EINVAL;
> - }
> -
> - /* Check extent size hints. */
> - if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode))
> - return -EINVAL;
> -
> - if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
> - !S_ISDIR(inode->i_mode))
> - return -EINVAL;
> -
> - if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
> - !S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
> - return -EINVAL;
> -
> - /*
> - * It is only valid to set the DAX flag on regular files and
> - * directories on filesystems.
> - */
> - if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
> - !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
> - return -EINVAL;
> -
> - /* Extent size hints of zero turn off the flags. */
> - if (fa->fsx_extsize == 0)
> - fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
> - if (fa->fsx_cowextsize == 0)
> - fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
> -
> - return 0;
> -}
> -
> -/**
> - * vfs_fileattr_set - change miscellaneous file attributes
> - * @idmap: idmap of the mount
> - * @dentry: the object to change
> - * @fa: fileattr pointer
> - *
> - * After verifying permissions, call i_op->fileattr_set() callback, if
> - * exists.
> - *
> - * Verifying attributes involves retrieving current attributes with
> - * i_op->fileattr_get(), this also allows initializing attributes that have
> - * not been set by the caller to current values. Inode lock is held
> - * thoughout to prevent racing with another instance.
> - *
> - * Return: 0 on success, or a negative error on failure.
> - */
> -int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> - struct fileattr *fa)
> -{
> - struct inode *inode = d_inode(dentry);
> - struct fileattr old_ma = {};
> - int err;
> -
> - if (!inode->i_op->fileattr_set)
> - return -ENOIOCTLCMD;
> -
> - if (!inode_owner_or_capable(idmap, inode))
> - return -EPERM;
> -
> - inode_lock(inode);
> - err = vfs_fileattr_get(dentry, &old_ma);
> - if (!err) {
> - /* initialize missing bits from old_ma */
> - if (fa->flags_valid) {
> - fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON;
> - fa->fsx_extsize = old_ma.fsx_extsize;
> - fa->fsx_nextents = old_ma.fsx_nextents;
> - fa->fsx_projid = old_ma.fsx_projid;
> - fa->fsx_cowextsize = old_ma.fsx_cowextsize;
> - } else {
> - fa->flags |= old_ma.flags & ~FS_COMMON_FL;
> - }
> - err = fileattr_set_prepare(inode, &old_ma, fa);
> - if (!err)
> - err = inode->i_op->fileattr_set(idmap, dentry, fa);
> - }
> - inode_unlock(inode);
> -
> - return err;
> -}
> -EXPORT_SYMBOL(vfs_fileattr_set);
> -
> -static int ioctl_getflags(struct file *file, unsigned int __user *argp)
> -{
> - struct fileattr fa = { .flags_valid = true }; /* hint only */
> - int err;
> -
> - err = vfs_fileattr_get(file->f_path.dentry, &fa);
> - if (!err)
> - err = put_user(fa.flags, argp);
> - return err;
> -}
> -
> -static int ioctl_setflags(struct file *file, unsigned int __user *argp)
> -{
> - struct mnt_idmap *idmap = file_mnt_idmap(file);
> - struct dentry *dentry = file->f_path.dentry;
> - struct fileattr fa;
> - unsigned int flags;
> - int err;
> -
> - err = get_user(flags, argp);
> - if (!err) {
> - err = mnt_want_write_file(file);
> - if (!err) {
> - fileattr_fill_flags(&fa, flags);
> - err = vfs_fileattr_set(idmap, dentry, &fa);
> - mnt_drop_write_file(file);
> - }
> - }
> - return err;
> -}
> -
> -static int ioctl_fsgetxattr(struct file *file, void __user *argp)
> -{
> - struct fileattr fa = { .fsx_valid = true }; /* hint only */
> - int err;
> -
> - err = vfs_fileattr_get(file->f_path.dentry, &fa);
> - if (!err)
> - err = copy_fsxattr_to_user(&fa, argp);
> -
> - return err;
> -}
> -
> -static int ioctl_fssetxattr(struct file *file, void __user *argp)
> -{
> - struct mnt_idmap *idmap = file_mnt_idmap(file);
> - struct dentry *dentry = file->f_path.dentry;
> - struct fileattr fa;
> - int err;
> -
> - err = copy_fsxattr_from_user(&fa, argp);
> - if (!err) {
> - err = mnt_want_write_file(file);
> - if (!err) {
> - err = vfs_fileattr_set(idmap, dentry, &fa);
> - mnt_drop_write_file(file);
> - }
> - }
> - return err;
> -}
> -
> static int ioctl_getfsuuid(struct file *file, void __user *argp)
> {
> struct super_block *sb = file_inode(file)->i_sb;
> diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h
> index 47c05a9851d0..6030d0bf7ad3 100644
> --- a/include/linux/fileattr.h
> +++ b/include/linux/fileattr.h
> @@ -55,5 +55,9 @@ static inline bool fileattr_has_fsx(const struct fileattr *fa)
> int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
> int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> struct fileattr *fa);
> +int ioctl_getflags(struct file *file, unsigned int __user *argp);
> +int ioctl_setflags(struct file *file, unsigned int __user *argp);
> +int ioctl_fsgetxattr(struct file *file, void __user *argp);
> +int ioctl_fssetxattr(struct file *file, void __user *argp);
>
> #endif /* _LINUX_FILEATTR_H */
>
> --
> 2.47.2
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP
2025-06-30 16:20 ` [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP Andrey Albershteyn
2025-06-30 18:05 ` Pali Rohár
@ 2025-07-01 6:05 ` Amir Goldstein
2025-07-01 12:51 ` Jan Kara
2025-07-01 12:52 ` Jan Kara
2025-07-01 18:18 ` Darrick J. Wong
3 siblings, 1 reply; 47+ messages in thread
From: Amir Goldstein @ 2025-07-01 6:05 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Arnd Bergmann, Casey Schaufler, Christian Brauner, Jan Kara,
Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon, Jun 30, 2025 at 6:20 PM Andrey Albershteyn <aalbersh@redhat.com> wrote:
>
> Future patches will add new syscalls which use these functions. As
> this interface won't be used for ioctls only, the EOPNOSUPP is more
> appropriate return code.
>
> This patch converts return code from ENOIOCTLCMD to EOPNOSUPP for
> vfs_fileattr_get and vfs_fileattr_set. To save old behavior translate
> EOPNOSUPP back for current users - overlayfs, encryptfs and fs/ioctl.c.
>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> ---
> fs/ecryptfs/inode.c | 8 +++++++-
> fs/file_attr.c | 12 ++++++++++--
> fs/overlayfs/inode.c | 2 +-
> 3 files changed, 18 insertions(+), 4 deletions(-)
>
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 493d7f194956..a55c1375127f 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -1126,7 +1126,13 @@ static int ecryptfs_removexattr(struct dentry *dentry, struct inode *inode,
>
> static int ecryptfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> {
> - return vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
> + int rc;
> +
> + rc = vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
> + if (rc == -EOPNOTSUPP)
> + rc = -ENOIOCTLCMD;
> +
> + return rc;
> }
>
I think the semantics should be
"This patch converts return code of vfs_fileattr_[gs]et and ->fileattr_[gs]et()
from ENOIOCTLCMD to EOPNOSUPP"
ENOIOCTLCMD belongs only in the ioctl frontend, so above conversion
is not needed.
> static int ecryptfs_fileattr_set(struct mnt_idmap *idmap,
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> index be62d97cc444..4e85fa00c092 100644
> --- a/fs/file_attr.c
> +++ b/fs/file_attr.c
> @@ -79,7 +79,7 @@ int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> int error;
>
> if (!inode->i_op->fileattr_get)
> - return -ENOIOCTLCMD;
> + return -EOPNOTSUPP;
>
> error = security_inode_file_getattr(dentry, fa);
> if (error)
> @@ -229,7 +229,7 @@ int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> int err;
>
> if (!inode->i_op->fileattr_set)
> - return -ENOIOCTLCMD;
> + return -EOPNOTSUPP;
>
> if (!inode_owner_or_capable(idmap, inode))
> return -EPERM;
> @@ -271,6 +271,8 @@ int ioctl_getflags(struct file *file, unsigned int __user *argp)
> int err;
>
> err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> if (!err)
> err = put_user(fa.flags, argp);
> return err;
> @@ -292,6 +294,8 @@ int ioctl_setflags(struct file *file, unsigned int __user *argp)
> fileattr_fill_flags(&fa, flags);
> err = vfs_fileattr_set(idmap, dentry, &fa);
> mnt_drop_write_file(file);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> }
> }
> return err;
> @@ -304,6 +308,8 @@ int ioctl_fsgetxattr(struct file *file, void __user *argp)
> int err;
>
> err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> if (!err)
> err = copy_fsxattr_to_user(&fa, argp);
>
> @@ -324,6 +330,8 @@ int ioctl_fssetxattr(struct file *file, void __user *argp)
> if (!err) {
> err = vfs_fileattr_set(idmap, dentry, &fa);
> mnt_drop_write_file(file);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> }
> }
> return err;
> diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
> index 6f0e15f86c21..096d44712bb1 100644
> --- a/fs/overlayfs/inode.c
> +++ b/fs/overlayfs/inode.c
> @@ -721,7 +721,7 @@ int ovl_real_fileattr_get(const struct path *realpath, struct fileattr *fa)
> return err;
>
> err = vfs_fileattr_get(realpath->dentry, fa);
> - if (err == -ENOIOCTLCMD)
> + if (err == -EOPNOTSUPP)
> err = -ENOTTY;
> return err;
> }
That's the wrong way, because it hides the desired -EOPNOTSUPP
return code from ovl_fileattr_get().
The conversion to -ENOTTY was done for
5b0a414d06c3 ("ovl: fix filattr copy-up failure"),
so please do this instead:
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -722,7 +722,7 @@ int ovl_real_fileattr_get(const struct path
*realpath, struct fileattr *fa)
err = vfs_fileattr_get(realpath->dentry, fa);
if (err == -ENOIOCTLCMD)
- err = -ENOTTY;
+ err = -EOPNOTSUPP;
return err;
}
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -178,7 +178,7 @@ static int ovl_copy_fileattr(struct inode *inode,
const struct path *old,
err = ovl_real_fileattr_get(old, &oldfa);
if (err) {
/* Ntfs-3g returns -EINVAL for "no fileattr support" */
- if (err == -ENOTTY || err == -EINVAL)
+ if (err == -ENOTTY || err == -EINVAL || err == -EOPNOTSUPP)
return 0;
pr_warn("failed to retrieve lower fileattr (%pd2, err=%i)\n",
old->dentry, err);
Thanks,
Amir.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls
2025-06-30 16:20 [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
` (5 preceding siblings ...)
2025-06-30 16:20 ` [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
@ 2025-07-01 6:11 ` Amir Goldstein
2025-07-01 12:29 ` Christian Brauner
2025-07-07 12:19 ` Christian Brauner
8 siblings, 0 replies; 47+ messages in thread
From: Amir Goldstein @ 2025-07-01 6:11 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Arnd Bergmann, Casey Schaufler, Christian Brauner, Jan Kara,
Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon, Jun 30, 2025 at 6:20 PM Andrey Albershteyn <aalbersh@redhat.com> wrote:
>
> This patchset introduced two new syscalls file_getattr() and
> file_setattr(). These syscalls are similar to FS_IOC_FSSETXATTR ioctl()
> except they use *at() semantics. Therefore, there's no need to open the
> file to get a fd.
>
> These syscalls allow userspace to set filesystem inode attributes on
> special files. One of the usage examples is XFS quota projects.
>
> XFS has project quotas which could be attached to a directory. All
> new inodes in these directories inherit project ID set on parent
> directory.
>
> The project is created from userspace by opening and calling
> FS_IOC_FSSETXATTR on each inode. This is not possible for special
> files such as FIFO, SOCK, BLK etc. Therefore, some inodes are left
> with empty project ID. Those inodes then are not shown in the quota
> accounting but still exist in the directory. This is not critical but in
> the case when special files are created in the directory with already
> existing project quota, these new inodes inherit extended attributes.
> This creates a mix of special files with and without attributes.
> Moreover, special files with attributes don't have a possibility to
> become clear or change the attributes. This, in turn, prevents userspace
> from re-creating quota project on these existing files.
>
> An xfstests test generic/766 with basic coverage is at:
> https://github.com/alberand/xfstests/commits/b4/file-attr/
>
> NAME
>
> file_getattr/file_setattr - get/set filesystem inode attributes
>
> SYNOPSIS
>
> #include <sys/syscall.h> /* Definition of SYS_* constants */
> #include <unistd.h>
>
> long syscall(SYS_file_getattr, int dirfd, const char *pathname,
> struct fsx_fileattr *fsx, size_t size,
> unsigned int at_flags);
> long syscall(SYS_file_setattr, int dirfd, const char *pathname,
> struct fsx_fileattr *fsx, size_t size,
> unsigned int at_flags);
>
> Note: glibc doesn't provide for file_getattr()/file_setattr(),
> use syscall(2) instead.
>
> DESCRIPTION
>
> The file_getattr()/file_setattr() are used to set extended file
> attributes. These syscalls take dirfd in conjunction with the
> pathname argument. The syscall then operates on inode opened
> according to openat(2) semantics.
>
> This is an alternative to FS_IOC_FSGETXATTR/FS_IOC_FSSETXATTR
> ioctl with a difference that file don't need to be open as file
> can be referenced with a path instead of fd. By having this one
> can manipulated filesystem inode attributes not only on regular
> files but also on special ones. This is not possible with
> FS_IOC_FSSETXATTR ioctl as ioctl() can not be called on special
> files directly for the filesystem inode.
>
> at_flags can be set to AT_SYMLINK_NOFOLLOW or AT_EMPTY_PATH.
>
> RETURN VALUE
>
> On success, 0 is returned. On error, -1 is returned, and errno
> is set to indicate the error.
>
> ERRORS
>
> EINVAL Invalid at_flag specified (only
> AT_SYMLINK_NOFOLLOW and AT_EMPTY_PATH is
> supported).
>
> EINVAL Size was smaller than any known version of
> struct fsx_fileattr.
>
> EINVAL Invalid combination of parameters provided in
> fsx_fileattr for this type of file.
>
> E2BIG Size of input argument struct fsx_fileattr
> is too big.
>
> EBADF Invalid file descriptor was provided.
>
> EPERM No permission to change this file.
>
> EOPNOTSUPP Filesystem does not support setting attributes
> on this type of inode
>
> HISTORY
>
> Added in Linux 6.16.
>
> EXAMPLE
>
> Create directory and file "mkdir ./dir && touch ./dir/foo" and then
> execute the following program:
>
> #include <fcntl.h>
> #include <errno.h>
> #include <string.h>
> #include <linux/fs.h>
> #include <stdio.h>
> #include <sys/syscall.h>
> #include <unistd.h>
>
> #if !defined(SYS_file_getattr) && defined(__x86_64__)
> #define SYS_file_getattr 468
> #define SYS_file_setattr 469
>
> struct fsx_fileattr {
> __u32 fsx_xflags;
> __u32 fsx_extsize;
> __u32 fsx_nextents;
> __u32 fsx_projid;
> __u32 fsx_cowextsize;
> };
> #endif
>
> int
> main(int argc, char **argv) {
> int dfd;
> int error;
> struct fsx_fileattr fsx;
>
> dfd = open("./dir", O_RDONLY);
> if (dfd == -1) {
> printf("can not open ./dir");
> return dfd;
> }
>
> error = syscall(SYS_file_getattr, dfd, "./foo", &fsx,
> sizeof(struct fsx_fileattr), 0);
> if (error) {
> printf("can not call SYS_file_getattr: %s",
> strerror(errno));
> return error;
> }
>
> printf("./dir/foo flags: %d\n", fsx.fsx_xflags);
>
> fsx.fsx_xflags |= FS_XFLAG_NODUMP;
> error = syscall(SYS_file_setattr, dfd, "./foo", &fsx,
> sizeof(struct fsx_fileattr), 0);
> if (error) {
> printf("can not call SYS_file_setattr: %s",
> strerror(errno));
> return error;
> }
>
> printf("./dir/foo flags: %d\n", fsx.fsx_xflags);
>
> return error;
> }
>
> SEE ALSO
>
> ioctl(2), ioctl_iflags(2), ioctl_xfs_fsgetxattr(2), openat(2)
>
> ---
> Changes in v6:
> - Update cover letter example and docs
> - Applied __free() attribute for syscall stack objects
> - Introduced struct fsx_fileattr
> - Replace 'struct fsxattr' with 'struct fsx_fileattr'
> - Add helper to fill in fsx_fileattr from fileattr
> - Dropped copy_fsx_to_user() header declaration
> - Link to v5: https://lore.kernel.org/r/20250513-xattrat-syscall-v5-0-22bb9c6c767f@kernel.org
>
Series looks good.
For mine and Pali's minor comments on patch 4 no need to resend.
I think they could be fixed on commit.
Thanks,
Amir.
> Changes in v5:
> - Remove setting of LOOKUP_EMPTY flags which does not have any effect
> - Return -ENOSUPP from vfs_fileattr_set()
> - Add fsxattr masking (by Amir)
> - Fix UAF issue dentry
> - Fix getname_maybe_null() issue with NULL path
> - Implement file_getattr/file_setattr hooks
> - Return LSM return code from file_setattr
> - Rename from getfsxattrat/setfsxattrat to file_getattr/file_setattr
> - Link to v4: https://lore.kernel.org/r/20250321-xattrat-syscall-v4-0-3e82e6fb3264@kernel.org
>
> Changes in v4:
> - Use getname_maybe_null() for correct handling of dfd + path semantic
> - Remove restriction for special files on which flags are allowed
> - Utilize copy_struct_from_user() for better future compatibility
> - Add draft man page to cover letter
> - Convert -ENOIOCTLCMD to -EOPNOSUPP as more appropriate for syscall
> - Add missing __user to header declaration of syscalls
> - Link to v3: https://lore.kernel.org/r/20250211-xattrat-syscall-v3-1-a07d15f898b2@kernel.org
>
> Changes in v3:
> - Remove unnecessary "dfd is dir" check as it checked in user_path_at()
> - Remove unnecessary "same filesystem" check
> - Use CLASS() instead of directly calling fdget/fdput
> - Link to v2: https://lore.kernel.org/r/20250122-xattrat-syscall-v2-1-5b360d4fbcb2@kernel.org
>
> v1:
> https://lore.kernel.org/linuxppc-dev/20250109174540.893098-1-aalbersh@kernel.org/
>
> Previous discussion:
> https://lore.kernel.org/linux-xfs/20240520164624.665269-2-aalbersh@redhat.com/
>
> ---
> Amir Goldstein (1):
> fs: prepare for extending file_get/setattr()
>
> Andrey Albershteyn (5):
> fs: split fileattr related helpers into separate file
> lsm: introduce new hooks for setting/getting inode fsxattr
> selinux: implement inode_file_[g|s]etattr hooks
> fs: make vfs_fileattr_[get|set] return -EOPNOSUPP
> fs: introduce file_getattr and file_setattr syscalls
>
> arch/alpha/kernel/syscalls/syscall.tbl | 2 +
> arch/arm/tools/syscall.tbl | 2 +
> arch/arm64/tools/syscall_32.tbl | 2 +
> arch/m68k/kernel/syscalls/syscall.tbl | 2 +
> arch/microblaze/kernel/syscalls/syscall.tbl | 2 +
> arch/mips/kernel/syscalls/syscall_n32.tbl | 2 +
> arch/mips/kernel/syscalls/syscall_n64.tbl | 2 +
> arch/mips/kernel/syscalls/syscall_o32.tbl | 2 +
> arch/parisc/kernel/syscalls/syscall.tbl | 2 +
> arch/powerpc/kernel/syscalls/syscall.tbl | 2 +
> arch/s390/kernel/syscalls/syscall.tbl | 2 +
> arch/sh/kernel/syscalls/syscall.tbl | 2 +
> arch/sparc/kernel/syscalls/syscall.tbl | 2 +
> arch/x86/entry/syscalls/syscall_32.tbl | 2 +
> arch/x86/entry/syscalls/syscall_64.tbl | 2 +
> arch/xtensa/kernel/syscalls/syscall.tbl | 2 +
> fs/Makefile | 3 +-
> fs/ecryptfs/inode.c | 8 +-
> fs/file_attr.c | 493 ++++++++++++++++++++++++++++
> fs/ioctl.c | 309 -----------------
> fs/overlayfs/inode.c | 2 +-
> include/linux/fileattr.h | 24 ++
> include/linux/lsm_hook_defs.h | 2 +
> include/linux/security.h | 16 +
> include/linux/syscalls.h | 6 +
> include/uapi/asm-generic/unistd.h | 8 +-
> include/uapi/linux/fs.h | 18 +
> scripts/syscall.tbl | 2 +
> security/security.c | 30 ++
> security/selinux/hooks.c | 14 +
> 30 files changed, 654 insertions(+), 313 deletions(-)
> ---
> base-commit: d0b3b7b22dfa1f4b515fd3a295b3fd958f9e81af
> change-id: 20250114-xattrat-syscall-6a1136d2db59
>
> Best regards,
> --
> Andrey Albershteyn <aalbersh@kernel.org>
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls
2025-06-30 16:20 [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
` (6 preceding siblings ...)
2025-07-01 6:11 ` [PATCH v6 0/6] " Amir Goldstein
@ 2025-07-01 12:29 ` Christian Brauner
2025-07-07 12:05 ` Andrey Albershteyn
2025-07-07 12:19 ` Christian Brauner
8 siblings, 1 reply; 47+ messages in thread
From: Christian Brauner @ 2025-07-01 12:29 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Jan Kara,
Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon, Jun 30, 2025 at 06:20:10PM +0200, Andrey Albershteyn wrote:
> This patchset introduced two new syscalls file_getattr() and
> file_setattr(). These syscalls are similar to FS_IOC_FSSETXATTR ioctl()
> except they use *at() semantics. Therefore, there's no need to open the
> file to get a fd.
>
> These syscalls allow userspace to set filesystem inode attributes on
> special files. One of the usage examples is XFS quota projects.
>
> XFS has project quotas which could be attached to a directory. All
> new inodes in these directories inherit project ID set on parent
> directory.
>
> The project is created from userspace by opening and calling
> FS_IOC_FSSETXATTR on each inode. This is not possible for special
> files such as FIFO, SOCK, BLK etc. Therefore, some inodes are left
> with empty project ID. Those inodes then are not shown in the quota
> accounting but still exist in the directory. This is not critical but in
> the case when special files are created in the directory with already
> existing project quota, these new inodes inherit extended attributes.
> This creates a mix of special files with and without attributes.
> Moreover, special files with attributes don't have a possibility to
> become clear or change the attributes. This, in turn, prevents userspace
> from re-creating quota project on these existing files.
Only small nits I'm going to comment on that I can fix myself.
Otherwise looks great.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-06-30 16:20 ` [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
@ 2025-07-01 12:34 ` Christian Brauner
2025-07-02 9:13 ` Amir Goldstein
2025-07-01 13:24 ` Jan Kara
2025-07-01 18:43 ` Darrick J. Wong
2 siblings, 1 reply; 47+ messages in thread
From: Christian Brauner @ 2025-07-01 12:34 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Jan Kara,
Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon, Jun 30, 2025 at 06:20:16PM +0200, Andrey Albershteyn wrote:
> From: Andrey Albershteyn <aalbersh@redhat.com>
>
> 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 fsx_fileattr 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>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> ---
> arch/alpha/kernel/syscalls/syscall.tbl | 2 +
> arch/arm/tools/syscall.tbl | 2 +
> arch/arm64/tools/syscall_32.tbl | 2 +
> arch/m68k/kernel/syscalls/syscall.tbl | 2 +
> arch/microblaze/kernel/syscalls/syscall.tbl | 2 +
> arch/mips/kernel/syscalls/syscall_n32.tbl | 2 +
> arch/mips/kernel/syscalls/syscall_n64.tbl | 2 +
> arch/mips/kernel/syscalls/syscall_o32.tbl | 2 +
> arch/parisc/kernel/syscalls/syscall.tbl | 2 +
> arch/powerpc/kernel/syscalls/syscall.tbl | 2 +
> arch/s390/kernel/syscalls/syscall.tbl | 2 +
> arch/sh/kernel/syscalls/syscall.tbl | 2 +
> arch/sparc/kernel/syscalls/syscall.tbl | 2 +
> arch/x86/entry/syscalls/syscall_32.tbl | 2 +
> arch/x86/entry/syscalls/syscall_64.tbl | 2 +
> arch/xtensa/kernel/syscalls/syscall.tbl | 2 +
> fs/file_attr.c | 148 ++++++++++++++++++++++++++++
> include/linux/syscalls.h | 6 ++
> include/uapi/asm-generic/unistd.h | 8 +-
> include/uapi/linux/fs.h | 18 ++++
> scripts/syscall.tbl | 2 +
> 21 files changed, 213 insertions(+), 1 deletion(-)
>
> diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
> index 2dd6340de6b4..16dca28ebf17 100644
> --- a/arch/alpha/kernel/syscalls/syscall.tbl
> +++ b/arch/alpha/kernel/syscalls/syscall.tbl
> @@ -507,3 +507,5 @@
> 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
> diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
> index 27c1d5ebcd91..b07e699aaa3c 100644
> --- a/arch/arm/tools/syscall.tbl
> +++ b/arch/arm/tools/syscall.tbl
> @@ -482,3 +482,5 @@
> 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
> diff --git a/arch/arm64/tools/syscall_32.tbl b/arch/arm64/tools/syscall_32.tbl
> index 0765b3a8d6d6..8d9088bc577d 100644
> --- a/arch/arm64/tools/syscall_32.tbl
> +++ b/arch/arm64/tools/syscall_32.tbl
> @@ -479,3 +479,5 @@
> 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
> diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
> index 9fe47112c586..f41d38dfbf13 100644
> --- a/arch/m68k/kernel/syscalls/syscall.tbl
> +++ b/arch/m68k/kernel/syscalls/syscall.tbl
> @@ -467,3 +467,5 @@
> 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
> diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
> index 7b6e97828e55..580af574fe73 100644
> --- a/arch/microblaze/kernel/syscalls/syscall.tbl
> +++ b/arch/microblaze/kernel/syscalls/syscall.tbl
> @@ -473,3 +473,5 @@
> 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
> diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
> index aa70e371bb54..d824ffe9a014 100644
> --- a/arch/mips/kernel/syscalls/syscall_n32.tbl
> +++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
> @@ -406,3 +406,5 @@
> 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
> diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
> index 1e8c44c7b614..7a7049c2c307 100644
> --- a/arch/mips/kernel/syscalls/syscall_n64.tbl
> +++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
> @@ -382,3 +382,5 @@
> 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
> diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
> index 114a5a1a6230..d330274f0601 100644
> --- a/arch/mips/kernel/syscalls/syscall_o32.tbl
> +++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
> @@ -455,3 +455,5 @@
> 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
> diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
> index 94df3cb957e9..88a788a7b18d 100644
> --- a/arch/parisc/kernel/syscalls/syscall.tbl
> +++ b/arch/parisc/kernel/syscalls/syscall.tbl
> @@ -466,3 +466,5 @@
> 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
> diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
> index 9a084bdb8926..b453e80dfc00 100644
> --- a/arch/powerpc/kernel/syscalls/syscall.tbl
> +++ b/arch/powerpc/kernel/syscalls/syscall.tbl
> @@ -558,3 +558,5 @@
> 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
> diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
> index a4569b96ef06..8a6744d658db 100644
> --- a/arch/s390/kernel/syscalls/syscall.tbl
> +++ b/arch/s390/kernel/syscalls/syscall.tbl
> @@ -470,3 +470,5 @@
> 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
> diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
> index 52a7652fcff6..5e9c9eff5539 100644
> --- a/arch/sh/kernel/syscalls/syscall.tbl
> +++ b/arch/sh/kernel/syscalls/syscall.tbl
> @@ -471,3 +471,5 @@
> 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
> diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
> index 83e45eb6c095..ebb7d06d1044 100644
> --- a/arch/sparc/kernel/syscalls/syscall.tbl
> +++ b/arch/sparc/kernel/syscalls/syscall.tbl
> @@ -513,3 +513,5 @@
> 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
> diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
> index ac007ea00979..4877e16da69a 100644
> --- a/arch/x86/entry/syscalls/syscall_32.tbl
> +++ b/arch/x86/entry/syscalls/syscall_32.tbl
> @@ -473,3 +473,5 @@
> 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
> diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
> index cfb5ca41e30d..92cf0fe2291e 100644
> --- a/arch/x86/entry/syscalls/syscall_64.tbl
> +++ b/arch/x86/entry/syscalls/syscall_64.tbl
> @@ -391,6 +391,8 @@
> 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
> diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
> index f657a77314f8..374e4cb788d8 100644
> --- a/arch/xtensa/kernel/syscalls/syscall.tbl
> +++ b/arch/xtensa/kernel/syscalls/syscall.tbl
> @@ -438,3 +438,5 @@
> 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
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> index 62f08872d4ad..fda9d847eee5 100644
> --- a/fs/file_attr.c
> +++ b/fs/file_attr.c
> @@ -3,6 +3,10 @@
> #include <linux/security.h>
> #include <linux/fscrypt.h>
> #include <linux/fileattr.h>
> +#include <linux/syscalls.h>
> +#include <linux/namei.h>
> +
> +#include "internal.h"
>
> /**
> * fileattr_fill_xflags - initialize fileattr with xflags
> @@ -89,6 +93,19 @@ int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> }
> EXPORT_SYMBOL(vfs_fileattr_get);
>
> +static void fileattr_to_fsx_fileattr(const struct fileattr *fa,
> + struct fsx_fileattr *fsx)
> +{
> + __u32 mask = FS_XFLAGS_MASK;
> +
> + memset(fsx, 0, sizeof(struct fsx_fileattr));
Fwiw, what also works is:
*fsx = (struct fsx_fileattr){
.fsx_xflags = fa->fsx_xflags & mask,
.fsx_extsize = fa->fsx_extsize,
.fsx_nextents = fa->fsx_nextents,
.fsx_projid = fa->fsx_projid,
.fsx_cowextsize = fa->fsx_cowextsize,
}
avoiding the memset(). Anyway, all minor nits.
> + fsx->fsx_xflags = fa->fsx_xflags & mask;
> + fsx->fsx_extsize = fa->fsx_extsize;
> + fsx->fsx_nextents = fa->fsx_nextents;
> + fsx->fsx_projid = fa->fsx_projid;
> + fsx->fsx_cowextsize = fa->fsx_cowextsize;
> +}
> +
> /**
> * copy_fsxattr_to_user - copy fsxattr to userspace.
> * @fa: fileattr pointer
> @@ -115,6 +132,23 @@ int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> }
> EXPORT_SYMBOL(copy_fsxattr_to_user);
>
> +static int fsx_fileattr_to_fileattr(const struct fsx_fileattr *fsx,
> + struct fileattr *fa)
> +{
> + __u32 mask = FS_XFLAGS_MASK;
> +
> + if (fsx->fsx_xflags & ~mask)
> + return -EINVAL;
> +
> + fileattr_fill_xflags(fa, fsx->fsx_xflags);
> + fa->fsx_xflags &= ~FS_XFLAG_RDONLY_MASK;
> + fa->fsx_extsize = fsx->fsx_extsize;
> + fa->fsx_projid = fsx->fsx_projid;
> + fa->fsx_cowextsize = fsx->fsx_cowextsize;
> +
> + return 0;
> +}
> +
> static int copy_fsxattr_from_user(struct fileattr *fa,
> struct fsxattr __user *ufa)
> {
> @@ -343,3 +377,117 @@ 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 fsx_fileattr __user *, ufsx, size_t, usize,
> + unsigned int, at_flags)
> +{
> + struct fileattr fa;
> + struct path filepath __free(path_put) = {};
> + int error;
> + unsigned int lookup_flags = 0;
> + struct filename *name __free(putname) = NULL;
Fwiw, cleanup guards should always be grouped together at the top like:
struct path filepath __free(path_put) = {};
struct filename *name __free(putname) = NULL;
struct fileattr fa;
int error;
unsigned int lookup_flags = 0;
This makes it easy to spot them when reading a function with multiple
variables on top.
> + struct fsx_fileattr fsx;
> + struct fsx_fileattr fsx;
> +
> + BUILD_BUG_ON(sizeof(struct fsx_fileattr) < FSX_FILEATTR_SIZE_VER0);
> + BUILD_BUG_ON(sizeof(struct fsx_fileattr) != FSX_FILEATTR_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 < FSX_FILEATTR_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);
> +
> + 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_fsx_fileattr(&fa, &fsx);
> + error = copy_struct_to_user(ufsx, usize, &fsx,
> + sizeof(struct fsx_fileattr), NULL);
> +
> + return error;
> +}
> +
> +SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename,
> + struct fsx_fileattr __user *, ufsx, size_t, usize,
> + unsigned int, at_flags)
> +{
> + struct fileattr fa;
> + struct path filepath __free(path_put) = {};
> + int error;
> + unsigned int lookup_flags = 0;
> + struct filename *name __free(putname) = NULL;
> + struct fsx_fileattr fsx;
> +
> + BUILD_BUG_ON(sizeof(struct fsx_fileattr) < FSX_FILEATTR_SIZE_VER0);
> + BUILD_BUG_ON(sizeof(struct fsx_fileattr) != FSX_FILEATTR_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 < FSX_FILEATTR_SIZE_VER0)
> + return -EINVAL;
> +
> + error = copy_struct_from_user(&fsx, sizeof(struct fsx_fileattr), ufsx,
> + usize);
> + if (error)
> + return error;
> +
> + error = fsx_fileattr_to_fileattr(&fsx, &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);
> +
> + 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);
> + }
Note-to-self: I really want scoped_guard()s for mnt_want_write() going forward...
> +
> + return error;
> +}
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index e5603cc91963..179acbe28fec 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -371,6 +371,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 fsx_fileattr __user *ufsx, size_t usize,
> + unsigned int at_flags);
> +asmlinkage long sys_file_setattr(int dfd, const char __user *filename,
> + struct fsx_fileattr __user *ufsx, 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);
> diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
> index 2892a45023af..04e0077fb4c9 100644
> --- a/include/uapi/asm-generic/unistd.h
> +++ b/include/uapi/asm-generic/unistd.h
> @@ -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
> diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
> index 0098b0ce8ccb..0784f2033ba4 100644
> --- a/include/uapi/linux/fs.h
> +++ b/include/uapi/linux/fs.h
> @@ -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 fsx_fileattr {
> + __u32 fsx_xflags; /* xflags field value (get/set) */
> + __u32 fsx_extsize; /* extsize field value (get/set)*/
> + __u32 fsx_nextents; /* nextents field value (get) */
> + __u32 fsx_projid; /* project identifier (get/set) */
> + __u32 fsx_cowextsize; /* CoW extsize field value (get/set) */
This misses a:
__u32 __spare;
so there's no holes in the struct. :)
> +};
> +
> +#define FSX_FILEATTR_SIZE_VER0 20
> +#define FSX_FILEATTR_SIZE_LATEST FSX_FILEATTR_SIZE_VER0
> +
> /*
> * Flags for the fsx_xflags field
> */
> diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
> index 580b4e246aec..d1ae5e92c615 100644
> --- a/scripts/syscall.tbl
> +++ b/scripts/syscall.tbl
> @@ -408,3 +408,5 @@
> 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
>
> --
> 2.47.2
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 1/6] fs: split fileattr related helpers into separate file
2025-06-30 16:20 ` [PATCH v6 1/6] fs: split fileattr related helpers into separate file Andrey Albershteyn
2025-07-01 5:39 ` Amir Goldstein
@ 2025-07-01 12:38 ` Jan Kara
2025-07-01 18:13 ` Darrick J. Wong
2 siblings, 0 replies; 47+ messages in thread
From: Jan Kara @ 2025-07-01 12:38 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon 30-06-25 18:20:11, Andrey Albershteyn wrote:
> From: Andrey Albershteyn <aalbersh@kernel.org>
>
> This patch moves function related to file extended attributes
> manipulations to separate file. Refactoring only.
>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/Makefile | 3 +-
> fs/file_attr.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++
> fs/ioctl.c | 309 ---------------------------------------------
> include/linux/fileattr.h | 4 +
> 4 files changed, 324 insertions(+), 310 deletions(-)
>
> diff --git a/fs/Makefile b/fs/Makefile
> index 79c08b914c47..334654f9584b 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -15,7 +15,8 @@ obj-y := open.o read_write.o file_table.o super.o \
> pnode.o splice.o sync.o utimes.o d_path.o \
> stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
> fs_types.o fs_context.o fs_parser.o fsopen.o init.o \
> - kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o
> + kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o \
> + file_attr.o
>
> obj-$(CONFIG_BUFFER_HEAD) += buffer.o mpage.o
> obj-$(CONFIG_PROC_FS) += proc_namespace.o
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> new file mode 100644
> index 000000000000..2910b7047721
> --- /dev/null
> +++ b/fs/file_attr.c
> @@ -0,0 +1,318 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/fs.h>
> +#include <linux/security.h>
> +#include <linux/fscrypt.h>
> +#include <linux/fileattr.h>
> +
> +/**
> + * fileattr_fill_xflags - initialize fileattr with xflags
> + * @fa: fileattr pointer
> + * @xflags: FS_XFLAG_* flags
> + *
> + * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All
> + * other fields are zeroed.
> + */
> +void fileattr_fill_xflags(struct fileattr *fa, u32 xflags)
> +{
> + memset(fa, 0, sizeof(*fa));
> + fa->fsx_valid = true;
> + fa->fsx_xflags = xflags;
> + if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
> + fa->flags |= FS_IMMUTABLE_FL;
> + if (fa->fsx_xflags & FS_XFLAG_APPEND)
> + fa->flags |= FS_APPEND_FL;
> + if (fa->fsx_xflags & FS_XFLAG_SYNC)
> + fa->flags |= FS_SYNC_FL;
> + if (fa->fsx_xflags & FS_XFLAG_NOATIME)
> + fa->flags |= FS_NOATIME_FL;
> + if (fa->fsx_xflags & FS_XFLAG_NODUMP)
> + fa->flags |= FS_NODUMP_FL;
> + if (fa->fsx_xflags & FS_XFLAG_DAX)
> + fa->flags |= FS_DAX_FL;
> + if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
> + fa->flags |= FS_PROJINHERIT_FL;
> +}
> +EXPORT_SYMBOL(fileattr_fill_xflags);
> +
> +/**
> + * fileattr_fill_flags - initialize fileattr with flags
> + * @fa: fileattr pointer
> + * @flags: FS_*_FL flags
> + *
> + * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
> + * All other fields are zeroed.
> + */
> +void fileattr_fill_flags(struct fileattr *fa, u32 flags)
> +{
> + memset(fa, 0, sizeof(*fa));
> + fa->flags_valid = true;
> + fa->flags = flags;
> + if (fa->flags & FS_SYNC_FL)
> + fa->fsx_xflags |= FS_XFLAG_SYNC;
> + if (fa->flags & FS_IMMUTABLE_FL)
> + fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
> + if (fa->flags & FS_APPEND_FL)
> + fa->fsx_xflags |= FS_XFLAG_APPEND;
> + if (fa->flags & FS_NODUMP_FL)
> + fa->fsx_xflags |= FS_XFLAG_NODUMP;
> + if (fa->flags & FS_NOATIME_FL)
> + fa->fsx_xflags |= FS_XFLAG_NOATIME;
> + if (fa->flags & FS_DAX_FL)
> + fa->fsx_xflags |= FS_XFLAG_DAX;
> + if (fa->flags & FS_PROJINHERIT_FL)
> + fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
> +}
> +EXPORT_SYMBOL(fileattr_fill_flags);
> +
> +/**
> + * vfs_fileattr_get - retrieve miscellaneous file attributes
> + * @dentry: the object to retrieve from
> + * @fa: fileattr pointer
> + *
> + * Call i_op->fileattr_get() callback, if exists.
> + *
> + * Return: 0 on success, or a negative error on failure.
> + */
> +int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> +{
> + struct inode *inode = d_inode(dentry);
> +
> + if (!inode->i_op->fileattr_get)
> + return -ENOIOCTLCMD;
> +
> + return inode->i_op->fileattr_get(dentry, fa);
> +}
> +EXPORT_SYMBOL(vfs_fileattr_get);
> +
> +/**
> + * copy_fsxattr_to_user - copy fsxattr to userspace.
> + * @fa: fileattr pointer
> + * @ufa: fsxattr user pointer
> + *
> + * Return: 0 on success, or -EFAULT on failure.
> + */
> +int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> +{
> + struct fsxattr xfa;
> +
> + memset(&xfa, 0, sizeof(xfa));
> + xfa.fsx_xflags = fa->fsx_xflags;
> + xfa.fsx_extsize = fa->fsx_extsize;
> + xfa.fsx_nextents = fa->fsx_nextents;
> + xfa.fsx_projid = fa->fsx_projid;
> + xfa.fsx_cowextsize = fa->fsx_cowextsize;
> +
> + if (copy_to_user(ufa, &xfa, sizeof(xfa)))
> + return -EFAULT;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(copy_fsxattr_to_user);
> +
> +static int copy_fsxattr_from_user(struct fileattr *fa,
> + struct fsxattr __user *ufa)
> +{
> + struct fsxattr xfa;
> +
> + if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> + return -EFAULT;
> +
> + fileattr_fill_xflags(fa, xfa.fsx_xflags);
> + fa->fsx_extsize = xfa.fsx_extsize;
> + fa->fsx_nextents = xfa.fsx_nextents;
> + fa->fsx_projid = xfa.fsx_projid;
> + fa->fsx_cowextsize = xfa.fsx_cowextsize;
> +
> + return 0;
> +}
> +
> +/*
> + * Generic function to check FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS values and reject
> + * any invalid configurations.
> + *
> + * Note: must be called with inode lock held.
> + */
> +static int fileattr_set_prepare(struct inode *inode,
> + const struct fileattr *old_ma,
> + struct fileattr *fa)
> +{
> + int err;
> +
> + /*
> + * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> + * the relevant capability.
> + */
> + if ((fa->flags ^ old_ma->flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
> + !capable(CAP_LINUX_IMMUTABLE))
> + return -EPERM;
> +
> + err = fscrypt_prepare_setflags(inode, old_ma->flags, fa->flags);
> + if (err)
> + return err;
> +
> + /*
> + * Project Quota ID state is only allowed to change from within the init
> + * namespace. Enforce that restriction only if we are trying to change
> + * the quota ID state. Everything else is allowed in user namespaces.
> + */
> + if (current_user_ns() != &init_user_ns) {
> + if (old_ma->fsx_projid != fa->fsx_projid)
> + return -EINVAL;
> + if ((old_ma->fsx_xflags ^ fa->fsx_xflags) &
> + FS_XFLAG_PROJINHERIT)
> + return -EINVAL;
> + } else {
> + /*
> + * Caller is allowed to change the project ID. If it is being
> + * changed, make sure that the new value is valid.
> + */
> + if (old_ma->fsx_projid != fa->fsx_projid &&
> + !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid)))
> + return -EINVAL;
> + }
> +
> + /* Check extent size hints. */
> + if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode))
> + return -EINVAL;
> +
> + if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
> + !S_ISDIR(inode->i_mode))
> + return -EINVAL;
> +
> + if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
> + !S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
> + return -EINVAL;
> +
> + /*
> + * It is only valid to set the DAX flag on regular files and
> + * directories on filesystems.
> + */
> + if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
> + !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
> + return -EINVAL;
> +
> + /* Extent size hints of zero turn off the flags. */
> + if (fa->fsx_extsize == 0)
> + fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
> + if (fa->fsx_cowextsize == 0)
> + fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
> +
> + return 0;
> +}
> +
> +/**
> + * vfs_fileattr_set - change miscellaneous file attributes
> + * @idmap: idmap of the mount
> + * @dentry: the object to change
> + * @fa: fileattr pointer
> + *
> + * After verifying permissions, call i_op->fileattr_set() callback, if
> + * exists.
> + *
> + * Verifying attributes involves retrieving current attributes with
> + * i_op->fileattr_get(), this also allows initializing attributes that have
> + * not been set by the caller to current values. Inode lock is held
> + * thoughout to prevent racing with another instance.
> + *
> + * Return: 0 on success, or a negative error on failure.
> + */
> +int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> + struct fileattr *fa)
> +{
> + struct inode *inode = d_inode(dentry);
> + struct fileattr old_ma = {};
> + int err;
> +
> + if (!inode->i_op->fileattr_set)
> + return -ENOIOCTLCMD;
> +
> + if (!inode_owner_or_capable(idmap, inode))
> + return -EPERM;
> +
> + inode_lock(inode);
> + err = vfs_fileattr_get(dentry, &old_ma);
> + if (!err) {
> + /* initialize missing bits from old_ma */
> + if (fa->flags_valid) {
> + fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON;
> + fa->fsx_extsize = old_ma.fsx_extsize;
> + fa->fsx_nextents = old_ma.fsx_nextents;
> + fa->fsx_projid = old_ma.fsx_projid;
> + fa->fsx_cowextsize = old_ma.fsx_cowextsize;
> + } else {
> + fa->flags |= old_ma.flags & ~FS_COMMON_FL;
> + }
> + err = fileattr_set_prepare(inode, &old_ma, fa);
> + if (!err)
> + err = inode->i_op->fileattr_set(idmap, dentry, fa);
> + }
> + inode_unlock(inode);
> +
> + return err;
> +}
> +EXPORT_SYMBOL(vfs_fileattr_set);
> +
> +int ioctl_getflags(struct file *file, unsigned int __user *argp)
> +{
> + struct fileattr fa = { .flags_valid = true }; /* hint only */
> + int err;
> +
> + err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (!err)
> + err = put_user(fa.flags, argp);
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_getflags);
> +
> +int ioctl_setflags(struct file *file, unsigned int __user *argp)
> +{
> + struct mnt_idmap *idmap = file_mnt_idmap(file);
> + struct dentry *dentry = file->f_path.dentry;
> + struct fileattr fa;
> + unsigned int flags;
> + int err;
> +
> + err = get_user(flags, argp);
> + if (!err) {
> + err = mnt_want_write_file(file);
> + if (!err) {
> + fileattr_fill_flags(&fa, flags);
> + err = vfs_fileattr_set(idmap, dentry, &fa);
> + mnt_drop_write_file(file);
> + }
> + }
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_setflags);
> +
> +int ioctl_fsgetxattr(struct file *file, void __user *argp)
> +{
> + struct fileattr fa = { .fsx_valid = true }; /* hint only */
> + int err;
> +
> + err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (!err)
> + err = copy_fsxattr_to_user(&fa, argp);
> +
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_fsgetxattr);
> +
> +int ioctl_fssetxattr(struct file *file, void __user *argp)
> +{
> + struct mnt_idmap *idmap = file_mnt_idmap(file);
> + struct dentry *dentry = file->f_path.dentry;
> + struct fileattr fa;
> + int err;
> +
> + err = copy_fsxattr_from_user(&fa, argp);
> + if (!err) {
> + err = mnt_want_write_file(file);
> + if (!err) {
> + err = vfs_fileattr_set(idmap, dentry, &fa);
> + mnt_drop_write_file(file);
> + }
> + }
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_fssetxattr);
> diff --git a/fs/ioctl.c b/fs/ioctl.c
> index 69107a245b4c..0248cb8db2d3 100644
> --- a/fs/ioctl.c
> +++ b/fs/ioctl.c
> @@ -453,315 +453,6 @@ static int ioctl_file_dedupe_range(struct file *file,
> return ret;
> }
>
> -/**
> - * fileattr_fill_xflags - initialize fileattr with xflags
> - * @fa: fileattr pointer
> - * @xflags: FS_XFLAG_* flags
> - *
> - * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All
> - * other fields are zeroed.
> - */
> -void fileattr_fill_xflags(struct fileattr *fa, u32 xflags)
> -{
> - memset(fa, 0, sizeof(*fa));
> - fa->fsx_valid = true;
> - fa->fsx_xflags = xflags;
> - if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
> - fa->flags |= FS_IMMUTABLE_FL;
> - if (fa->fsx_xflags & FS_XFLAG_APPEND)
> - fa->flags |= FS_APPEND_FL;
> - if (fa->fsx_xflags & FS_XFLAG_SYNC)
> - fa->flags |= FS_SYNC_FL;
> - if (fa->fsx_xflags & FS_XFLAG_NOATIME)
> - fa->flags |= FS_NOATIME_FL;
> - if (fa->fsx_xflags & FS_XFLAG_NODUMP)
> - fa->flags |= FS_NODUMP_FL;
> - if (fa->fsx_xflags & FS_XFLAG_DAX)
> - fa->flags |= FS_DAX_FL;
> - if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
> - fa->flags |= FS_PROJINHERIT_FL;
> -}
> -EXPORT_SYMBOL(fileattr_fill_xflags);
> -
> -/**
> - * fileattr_fill_flags - initialize fileattr with flags
> - * @fa: fileattr pointer
> - * @flags: FS_*_FL flags
> - *
> - * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
> - * All other fields are zeroed.
> - */
> -void fileattr_fill_flags(struct fileattr *fa, u32 flags)
> -{
> - memset(fa, 0, sizeof(*fa));
> - fa->flags_valid = true;
> - fa->flags = flags;
> - if (fa->flags & FS_SYNC_FL)
> - fa->fsx_xflags |= FS_XFLAG_SYNC;
> - if (fa->flags & FS_IMMUTABLE_FL)
> - fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
> - if (fa->flags & FS_APPEND_FL)
> - fa->fsx_xflags |= FS_XFLAG_APPEND;
> - if (fa->flags & FS_NODUMP_FL)
> - fa->fsx_xflags |= FS_XFLAG_NODUMP;
> - if (fa->flags & FS_NOATIME_FL)
> - fa->fsx_xflags |= FS_XFLAG_NOATIME;
> - if (fa->flags & FS_DAX_FL)
> - fa->fsx_xflags |= FS_XFLAG_DAX;
> - if (fa->flags & FS_PROJINHERIT_FL)
> - fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
> -}
> -EXPORT_SYMBOL(fileattr_fill_flags);
> -
> -/**
> - * vfs_fileattr_get - retrieve miscellaneous file attributes
> - * @dentry: the object to retrieve from
> - * @fa: fileattr pointer
> - *
> - * Call i_op->fileattr_get() callback, if exists.
> - *
> - * Return: 0 on success, or a negative error on failure.
> - */
> -int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> -{
> - struct inode *inode = d_inode(dentry);
> -
> - if (!inode->i_op->fileattr_get)
> - return -ENOIOCTLCMD;
> -
> - return inode->i_op->fileattr_get(dentry, fa);
> -}
> -EXPORT_SYMBOL(vfs_fileattr_get);
> -
> -/**
> - * copy_fsxattr_to_user - copy fsxattr to userspace.
> - * @fa: fileattr pointer
> - * @ufa: fsxattr user pointer
> - *
> - * Return: 0 on success, or -EFAULT on failure.
> - */
> -int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> -{
> - struct fsxattr xfa;
> -
> - memset(&xfa, 0, sizeof(xfa));
> - xfa.fsx_xflags = fa->fsx_xflags;
> - xfa.fsx_extsize = fa->fsx_extsize;
> - xfa.fsx_nextents = fa->fsx_nextents;
> - xfa.fsx_projid = fa->fsx_projid;
> - xfa.fsx_cowextsize = fa->fsx_cowextsize;
> -
> - if (copy_to_user(ufa, &xfa, sizeof(xfa)))
> - return -EFAULT;
> -
> - return 0;
> -}
> -EXPORT_SYMBOL(copy_fsxattr_to_user);
> -
> -static int copy_fsxattr_from_user(struct fileattr *fa,
> - struct fsxattr __user *ufa)
> -{
> - struct fsxattr xfa;
> -
> - if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> - return -EFAULT;
> -
> - fileattr_fill_xflags(fa, xfa.fsx_xflags);
> - fa->fsx_extsize = xfa.fsx_extsize;
> - fa->fsx_nextents = xfa.fsx_nextents;
> - fa->fsx_projid = xfa.fsx_projid;
> - fa->fsx_cowextsize = xfa.fsx_cowextsize;
> -
> - return 0;
> -}
> -
> -/*
> - * Generic function to check FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS values and reject
> - * any invalid configurations.
> - *
> - * Note: must be called with inode lock held.
> - */
> -static int fileattr_set_prepare(struct inode *inode,
> - const struct fileattr *old_ma,
> - struct fileattr *fa)
> -{
> - int err;
> -
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - if ((fa->flags ^ old_ma->flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
> - !capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> -
> - err = fscrypt_prepare_setflags(inode, old_ma->flags, fa->flags);
> - if (err)
> - return err;
> -
> - /*
> - * Project Quota ID state is only allowed to change from within the init
> - * namespace. Enforce that restriction only if we are trying to change
> - * the quota ID state. Everything else is allowed in user namespaces.
> - */
> - if (current_user_ns() != &init_user_ns) {
> - if (old_ma->fsx_projid != fa->fsx_projid)
> - return -EINVAL;
> - if ((old_ma->fsx_xflags ^ fa->fsx_xflags) &
> - FS_XFLAG_PROJINHERIT)
> - return -EINVAL;
> - } else {
> - /*
> - * Caller is allowed to change the project ID. If it is being
> - * changed, make sure that the new value is valid.
> - */
> - if (old_ma->fsx_projid != fa->fsx_projid &&
> - !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid)))
> - return -EINVAL;
> - }
> -
> - /* Check extent size hints. */
> - if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode))
> - return -EINVAL;
> -
> - if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
> - !S_ISDIR(inode->i_mode))
> - return -EINVAL;
> -
> - if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
> - !S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
> - return -EINVAL;
> -
> - /*
> - * It is only valid to set the DAX flag on regular files and
> - * directories on filesystems.
> - */
> - if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
> - !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
> - return -EINVAL;
> -
> - /* Extent size hints of zero turn off the flags. */
> - if (fa->fsx_extsize == 0)
> - fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
> - if (fa->fsx_cowextsize == 0)
> - fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
> -
> - return 0;
> -}
> -
> -/**
> - * vfs_fileattr_set - change miscellaneous file attributes
> - * @idmap: idmap of the mount
> - * @dentry: the object to change
> - * @fa: fileattr pointer
> - *
> - * After verifying permissions, call i_op->fileattr_set() callback, if
> - * exists.
> - *
> - * Verifying attributes involves retrieving current attributes with
> - * i_op->fileattr_get(), this also allows initializing attributes that have
> - * not been set by the caller to current values. Inode lock is held
> - * thoughout to prevent racing with another instance.
> - *
> - * Return: 0 on success, or a negative error on failure.
> - */
> -int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> - struct fileattr *fa)
> -{
> - struct inode *inode = d_inode(dentry);
> - struct fileattr old_ma = {};
> - int err;
> -
> - if (!inode->i_op->fileattr_set)
> - return -ENOIOCTLCMD;
> -
> - if (!inode_owner_or_capable(idmap, inode))
> - return -EPERM;
> -
> - inode_lock(inode);
> - err = vfs_fileattr_get(dentry, &old_ma);
> - if (!err) {
> - /* initialize missing bits from old_ma */
> - if (fa->flags_valid) {
> - fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON;
> - fa->fsx_extsize = old_ma.fsx_extsize;
> - fa->fsx_nextents = old_ma.fsx_nextents;
> - fa->fsx_projid = old_ma.fsx_projid;
> - fa->fsx_cowextsize = old_ma.fsx_cowextsize;
> - } else {
> - fa->flags |= old_ma.flags & ~FS_COMMON_FL;
> - }
> - err = fileattr_set_prepare(inode, &old_ma, fa);
> - if (!err)
> - err = inode->i_op->fileattr_set(idmap, dentry, fa);
> - }
> - inode_unlock(inode);
> -
> - return err;
> -}
> -EXPORT_SYMBOL(vfs_fileattr_set);
> -
> -static int ioctl_getflags(struct file *file, unsigned int __user *argp)
> -{
> - struct fileattr fa = { .flags_valid = true }; /* hint only */
> - int err;
> -
> - err = vfs_fileattr_get(file->f_path.dentry, &fa);
> - if (!err)
> - err = put_user(fa.flags, argp);
> - return err;
> -}
> -
> -static int ioctl_setflags(struct file *file, unsigned int __user *argp)
> -{
> - struct mnt_idmap *idmap = file_mnt_idmap(file);
> - struct dentry *dentry = file->f_path.dentry;
> - struct fileattr fa;
> - unsigned int flags;
> - int err;
> -
> - err = get_user(flags, argp);
> - if (!err) {
> - err = mnt_want_write_file(file);
> - if (!err) {
> - fileattr_fill_flags(&fa, flags);
> - err = vfs_fileattr_set(idmap, dentry, &fa);
> - mnt_drop_write_file(file);
> - }
> - }
> - return err;
> -}
> -
> -static int ioctl_fsgetxattr(struct file *file, void __user *argp)
> -{
> - struct fileattr fa = { .fsx_valid = true }; /* hint only */
> - int err;
> -
> - err = vfs_fileattr_get(file->f_path.dentry, &fa);
> - if (!err)
> - err = copy_fsxattr_to_user(&fa, argp);
> -
> - return err;
> -}
> -
> -static int ioctl_fssetxattr(struct file *file, void __user *argp)
> -{
> - struct mnt_idmap *idmap = file_mnt_idmap(file);
> - struct dentry *dentry = file->f_path.dentry;
> - struct fileattr fa;
> - int err;
> -
> - err = copy_fsxattr_from_user(&fa, argp);
> - if (!err) {
> - err = mnt_want_write_file(file);
> - if (!err) {
> - err = vfs_fileattr_set(idmap, dentry, &fa);
> - mnt_drop_write_file(file);
> - }
> - }
> - return err;
> -}
> -
> static int ioctl_getfsuuid(struct file *file, void __user *argp)
> {
> struct super_block *sb = file_inode(file)->i_sb;
> diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h
> index 47c05a9851d0..6030d0bf7ad3 100644
> --- a/include/linux/fileattr.h
> +++ b/include/linux/fileattr.h
> @@ -55,5 +55,9 @@ static inline bool fileattr_has_fsx(const struct fileattr *fa)
> int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
> int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> struct fileattr *fa);
> +int ioctl_getflags(struct file *file, unsigned int __user *argp);
> +int ioctl_setflags(struct file *file, unsigned int __user *argp);
> +int ioctl_fsgetxattr(struct file *file, void __user *argp);
> +int ioctl_fssetxattr(struct file *file, void __user *argp);
>
> #endif /* _LINUX_FILEATTR_H */
>
> --
> 2.47.2
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 2/6] lsm: introduce new hooks for setting/getting inode fsxattr
2025-06-30 16:20 ` [PATCH v6 2/6] lsm: introduce new hooks for setting/getting inode fsxattr Andrey Albershteyn
@ 2025-07-01 12:39 ` Jan Kara
2025-07-01 18:18 ` Darrick J. Wong
1 sibling, 0 replies; 47+ messages in thread
From: Jan Kara @ 2025-07-01 12:39 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon 30-06-25 18:20:12, Andrey Albershteyn wrote:
> Introduce new hooks for setting and getting filesystem extended
> attributes on inode (FS_IOC_FSGETXATTR).
>
> Cc: selinux@vger.kernel.org
> Cc: Paul Moore <paul@paul-moore.com>
>
> Acked-by: Paul Moore <paul@paul-moore.com>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/file_attr.c | 19 ++++++++++++++++---
> include/linux/lsm_hook_defs.h | 2 ++
> include/linux/security.h | 16 ++++++++++++++++
> security/security.c | 30 ++++++++++++++++++++++++++++++
> 4 files changed, 64 insertions(+), 3 deletions(-)
>
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> index 2910b7047721..be62d97cc444 100644
> --- a/fs/file_attr.c
> +++ b/fs/file_attr.c
> @@ -76,10 +76,15 @@ EXPORT_SYMBOL(fileattr_fill_flags);
> int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> {
> struct inode *inode = d_inode(dentry);
> + int error;
>
> if (!inode->i_op->fileattr_get)
> return -ENOIOCTLCMD;
>
> + error = security_inode_file_getattr(dentry, fa);
> + if (error)
> + return error;
> +
> return inode->i_op->fileattr_get(dentry, fa);
> }
> EXPORT_SYMBOL(vfs_fileattr_get);
> @@ -242,12 +247,20 @@ int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> } else {
> fa->flags |= old_ma.flags & ~FS_COMMON_FL;
> }
> +
> err = fileattr_set_prepare(inode, &old_ma, fa);
> - if (!err)
> - err = inode->i_op->fileattr_set(idmap, dentry, fa);
> + if (err)
> + goto out;
> + err = security_inode_file_setattr(dentry, fa);
> + if (err)
> + goto out;
> + err = inode->i_op->fileattr_set(idmap, dentry, fa);
> + if (err)
> + goto out;
> }
> +
> +out:
> inode_unlock(inode);
> -
> return err;
> }
> EXPORT_SYMBOL(vfs_fileattr_set);
> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> index bf3bbac4e02a..9600a4350e79 100644
> --- a/include/linux/lsm_hook_defs.h
> +++ b/include/linux/lsm_hook_defs.h
> @@ -157,6 +157,8 @@ LSM_HOOK(int, 0, inode_removexattr, struct mnt_idmap *idmap,
> struct dentry *dentry, const char *name)
> LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
> const char *name)
> +LSM_HOOK(int, 0, inode_file_setattr, struct dentry *dentry, struct fileattr *fa)
> +LSM_HOOK(int, 0, inode_file_getattr, struct dentry *dentry, struct fileattr *fa)
> LSM_HOOK(int, 0, inode_set_acl, struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)
> LSM_HOOK(void, LSM_RET_VOID, inode_post_set_acl, struct dentry *dentry,
> diff --git a/include/linux/security.h b/include/linux/security.h
> index dba349629229..9ed0d0e0c81f 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -451,6 +451,10 @@ int security_inode_listxattr(struct dentry *dentry);
> int security_inode_removexattr(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *name);
> void security_inode_post_removexattr(struct dentry *dentry, const char *name);
> +int security_inode_file_setattr(struct dentry *dentry,
> + struct fileattr *fa);
> +int security_inode_file_getattr(struct dentry *dentry,
> + struct fileattr *fa);
> int security_inode_need_killpriv(struct dentry *dentry);
> int security_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry);
> int security_inode_getsecurity(struct mnt_idmap *idmap,
> @@ -1052,6 +1056,18 @@ static inline void security_inode_post_removexattr(struct dentry *dentry,
> const char *name)
> { }
>
> +static inline int security_inode_file_setattr(struct dentry *dentry,
> + struct fileattr *fa)
> +{
> + return 0;
> +}
> +
> +static inline int security_inode_file_getattr(struct dentry *dentry,
> + struct fileattr *fa)
> +{
> + return 0;
> +}
> +
> static inline int security_inode_need_killpriv(struct dentry *dentry)
> {
> return cap_inode_need_killpriv(dentry);
> diff --git a/security/security.c b/security/security.c
> index 596d41818577..711b4de40b8d 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -2622,6 +2622,36 @@ void security_inode_post_removexattr(struct dentry *dentry, const char *name)
> call_void_hook(inode_post_removexattr, dentry, name);
> }
>
> +/**
> + * security_inode_file_setattr() - check if setting fsxattr is allowed
> + * @dentry: file to set filesystem extended attributes on
> + * @fa: extended attributes to set on the inode
> + *
> + * Called when file_setattr() syscall or FS_IOC_FSSETXATTR ioctl() is called on
> + * inode
> + *
> + * Return: Returns 0 if permission is granted.
> + */
> +int security_inode_file_setattr(struct dentry *dentry, struct fileattr *fa)
> +{
> + return call_int_hook(inode_file_setattr, dentry, fa);
> +}
> +
> +/**
> + * security_inode_file_getattr() - check if retrieving fsxattr is allowed
> + * @dentry: file to retrieve filesystem extended attributes from
> + * @fa: extended attributes to get
> + *
> + * Called when file_getattr() syscall or FS_IOC_FSGETXATTR ioctl() is called on
> + * inode
> + *
> + * Return: Returns 0 if permission is granted.
> + */
> +int security_inode_file_getattr(struct dentry *dentry, struct fileattr *fa)
> +{
> + return call_int_hook(inode_file_getattr, dentry, fa);
> +}
> +
> /**
> * security_inode_need_killpriv() - Check if security_inode_killpriv() required
> * @dentry: associated dentry
>
> --
> 2.47.2
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP
2025-07-01 6:05 ` Amir Goldstein
@ 2025-07-01 12:51 ` Jan Kara
2025-07-01 14:16 ` Amir Goldstein
0 siblings, 1 reply; 47+ messages in thread
From: Jan Kara @ 2025-07-01 12:51 UTC (permalink / raw)
To: Amir Goldstein
Cc: Andrey Albershteyn, Arnd Bergmann, Casey Schaufler,
Christian Brauner, Jan Kara, Pali Rohár, Paul Moore,
linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Tue 01-07-25 08:05:45, Amir Goldstein wrote:
> On Mon, Jun 30, 2025 at 6:20 PM Andrey Albershteyn <aalbersh@redhat.com> wrote:
> >
> > Future patches will add new syscalls which use these functions. As
> > this interface won't be used for ioctls only, the EOPNOSUPP is more
> > appropriate return code.
> >
> > This patch converts return code from ENOIOCTLCMD to EOPNOSUPP for
> > vfs_fileattr_get and vfs_fileattr_set. To save old behavior translate
> > EOPNOSUPP back for current users - overlayfs, encryptfs and fs/ioctl.c.
> >
> > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
...
> > --- a/fs/overlayfs/inode.c
> > +++ b/fs/overlayfs/inode.c
> > @@ -721,7 +721,7 @@ int ovl_real_fileattr_get(const struct path *realpath, struct fileattr *fa)
> > return err;
> >
> > err = vfs_fileattr_get(realpath->dentry, fa);
> > - if (err == -ENOIOCTLCMD)
> > + if (err == -EOPNOTSUPP)
> > err = -ENOTTY;
> > return err;
> > }
>
> That's the wrong way, because it hides the desired -EOPNOTSUPP
> return code from ovl_fileattr_get().
>
> The conversion to -ENOTTY was done for
> 5b0a414d06c3 ("ovl: fix filattr copy-up failure"),
> so please do this instead:
>
> --- a/fs/overlayfs/inode.c
> +++ b/fs/overlayfs/inode.c
> @@ -722,7 +722,7 @@ int ovl_real_fileattr_get(const struct path
> *realpath, struct fileattr *fa)
>
> err = vfs_fileattr_get(realpath->dentry, fa);
> if (err == -ENOIOCTLCMD)
> - err = -ENOTTY;
> + err = -EOPNOTSUPP;
Is this really needed? AFAICS nobody returns ENOIOCTLCMD after this
patch...
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP
2025-06-30 16:20 ` [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP Andrey Albershteyn
2025-06-30 18:05 ` Pali Rohár
2025-07-01 6:05 ` Amir Goldstein
@ 2025-07-01 12:52 ` Jan Kara
2025-07-01 18:18 ` Darrick J. Wong
3 siblings, 0 replies; 47+ messages in thread
From: Jan Kara @ 2025-07-01 12:52 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon 30-06-25 18:20:14, Andrey Albershteyn wrote:
> Future patches will add new syscalls which use these functions. As
> this interface won't be used for ioctls only, the EOPNOSUPP is more
> appropriate return code.
>
> This patch converts return code from ENOIOCTLCMD to EOPNOSUPP for
> vfs_fileattr_get and vfs_fileattr_set. To save old behavior translate
> EOPNOSUPP back for current users - overlayfs, encryptfs and fs/ioctl.c.
>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Modulo the small nits already pointed out this looks good to me. Feel free
to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 5/6] fs: prepare for extending file_get/setattr()
2025-06-30 16:20 ` [PATCH v6 5/6] fs: prepare for extending file_get/setattr() Andrey Albershteyn
@ 2025-07-01 13:06 ` Jan Kara
2025-07-01 18:31 ` Darrick J. Wong
1 sibling, 0 replies; 47+ messages in thread
From: Jan Kara @ 2025-07-01 13:06 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon 30-06-25 18:20:15, Andrey Albershteyn wrote:
> From: Amir Goldstein <amir73il@gmail.com>
>
> We intend to add support for more xflags to selective filesystems and
> We cannot rely on copy_struct_from_user() to detect this extension.
>
> In preparation of extending the API, do not allow setting xflags unknown
> by this kernel version.
>
> Also do not pass the read-only flags and read-only field fsx_nextents to
> filesystem.
>
> These changes should not affect existing chattr programs that use the
> ioctl to get fsxattr before setting the new values.
>
> Link: https://lore.kernel.org/linux-fsdevel/20250216164029.20673-4-pali@kernel.org/
> Cc: Pali Rohár <pali@kernel.org>
> Cc: Andrey Albershteyn <aalbersh@redhat.com>
> Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
I'd just note that:
> @@ -118,11 +119,16 @@ static int copy_fsxattr_from_user(struct fileattr *fa,
> struct fsxattr __user *ufa)
> {
> struct fsxattr xfa;
> + __u32 mask = FS_XFLAGS_MASK;
>
> if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> return -EFAULT;
>
> + if (xfa.fsx_xflags & ~mask)
> + return -EINVAL;
> +
> fileattr_fill_xflags(fa, xfa.fsx_xflags);
> + fa->fsx_xflags &= ~FS_XFLAG_RDONLY_MASK;
This means that the two flags in FS_XFLAG_RDONLY_MASK cannot easily become
writeable in the future due to this. I think it is a sensible compromise
but I wanted to mention it.
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-06-30 16:20 ` [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
2025-07-01 12:34 ` Christian Brauner
@ 2025-07-01 13:24 ` Jan Kara
2025-07-01 18:43 ` Darrick J. Wong
2 siblings, 0 replies; 47+ messages in thread
From: Jan Kara @ 2025-07-01 13:24 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon 30-06-25 18:20:16, Andrey Albershteyn wrote:
> From: Andrey Albershteyn <aalbersh@redhat.com>
>
> 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()
^^^ according
> semantics. The struct fsx_fileattr 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
^^^ doesn't
> 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>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
There's possible NULL ptr deref bug below (2x) that's easy to fix. Once
done feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
> @@ -343,3 +377,117 @@ 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 fsx_fileattr __user *, ufsx, size_t, usize,
> + unsigned int, at_flags)
> +{
> + struct fileattr fa;
> + struct path filepath __free(path_put) = {};
> + int error;
> + unsigned int lookup_flags = 0;
> + struct filename *name __free(putname) = NULL;
> + struct fsx_fileattr fsx;
> +
> + BUILD_BUG_ON(sizeof(struct fsx_fileattr) < FSX_FILEATTR_SIZE_VER0);
> + BUILD_BUG_ON(sizeof(struct fsx_fileattr) != FSX_FILEATTR_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 < FSX_FILEATTR_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);
> +
> + filepath = fd_file(f)->f_path;
If dfd is not correct fd, then this will dereference NULL AFAICT. I think
you need here:
if (fd_empty(f))
return -EBADF;
> + 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_fsx_fileattr(&fa, &fsx);
> + error = copy_struct_to_user(ufsx, usize, &fsx,
> + sizeof(struct fsx_fileattr), NULL);
> +
> + return error;
> +}
> +
> +SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename,
> + struct fsx_fileattr __user *, ufsx, size_t, usize,
> + unsigned int, at_flags)
> +{
> + struct fileattr fa;
> + struct path filepath __free(path_put) = {};
> + int error;
> + unsigned int lookup_flags = 0;
> + struct filename *name __free(putname) = NULL;
> + struct fsx_fileattr fsx;
> +
> + BUILD_BUG_ON(sizeof(struct fsx_fileattr) < FSX_FILEATTR_SIZE_VER0);
> + BUILD_BUG_ON(sizeof(struct fsx_fileattr) != FSX_FILEATTR_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 < FSX_FILEATTR_SIZE_VER0)
> + return -EINVAL;
> +
> + error = copy_struct_from_user(&fsx, sizeof(struct fsx_fileattr), ufsx,
> + usize);
> + if (error)
> + return error;
> +
> + error = fsx_fileattr_to_fileattr(&fsx, &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);
> +
Same comment here as above.
> + 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;
> +}
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP
2025-07-01 12:51 ` Jan Kara
@ 2025-07-01 14:16 ` Amir Goldstein
0 siblings, 0 replies; 47+ messages in thread
From: Amir Goldstein @ 2025-07-01 14:16 UTC (permalink / raw)
To: Jan Kara
Cc: Andrey Albershteyn, Arnd Bergmann, Casey Schaufler,
Christian Brauner, Pali Rohár, Paul Moore, linux-api,
linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
[-- Attachment #1: Type: text/plain, Size: 2222 bytes --]
On Tue, Jul 1, 2025 at 2:51 PM Jan Kara <jack@suse.cz> wrote:
>
> On Tue 01-07-25 08:05:45, Amir Goldstein wrote:
> > On Mon, Jun 30, 2025 at 6:20 PM Andrey Albershteyn <aalbersh@redhat.com> wrote:
> > >
> > > Future patches will add new syscalls which use these functions. As
> > > this interface won't be used for ioctls only, the EOPNOSUPP is more
> > > appropriate return code.
> > >
> > > This patch converts return code from ENOIOCTLCMD to EOPNOSUPP for
> > > vfs_fileattr_get and vfs_fileattr_set. To save old behavior translate
> > > EOPNOSUPP back for current users - overlayfs, encryptfs and fs/ioctl.c.
> > >
> > > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> ...
> > > --- a/fs/overlayfs/inode.c
> > > +++ b/fs/overlayfs/inode.c
> > > @@ -721,7 +721,7 @@ int ovl_real_fileattr_get(const struct path *realpath, struct fileattr *fa)
> > > return err;
> > >
> > > err = vfs_fileattr_get(realpath->dentry, fa);
> > > - if (err == -ENOIOCTLCMD)
> > > + if (err == -EOPNOTSUPP)
> > > err = -ENOTTY;
> > > return err;
> > > }
> >
> > That's the wrong way, because it hides the desired -EOPNOTSUPP
> > return code from ovl_fileattr_get().
> >
> > The conversion to -ENOTTY was done for
> > 5b0a414d06c3 ("ovl: fix filattr copy-up failure"),
> > so please do this instead:
> >
> > --- a/fs/overlayfs/inode.c
> > +++ b/fs/overlayfs/inode.c
> > @@ -722,7 +722,7 @@ int ovl_real_fileattr_get(const struct path
> > *realpath, struct fileattr *fa)
> >
> > err = vfs_fileattr_get(realpath->dentry, fa);
> > if (err == -ENOIOCTLCMD)
> > - err = -ENOTTY;
> > + err = -EOPNOTSUPP;
>
> Is this really needed? AFAICS nobody returns ENOIOCTLCMD after this
> patch...
you are right it is not needed
Attaching the patch with missing bits of fuse and overlayfs to make this
conversion complete.
Christian, please squash my patch
and afterward make sure there is no conversion remaining in
ovl_real_fileattr_get() as well as in ecryptfs_fileattr_get()
Both those helpers should return the value they
got from vfs_fileattr_get() as is.
Thanks,
Amir.
[-- Attachment #2: 0001-fuse-return-EOPNOTSUPP-from-fileattr_-gs-et-instead-.patch --]
[-- Type: text/x-patch, Size: 2017 bytes --]
From 85d097f639518670c57827513b02f497950071de Mon Sep 17 00:00:00 2001
From: Amir Goldstein <amir73il@gmail.com>
Date: Tue, 1 Jul 2025 16:06:44 +0200
Subject: [PATCH] fuse: return -EOPNOTSUPP from ->fileattr_[gs]et() instead of
-ENOTTY
As part of changing calling convenstion of ->fileattr_[gs]et()
to return -EOPNOTSUPP and fix related overlayfs code.
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
fs/fuse/ioctl.c | 4 ++++
fs/overlayfs/copy_up.c | 2 +-
fs/overlayfs/inode.c | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c
index 2d9abf48828f..f2692f7d5932 100644
--- a/fs/fuse/ioctl.c
+++ b/fs/fuse/ioctl.c
@@ -536,6 +536,8 @@ int fuse_fileattr_get(struct dentry *dentry, struct fileattr *fa)
cleanup:
fuse_priv_ioctl_cleanup(inode, ff);
+ if (err == -ENOTTY)
+ err = -EOPNOTSUPP;
return err;
}
@@ -572,5 +574,7 @@ int fuse_fileattr_set(struct mnt_idmap *idmap,
cleanup:
fuse_priv_ioctl_cleanup(inode, ff);
+ if (err == -ENOTTY)
+ err = -EOPNOTSUPP;
return err;
}
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index d7310fcf3888..2c646b7076d0 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -178,7 +178,7 @@ static int ovl_copy_fileattr(struct inode *inode, const struct path *old,
err = ovl_real_fileattr_get(old, &oldfa);
if (err) {
/* Ntfs-3g returns -EINVAL for "no fileattr support" */
- if (err == -ENOTTY || err == -EINVAL)
+ if (err == -EOPNOTSUPP || err == -EINVAL)
return 0;
pr_warn("failed to retrieve lower fileattr (%pd2, err=%i)\n",
old->dentry, err);
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 6f0e15f86c21..92754749f316 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -722,7 +722,7 @@ int ovl_real_fileattr_get(const struct path *realpath, struct fileattr *fa)
err = vfs_fileattr_get(realpath->dentry, fa);
if (err == -ENOIOCTLCMD)
- err = -ENOTTY;
+ err = -EOPNOTSUPP;
return err;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 47+ messages in thread
* Re: [PATCH v6 1/6] fs: split fileattr related helpers into separate file
2025-06-30 16:20 ` [PATCH v6 1/6] fs: split fileattr related helpers into separate file Andrey Albershteyn
2025-07-01 5:39 ` Amir Goldstein
2025-07-01 12:38 ` Jan Kara
@ 2025-07-01 18:13 ` Darrick J. Wong
2 siblings, 0 replies; 47+ messages in thread
From: Darrick J. Wong @ 2025-07-01 18:13 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon, Jun 30, 2025 at 06:20:11PM +0200, Andrey Albershteyn wrote:
> From: Andrey Albershteyn <aalbersh@kernel.org>
>
> This patch moves function related to file extended attributes
> manipulations to separate file. Refactoring only.
>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
Seems fine to me to move that to a separate file.
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
--D
> ---
> fs/Makefile | 3 +-
> fs/file_attr.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++
> fs/ioctl.c | 309 ---------------------------------------------
> include/linux/fileattr.h | 4 +
> 4 files changed, 324 insertions(+), 310 deletions(-)
>
> diff --git a/fs/Makefile b/fs/Makefile
> index 79c08b914c47..334654f9584b 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -15,7 +15,8 @@ obj-y := open.o read_write.o file_table.o super.o \
> pnode.o splice.o sync.o utimes.o d_path.o \
> stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
> fs_types.o fs_context.o fs_parser.o fsopen.o init.o \
> - kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o
> + kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o \
> + file_attr.o
>
> obj-$(CONFIG_BUFFER_HEAD) += buffer.o mpage.o
> obj-$(CONFIG_PROC_FS) += proc_namespace.o
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> new file mode 100644
> index 000000000000..2910b7047721
> --- /dev/null
> +++ b/fs/file_attr.c
> @@ -0,0 +1,318 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/fs.h>
> +#include <linux/security.h>
> +#include <linux/fscrypt.h>
> +#include <linux/fileattr.h>
> +
> +/**
> + * fileattr_fill_xflags - initialize fileattr with xflags
> + * @fa: fileattr pointer
> + * @xflags: FS_XFLAG_* flags
> + *
> + * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All
> + * other fields are zeroed.
> + */
> +void fileattr_fill_xflags(struct fileattr *fa, u32 xflags)
> +{
> + memset(fa, 0, sizeof(*fa));
> + fa->fsx_valid = true;
> + fa->fsx_xflags = xflags;
> + if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
> + fa->flags |= FS_IMMUTABLE_FL;
> + if (fa->fsx_xflags & FS_XFLAG_APPEND)
> + fa->flags |= FS_APPEND_FL;
> + if (fa->fsx_xflags & FS_XFLAG_SYNC)
> + fa->flags |= FS_SYNC_FL;
> + if (fa->fsx_xflags & FS_XFLAG_NOATIME)
> + fa->flags |= FS_NOATIME_FL;
> + if (fa->fsx_xflags & FS_XFLAG_NODUMP)
> + fa->flags |= FS_NODUMP_FL;
> + if (fa->fsx_xflags & FS_XFLAG_DAX)
> + fa->flags |= FS_DAX_FL;
> + if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
> + fa->flags |= FS_PROJINHERIT_FL;
> +}
> +EXPORT_SYMBOL(fileattr_fill_xflags);
> +
> +/**
> + * fileattr_fill_flags - initialize fileattr with flags
> + * @fa: fileattr pointer
> + * @flags: FS_*_FL flags
> + *
> + * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
> + * All other fields are zeroed.
> + */
> +void fileattr_fill_flags(struct fileattr *fa, u32 flags)
> +{
> + memset(fa, 0, sizeof(*fa));
> + fa->flags_valid = true;
> + fa->flags = flags;
> + if (fa->flags & FS_SYNC_FL)
> + fa->fsx_xflags |= FS_XFLAG_SYNC;
> + if (fa->flags & FS_IMMUTABLE_FL)
> + fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
> + if (fa->flags & FS_APPEND_FL)
> + fa->fsx_xflags |= FS_XFLAG_APPEND;
> + if (fa->flags & FS_NODUMP_FL)
> + fa->fsx_xflags |= FS_XFLAG_NODUMP;
> + if (fa->flags & FS_NOATIME_FL)
> + fa->fsx_xflags |= FS_XFLAG_NOATIME;
> + if (fa->flags & FS_DAX_FL)
> + fa->fsx_xflags |= FS_XFLAG_DAX;
> + if (fa->flags & FS_PROJINHERIT_FL)
> + fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
> +}
> +EXPORT_SYMBOL(fileattr_fill_flags);
> +
> +/**
> + * vfs_fileattr_get - retrieve miscellaneous file attributes
> + * @dentry: the object to retrieve from
> + * @fa: fileattr pointer
> + *
> + * Call i_op->fileattr_get() callback, if exists.
> + *
> + * Return: 0 on success, or a negative error on failure.
> + */
> +int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> +{
> + struct inode *inode = d_inode(dentry);
> +
> + if (!inode->i_op->fileattr_get)
> + return -ENOIOCTLCMD;
> +
> + return inode->i_op->fileattr_get(dentry, fa);
> +}
> +EXPORT_SYMBOL(vfs_fileattr_get);
> +
> +/**
> + * copy_fsxattr_to_user - copy fsxattr to userspace.
> + * @fa: fileattr pointer
> + * @ufa: fsxattr user pointer
> + *
> + * Return: 0 on success, or -EFAULT on failure.
> + */
> +int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> +{
> + struct fsxattr xfa;
> +
> + memset(&xfa, 0, sizeof(xfa));
> + xfa.fsx_xflags = fa->fsx_xflags;
> + xfa.fsx_extsize = fa->fsx_extsize;
> + xfa.fsx_nextents = fa->fsx_nextents;
> + xfa.fsx_projid = fa->fsx_projid;
> + xfa.fsx_cowextsize = fa->fsx_cowextsize;
> +
> + if (copy_to_user(ufa, &xfa, sizeof(xfa)))
> + return -EFAULT;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(copy_fsxattr_to_user);
> +
> +static int copy_fsxattr_from_user(struct fileattr *fa,
> + struct fsxattr __user *ufa)
> +{
> + struct fsxattr xfa;
> +
> + if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> + return -EFAULT;
> +
> + fileattr_fill_xflags(fa, xfa.fsx_xflags);
> + fa->fsx_extsize = xfa.fsx_extsize;
> + fa->fsx_nextents = xfa.fsx_nextents;
> + fa->fsx_projid = xfa.fsx_projid;
> + fa->fsx_cowextsize = xfa.fsx_cowextsize;
> +
> + return 0;
> +}
> +
> +/*
> + * Generic function to check FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS values and reject
> + * any invalid configurations.
> + *
> + * Note: must be called with inode lock held.
> + */
> +static int fileattr_set_prepare(struct inode *inode,
> + const struct fileattr *old_ma,
> + struct fileattr *fa)
> +{
> + int err;
> +
> + /*
> + * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> + * the relevant capability.
> + */
> + if ((fa->flags ^ old_ma->flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
> + !capable(CAP_LINUX_IMMUTABLE))
> + return -EPERM;
> +
> + err = fscrypt_prepare_setflags(inode, old_ma->flags, fa->flags);
> + if (err)
> + return err;
> +
> + /*
> + * Project Quota ID state is only allowed to change from within the init
> + * namespace. Enforce that restriction only if we are trying to change
> + * the quota ID state. Everything else is allowed in user namespaces.
> + */
> + if (current_user_ns() != &init_user_ns) {
> + if (old_ma->fsx_projid != fa->fsx_projid)
> + return -EINVAL;
> + if ((old_ma->fsx_xflags ^ fa->fsx_xflags) &
> + FS_XFLAG_PROJINHERIT)
> + return -EINVAL;
> + } else {
> + /*
> + * Caller is allowed to change the project ID. If it is being
> + * changed, make sure that the new value is valid.
> + */
> + if (old_ma->fsx_projid != fa->fsx_projid &&
> + !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid)))
> + return -EINVAL;
> + }
> +
> + /* Check extent size hints. */
> + if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode))
> + return -EINVAL;
> +
> + if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
> + !S_ISDIR(inode->i_mode))
> + return -EINVAL;
> +
> + if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
> + !S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
> + return -EINVAL;
> +
> + /*
> + * It is only valid to set the DAX flag on regular files and
> + * directories on filesystems.
> + */
> + if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
> + !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
> + return -EINVAL;
> +
> + /* Extent size hints of zero turn off the flags. */
> + if (fa->fsx_extsize == 0)
> + fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
> + if (fa->fsx_cowextsize == 0)
> + fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
> +
> + return 0;
> +}
> +
> +/**
> + * vfs_fileattr_set - change miscellaneous file attributes
> + * @idmap: idmap of the mount
> + * @dentry: the object to change
> + * @fa: fileattr pointer
> + *
> + * After verifying permissions, call i_op->fileattr_set() callback, if
> + * exists.
> + *
> + * Verifying attributes involves retrieving current attributes with
> + * i_op->fileattr_get(), this also allows initializing attributes that have
> + * not been set by the caller to current values. Inode lock is held
> + * thoughout to prevent racing with another instance.
> + *
> + * Return: 0 on success, or a negative error on failure.
> + */
> +int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> + struct fileattr *fa)
> +{
> + struct inode *inode = d_inode(dentry);
> + struct fileattr old_ma = {};
> + int err;
> +
> + if (!inode->i_op->fileattr_set)
> + return -ENOIOCTLCMD;
> +
> + if (!inode_owner_or_capable(idmap, inode))
> + return -EPERM;
> +
> + inode_lock(inode);
> + err = vfs_fileattr_get(dentry, &old_ma);
> + if (!err) {
> + /* initialize missing bits from old_ma */
> + if (fa->flags_valid) {
> + fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON;
> + fa->fsx_extsize = old_ma.fsx_extsize;
> + fa->fsx_nextents = old_ma.fsx_nextents;
> + fa->fsx_projid = old_ma.fsx_projid;
> + fa->fsx_cowextsize = old_ma.fsx_cowextsize;
> + } else {
> + fa->flags |= old_ma.flags & ~FS_COMMON_FL;
> + }
> + err = fileattr_set_prepare(inode, &old_ma, fa);
> + if (!err)
> + err = inode->i_op->fileattr_set(idmap, dentry, fa);
> + }
> + inode_unlock(inode);
> +
> + return err;
> +}
> +EXPORT_SYMBOL(vfs_fileattr_set);
> +
> +int ioctl_getflags(struct file *file, unsigned int __user *argp)
> +{
> + struct fileattr fa = { .flags_valid = true }; /* hint only */
> + int err;
> +
> + err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (!err)
> + err = put_user(fa.flags, argp);
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_getflags);
> +
> +int ioctl_setflags(struct file *file, unsigned int __user *argp)
> +{
> + struct mnt_idmap *idmap = file_mnt_idmap(file);
> + struct dentry *dentry = file->f_path.dentry;
> + struct fileattr fa;
> + unsigned int flags;
> + int err;
> +
> + err = get_user(flags, argp);
> + if (!err) {
> + err = mnt_want_write_file(file);
> + if (!err) {
> + fileattr_fill_flags(&fa, flags);
> + err = vfs_fileattr_set(idmap, dentry, &fa);
> + mnt_drop_write_file(file);
> + }
> + }
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_setflags);
> +
> +int ioctl_fsgetxattr(struct file *file, void __user *argp)
> +{
> + struct fileattr fa = { .fsx_valid = true }; /* hint only */
> + int err;
> +
> + err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (!err)
> + err = copy_fsxattr_to_user(&fa, argp);
> +
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_fsgetxattr);
> +
> +int ioctl_fssetxattr(struct file *file, void __user *argp)
> +{
> + struct mnt_idmap *idmap = file_mnt_idmap(file);
> + struct dentry *dentry = file->f_path.dentry;
> + struct fileattr fa;
> + int err;
> +
> + err = copy_fsxattr_from_user(&fa, argp);
> + if (!err) {
> + err = mnt_want_write_file(file);
> + if (!err) {
> + err = vfs_fileattr_set(idmap, dentry, &fa);
> + mnt_drop_write_file(file);
> + }
> + }
> + return err;
> +}
> +EXPORT_SYMBOL(ioctl_fssetxattr);
> diff --git a/fs/ioctl.c b/fs/ioctl.c
> index 69107a245b4c..0248cb8db2d3 100644
> --- a/fs/ioctl.c
> +++ b/fs/ioctl.c
> @@ -453,315 +453,6 @@ static int ioctl_file_dedupe_range(struct file *file,
> return ret;
> }
>
> -/**
> - * fileattr_fill_xflags - initialize fileattr with xflags
> - * @fa: fileattr pointer
> - * @xflags: FS_XFLAG_* flags
> - *
> - * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All
> - * other fields are zeroed.
> - */
> -void fileattr_fill_xflags(struct fileattr *fa, u32 xflags)
> -{
> - memset(fa, 0, sizeof(*fa));
> - fa->fsx_valid = true;
> - fa->fsx_xflags = xflags;
> - if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
> - fa->flags |= FS_IMMUTABLE_FL;
> - if (fa->fsx_xflags & FS_XFLAG_APPEND)
> - fa->flags |= FS_APPEND_FL;
> - if (fa->fsx_xflags & FS_XFLAG_SYNC)
> - fa->flags |= FS_SYNC_FL;
> - if (fa->fsx_xflags & FS_XFLAG_NOATIME)
> - fa->flags |= FS_NOATIME_FL;
> - if (fa->fsx_xflags & FS_XFLAG_NODUMP)
> - fa->flags |= FS_NODUMP_FL;
> - if (fa->fsx_xflags & FS_XFLAG_DAX)
> - fa->flags |= FS_DAX_FL;
> - if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
> - fa->flags |= FS_PROJINHERIT_FL;
> -}
> -EXPORT_SYMBOL(fileattr_fill_xflags);
> -
> -/**
> - * fileattr_fill_flags - initialize fileattr with flags
> - * @fa: fileattr pointer
> - * @flags: FS_*_FL flags
> - *
> - * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
> - * All other fields are zeroed.
> - */
> -void fileattr_fill_flags(struct fileattr *fa, u32 flags)
> -{
> - memset(fa, 0, sizeof(*fa));
> - fa->flags_valid = true;
> - fa->flags = flags;
> - if (fa->flags & FS_SYNC_FL)
> - fa->fsx_xflags |= FS_XFLAG_SYNC;
> - if (fa->flags & FS_IMMUTABLE_FL)
> - fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
> - if (fa->flags & FS_APPEND_FL)
> - fa->fsx_xflags |= FS_XFLAG_APPEND;
> - if (fa->flags & FS_NODUMP_FL)
> - fa->fsx_xflags |= FS_XFLAG_NODUMP;
> - if (fa->flags & FS_NOATIME_FL)
> - fa->fsx_xflags |= FS_XFLAG_NOATIME;
> - if (fa->flags & FS_DAX_FL)
> - fa->fsx_xflags |= FS_XFLAG_DAX;
> - if (fa->flags & FS_PROJINHERIT_FL)
> - fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
> -}
> -EXPORT_SYMBOL(fileattr_fill_flags);
> -
> -/**
> - * vfs_fileattr_get - retrieve miscellaneous file attributes
> - * @dentry: the object to retrieve from
> - * @fa: fileattr pointer
> - *
> - * Call i_op->fileattr_get() callback, if exists.
> - *
> - * Return: 0 on success, or a negative error on failure.
> - */
> -int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> -{
> - struct inode *inode = d_inode(dentry);
> -
> - if (!inode->i_op->fileattr_get)
> - return -ENOIOCTLCMD;
> -
> - return inode->i_op->fileattr_get(dentry, fa);
> -}
> -EXPORT_SYMBOL(vfs_fileattr_get);
> -
> -/**
> - * copy_fsxattr_to_user - copy fsxattr to userspace.
> - * @fa: fileattr pointer
> - * @ufa: fsxattr user pointer
> - *
> - * Return: 0 on success, or -EFAULT on failure.
> - */
> -int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> -{
> - struct fsxattr xfa;
> -
> - memset(&xfa, 0, sizeof(xfa));
> - xfa.fsx_xflags = fa->fsx_xflags;
> - xfa.fsx_extsize = fa->fsx_extsize;
> - xfa.fsx_nextents = fa->fsx_nextents;
> - xfa.fsx_projid = fa->fsx_projid;
> - xfa.fsx_cowextsize = fa->fsx_cowextsize;
> -
> - if (copy_to_user(ufa, &xfa, sizeof(xfa)))
> - return -EFAULT;
> -
> - return 0;
> -}
> -EXPORT_SYMBOL(copy_fsxattr_to_user);
> -
> -static int copy_fsxattr_from_user(struct fileattr *fa,
> - struct fsxattr __user *ufa)
> -{
> - struct fsxattr xfa;
> -
> - if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> - return -EFAULT;
> -
> - fileattr_fill_xflags(fa, xfa.fsx_xflags);
> - fa->fsx_extsize = xfa.fsx_extsize;
> - fa->fsx_nextents = xfa.fsx_nextents;
> - fa->fsx_projid = xfa.fsx_projid;
> - fa->fsx_cowextsize = xfa.fsx_cowextsize;
> -
> - return 0;
> -}
> -
> -/*
> - * Generic function to check FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS values and reject
> - * any invalid configurations.
> - *
> - * Note: must be called with inode lock held.
> - */
> -static int fileattr_set_prepare(struct inode *inode,
> - const struct fileattr *old_ma,
> - struct fileattr *fa)
> -{
> - int err;
> -
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - if ((fa->flags ^ old_ma->flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
> - !capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> -
> - err = fscrypt_prepare_setflags(inode, old_ma->flags, fa->flags);
> - if (err)
> - return err;
> -
> - /*
> - * Project Quota ID state is only allowed to change from within the init
> - * namespace. Enforce that restriction only if we are trying to change
> - * the quota ID state. Everything else is allowed in user namespaces.
> - */
> - if (current_user_ns() != &init_user_ns) {
> - if (old_ma->fsx_projid != fa->fsx_projid)
> - return -EINVAL;
> - if ((old_ma->fsx_xflags ^ fa->fsx_xflags) &
> - FS_XFLAG_PROJINHERIT)
> - return -EINVAL;
> - } else {
> - /*
> - * Caller is allowed to change the project ID. If it is being
> - * changed, make sure that the new value is valid.
> - */
> - if (old_ma->fsx_projid != fa->fsx_projid &&
> - !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid)))
> - return -EINVAL;
> - }
> -
> - /* Check extent size hints. */
> - if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode))
> - return -EINVAL;
> -
> - if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
> - !S_ISDIR(inode->i_mode))
> - return -EINVAL;
> -
> - if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
> - !S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
> - return -EINVAL;
> -
> - /*
> - * It is only valid to set the DAX flag on regular files and
> - * directories on filesystems.
> - */
> - if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
> - !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
> - return -EINVAL;
> -
> - /* Extent size hints of zero turn off the flags. */
> - if (fa->fsx_extsize == 0)
> - fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
> - if (fa->fsx_cowextsize == 0)
> - fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
> -
> - return 0;
> -}
> -
> -/**
> - * vfs_fileattr_set - change miscellaneous file attributes
> - * @idmap: idmap of the mount
> - * @dentry: the object to change
> - * @fa: fileattr pointer
> - *
> - * After verifying permissions, call i_op->fileattr_set() callback, if
> - * exists.
> - *
> - * Verifying attributes involves retrieving current attributes with
> - * i_op->fileattr_get(), this also allows initializing attributes that have
> - * not been set by the caller to current values. Inode lock is held
> - * thoughout to prevent racing with another instance.
> - *
> - * Return: 0 on success, or a negative error on failure.
> - */
> -int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> - struct fileattr *fa)
> -{
> - struct inode *inode = d_inode(dentry);
> - struct fileattr old_ma = {};
> - int err;
> -
> - if (!inode->i_op->fileattr_set)
> - return -ENOIOCTLCMD;
> -
> - if (!inode_owner_or_capable(idmap, inode))
> - return -EPERM;
> -
> - inode_lock(inode);
> - err = vfs_fileattr_get(dentry, &old_ma);
> - if (!err) {
> - /* initialize missing bits from old_ma */
> - if (fa->flags_valid) {
> - fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON;
> - fa->fsx_extsize = old_ma.fsx_extsize;
> - fa->fsx_nextents = old_ma.fsx_nextents;
> - fa->fsx_projid = old_ma.fsx_projid;
> - fa->fsx_cowextsize = old_ma.fsx_cowextsize;
> - } else {
> - fa->flags |= old_ma.flags & ~FS_COMMON_FL;
> - }
> - err = fileattr_set_prepare(inode, &old_ma, fa);
> - if (!err)
> - err = inode->i_op->fileattr_set(idmap, dentry, fa);
> - }
> - inode_unlock(inode);
> -
> - return err;
> -}
> -EXPORT_SYMBOL(vfs_fileattr_set);
> -
> -static int ioctl_getflags(struct file *file, unsigned int __user *argp)
> -{
> - struct fileattr fa = { .flags_valid = true }; /* hint only */
> - int err;
> -
> - err = vfs_fileattr_get(file->f_path.dentry, &fa);
> - if (!err)
> - err = put_user(fa.flags, argp);
> - return err;
> -}
> -
> -static int ioctl_setflags(struct file *file, unsigned int __user *argp)
> -{
> - struct mnt_idmap *idmap = file_mnt_idmap(file);
> - struct dentry *dentry = file->f_path.dentry;
> - struct fileattr fa;
> - unsigned int flags;
> - int err;
> -
> - err = get_user(flags, argp);
> - if (!err) {
> - err = mnt_want_write_file(file);
> - if (!err) {
> - fileattr_fill_flags(&fa, flags);
> - err = vfs_fileattr_set(idmap, dentry, &fa);
> - mnt_drop_write_file(file);
> - }
> - }
> - return err;
> -}
> -
> -static int ioctl_fsgetxattr(struct file *file, void __user *argp)
> -{
> - struct fileattr fa = { .fsx_valid = true }; /* hint only */
> - int err;
> -
> - err = vfs_fileattr_get(file->f_path.dentry, &fa);
> - if (!err)
> - err = copy_fsxattr_to_user(&fa, argp);
> -
> - return err;
> -}
> -
> -static int ioctl_fssetxattr(struct file *file, void __user *argp)
> -{
> - struct mnt_idmap *idmap = file_mnt_idmap(file);
> - struct dentry *dentry = file->f_path.dentry;
> - struct fileattr fa;
> - int err;
> -
> - err = copy_fsxattr_from_user(&fa, argp);
> - if (!err) {
> - err = mnt_want_write_file(file);
> - if (!err) {
> - err = vfs_fileattr_set(idmap, dentry, &fa);
> - mnt_drop_write_file(file);
> - }
> - }
> - return err;
> -}
> -
> static int ioctl_getfsuuid(struct file *file, void __user *argp)
> {
> struct super_block *sb = file_inode(file)->i_sb;
> diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h
> index 47c05a9851d0..6030d0bf7ad3 100644
> --- a/include/linux/fileattr.h
> +++ b/include/linux/fileattr.h
> @@ -55,5 +55,9 @@ static inline bool fileattr_has_fsx(const struct fileattr *fa)
> int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
> int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> struct fileattr *fa);
> +int ioctl_getflags(struct file *file, unsigned int __user *argp);
> +int ioctl_setflags(struct file *file, unsigned int __user *argp);
> +int ioctl_fsgetxattr(struct file *file, void __user *argp);
> +int ioctl_fssetxattr(struct file *file, void __user *argp);
>
> #endif /* _LINUX_FILEATTR_H */
>
> --
> 2.47.2
>
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 2/6] lsm: introduce new hooks for setting/getting inode fsxattr
2025-06-30 16:20 ` [PATCH v6 2/6] lsm: introduce new hooks for setting/getting inode fsxattr Andrey Albershteyn
2025-07-01 12:39 ` Jan Kara
@ 2025-07-01 18:18 ` Darrick J. Wong
2025-07-02 8:47 ` Andrey Albershteyn
1 sibling, 1 reply; 47+ messages in thread
From: Darrick J. Wong @ 2025-07-01 18:18 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon, Jun 30, 2025 at 06:20:12PM +0200, Andrey Albershteyn wrote:
> Introduce new hooks for setting and getting filesystem extended
> attributes on inode (FS_IOC_FSGETXATTR).
>
> Cc: selinux@vger.kernel.org
> Cc: Paul Moore <paul@paul-moore.com>
>
> Acked-by: Paul Moore <paul@paul-moore.com>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
I wonder, were FS_IOC_FS[GS]ETXATTR already covered by the
security_file_ioctl hook? If so, will an out of date security policy
on a 6.17 kernel now fail to check the new file_[gs]etattr syscalls?
Though AFAICT the future of managing these "extra" file attributes is
the system call so it's probably appropriate to have an explicit
callout to LSMs.
Acked-by: "Darrick J. Wong" <djwong@kernel.org>
--D
> ---
> fs/file_attr.c | 19 ++++++++++++++++---
> include/linux/lsm_hook_defs.h | 2 ++
> include/linux/security.h | 16 ++++++++++++++++
> security/security.c | 30 ++++++++++++++++++++++++++++++
> 4 files changed, 64 insertions(+), 3 deletions(-)
>
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> index 2910b7047721..be62d97cc444 100644
> --- a/fs/file_attr.c
> +++ b/fs/file_attr.c
> @@ -76,10 +76,15 @@ EXPORT_SYMBOL(fileattr_fill_flags);
> int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> {
> struct inode *inode = d_inode(dentry);
> + int error;
>
> if (!inode->i_op->fileattr_get)
> return -ENOIOCTLCMD;
>
> + error = security_inode_file_getattr(dentry, fa);
> + if (error)
> + return error;
> +
> return inode->i_op->fileattr_get(dentry, fa);
> }
> EXPORT_SYMBOL(vfs_fileattr_get);
> @@ -242,12 +247,20 @@ int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> } else {
> fa->flags |= old_ma.flags & ~FS_COMMON_FL;
> }
> +
> err = fileattr_set_prepare(inode, &old_ma, fa);
> - if (!err)
> - err = inode->i_op->fileattr_set(idmap, dentry, fa);
> + if (err)
> + goto out;
> + err = security_inode_file_setattr(dentry, fa);
> + if (err)
> + goto out;
> + err = inode->i_op->fileattr_set(idmap, dentry, fa);
> + if (err)
> + goto out;
> }
> +
> +out:
> inode_unlock(inode);
> -
> return err;
> }
> EXPORT_SYMBOL(vfs_fileattr_set);
> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> index bf3bbac4e02a..9600a4350e79 100644
> --- a/include/linux/lsm_hook_defs.h
> +++ b/include/linux/lsm_hook_defs.h
> @@ -157,6 +157,8 @@ LSM_HOOK(int, 0, inode_removexattr, struct mnt_idmap *idmap,
> struct dentry *dentry, const char *name)
> LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
> const char *name)
> +LSM_HOOK(int, 0, inode_file_setattr, struct dentry *dentry, struct fileattr *fa)
> +LSM_HOOK(int, 0, inode_file_getattr, struct dentry *dentry, struct fileattr *fa)
> LSM_HOOK(int, 0, inode_set_acl, struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)
> LSM_HOOK(void, LSM_RET_VOID, inode_post_set_acl, struct dentry *dentry,
> diff --git a/include/linux/security.h b/include/linux/security.h
> index dba349629229..9ed0d0e0c81f 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -451,6 +451,10 @@ int security_inode_listxattr(struct dentry *dentry);
> int security_inode_removexattr(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *name);
> void security_inode_post_removexattr(struct dentry *dentry, const char *name);
> +int security_inode_file_setattr(struct dentry *dentry,
> + struct fileattr *fa);
> +int security_inode_file_getattr(struct dentry *dentry,
> + struct fileattr *fa);
> int security_inode_need_killpriv(struct dentry *dentry);
> int security_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry);
> int security_inode_getsecurity(struct mnt_idmap *idmap,
> @@ -1052,6 +1056,18 @@ static inline void security_inode_post_removexattr(struct dentry *dentry,
> const char *name)
> { }
>
> +static inline int security_inode_file_setattr(struct dentry *dentry,
> + struct fileattr *fa)
> +{
> + return 0;
> +}
> +
> +static inline int security_inode_file_getattr(struct dentry *dentry,
> + struct fileattr *fa)
> +{
> + return 0;
> +}
> +
> static inline int security_inode_need_killpriv(struct dentry *dentry)
> {
> return cap_inode_need_killpriv(dentry);
> diff --git a/security/security.c b/security/security.c
> index 596d41818577..711b4de40b8d 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -2622,6 +2622,36 @@ void security_inode_post_removexattr(struct dentry *dentry, const char *name)
> call_void_hook(inode_post_removexattr, dentry, name);
> }
>
> +/**
> + * security_inode_file_setattr() - check if setting fsxattr is allowed
> + * @dentry: file to set filesystem extended attributes on
> + * @fa: extended attributes to set on the inode
> + *
> + * Called when file_setattr() syscall or FS_IOC_FSSETXATTR ioctl() is called on
> + * inode
> + *
> + * Return: Returns 0 if permission is granted.
> + */
> +int security_inode_file_setattr(struct dentry *dentry, struct fileattr *fa)
> +{
> + return call_int_hook(inode_file_setattr, dentry, fa);
> +}
> +
> +/**
> + * security_inode_file_getattr() - check if retrieving fsxattr is allowed
> + * @dentry: file to retrieve filesystem extended attributes from
> + * @fa: extended attributes to get
> + *
> + * Called when file_getattr() syscall or FS_IOC_FSGETXATTR ioctl() is called on
> + * inode
> + *
> + * Return: Returns 0 if permission is granted.
> + */
> +int security_inode_file_getattr(struct dentry *dentry, struct fileattr *fa)
> +{
> + return call_int_hook(inode_file_getattr, dentry, fa);
> +}
> +
> /**
> * security_inode_need_killpriv() - Check if security_inode_killpriv() required
> * @dentry: associated dentry
>
> --
> 2.47.2
>
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP
2025-06-30 16:20 ` [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP Andrey Albershteyn
` (2 preceding siblings ...)
2025-07-01 12:52 ` Jan Kara
@ 2025-07-01 18:18 ` Darrick J. Wong
3 siblings, 0 replies; 47+ messages in thread
From: Darrick J. Wong @ 2025-07-01 18:18 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon, Jun 30, 2025 at 06:20:14PM +0200, Andrey Albershteyn wrote:
> Future patches will add new syscalls which use these functions. As
> this interface won't be used for ioctls only, the EOPNOSUPP is more
> appropriate return code.
>
> This patch converts return code from ENOIOCTLCMD to EOPNOSUPP for
> vfs_fileattr_get and vfs_fileattr_set. To save old behavior translate
> EOPNOSUPP back for current users - overlayfs, encryptfs and fs/ioctl.c.
>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
With EOPNOSUPP -> EOPNOTSUPP corrected,
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
--D
> ---
> fs/ecryptfs/inode.c | 8 +++++++-
> fs/file_attr.c | 12 ++++++++++--
> fs/overlayfs/inode.c | 2 +-
> 3 files changed, 18 insertions(+), 4 deletions(-)
>
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 493d7f194956..a55c1375127f 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -1126,7 +1126,13 @@ static int ecryptfs_removexattr(struct dentry *dentry, struct inode *inode,
>
> static int ecryptfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> {
> - return vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
> + int rc;
> +
> + rc = vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
> + if (rc == -EOPNOTSUPP)
> + rc = -ENOIOCTLCMD;
> +
> + return rc;
> }
>
> static int ecryptfs_fileattr_set(struct mnt_idmap *idmap,
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> index be62d97cc444..4e85fa00c092 100644
> --- a/fs/file_attr.c
> +++ b/fs/file_attr.c
> @@ -79,7 +79,7 @@ int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> int error;
>
> if (!inode->i_op->fileattr_get)
> - return -ENOIOCTLCMD;
> + return -EOPNOTSUPP;
>
> error = security_inode_file_getattr(dentry, fa);
> if (error)
> @@ -229,7 +229,7 @@ int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> int err;
>
> if (!inode->i_op->fileattr_set)
> - return -ENOIOCTLCMD;
> + return -EOPNOTSUPP;
>
> if (!inode_owner_or_capable(idmap, inode))
> return -EPERM;
> @@ -271,6 +271,8 @@ int ioctl_getflags(struct file *file, unsigned int __user *argp)
> int err;
>
> err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> if (!err)
> err = put_user(fa.flags, argp);
> return err;
> @@ -292,6 +294,8 @@ int ioctl_setflags(struct file *file, unsigned int __user *argp)
> fileattr_fill_flags(&fa, flags);
> err = vfs_fileattr_set(idmap, dentry, &fa);
> mnt_drop_write_file(file);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> }
> }
> return err;
> @@ -304,6 +308,8 @@ int ioctl_fsgetxattr(struct file *file, void __user *argp)
> int err;
>
> err = vfs_fileattr_get(file->f_path.dentry, &fa);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> if (!err)
> err = copy_fsxattr_to_user(&fa, argp);
>
> @@ -324,6 +330,8 @@ int ioctl_fssetxattr(struct file *file, void __user *argp)
> if (!err) {
> err = vfs_fileattr_set(idmap, dentry, &fa);
> mnt_drop_write_file(file);
> + if (err == -EOPNOTSUPP)
> + err = -ENOIOCTLCMD;
> }
> }
> return err;
> diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
> index 6f0e15f86c21..096d44712bb1 100644
> --- a/fs/overlayfs/inode.c
> +++ b/fs/overlayfs/inode.c
> @@ -721,7 +721,7 @@ int ovl_real_fileattr_get(const struct path *realpath, struct fileattr *fa)
> return err;
>
> err = vfs_fileattr_get(realpath->dentry, fa);
> - if (err == -ENOIOCTLCMD)
> + if (err == -EOPNOTSUPP)
> err = -ENOTTY;
> return err;
> }
>
> --
> 2.47.2
>
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 5/6] fs: prepare for extending file_get/setattr()
2025-06-30 16:20 ` [PATCH v6 5/6] fs: prepare for extending file_get/setattr() Andrey Albershteyn
2025-07-01 13:06 ` Jan Kara
@ 2025-07-01 18:31 ` Darrick J. Wong
2025-07-01 19:27 ` Amir Goldstein
1 sibling, 1 reply; 47+ messages in thread
From: Darrick J. Wong @ 2025-07-01 18:31 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon, Jun 30, 2025 at 06:20:15PM +0200, Andrey Albershteyn wrote:
> From: Amir Goldstein <amir73il@gmail.com>
>
> We intend to add support for more xflags to selective filesystems and
> We cannot rely on copy_struct_from_user() to detect this extension.
>
> In preparation of extending the API, do not allow setting xflags unknown
> by this kernel version.
>
> Also do not pass the read-only flags and read-only field fsx_nextents to
> filesystem.
>
> These changes should not affect existing chattr programs that use the
> ioctl to get fsxattr before setting the new values.
>
> Link: https://lore.kernel.org/linux-fsdevel/20250216164029.20673-4-pali@kernel.org/
> Cc: Pali Rohár <pali@kernel.org>
> Cc: Andrey Albershteyn <aalbersh@redhat.com>
> Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> ---
> fs/file_attr.c | 8 +++++++-
> include/linux/fileattr.h | 20 ++++++++++++++++++++
> 2 files changed, 27 insertions(+), 1 deletion(-)
>
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> index 4e85fa00c092..62f08872d4ad 100644
> --- a/fs/file_attr.c
> +++ b/fs/file_attr.c
> @@ -99,9 +99,10 @@ EXPORT_SYMBOL(vfs_fileattr_get);
> int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> {
> struct fsxattr xfa;
> + __u32 mask = FS_XFLAGS_MASK;
>
> memset(&xfa, 0, sizeof(xfa));
> - xfa.fsx_xflags = fa->fsx_xflags;
> + xfa.fsx_xflags = fa->fsx_xflags & mask;
I wonder, should it be an error if a filesystem sets an fsx_xflags bit
outside of FS_XFLAGS_MASK? I guess that's one way to prevent
filesystems from overriding the VFS bits. ;)
Though couldn't that be:
xfa.fsx_xflags = fa->fsx_xflags & FS_XFLAGS_MASK;
instead? And same below?
> xfa.fsx_extsize = fa->fsx_extsize;
> xfa.fsx_nextents = fa->fsx_nextents;
> xfa.fsx_projid = fa->fsx_projid;
> @@ -118,11 +119,16 @@ static int copy_fsxattr_from_user(struct fileattr *fa,
> struct fsxattr __user *ufa)
> {
> struct fsxattr xfa;
> + __u32 mask = FS_XFLAGS_MASK;
>
> if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> return -EFAULT;
>
> + if (xfa.fsx_xflags & ~mask)
> + return -EINVAL;
I wonder if you want EOPNOTSUPP here? We don't know how to support
unknown xflags. OTOH if you all have beaten this to death while I was
out then don't start another round just for me. :P
--D
> +
> fileattr_fill_xflags(fa, xfa.fsx_xflags);
> + fa->fsx_xflags &= ~FS_XFLAG_RDONLY_MASK;
> fa->fsx_extsize = xfa.fsx_extsize;
> fa->fsx_nextents = xfa.fsx_nextents;
> fa->fsx_projid = xfa.fsx_projid;
> diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h
> index 6030d0bf7ad3..e2a2f4ae242d 100644
> --- a/include/linux/fileattr.h
> +++ b/include/linux/fileattr.h
> @@ -14,6 +14,26 @@
> FS_XFLAG_NODUMP | FS_XFLAG_NOATIME | FS_XFLAG_DAX | \
> FS_XFLAG_PROJINHERIT)
>
> +/* Read-only inode flags */
> +#define FS_XFLAG_RDONLY_MASK \
> + (FS_XFLAG_PREALLOC | FS_XFLAG_HASATTR)
> +
> +/* Flags to indicate valid value of fsx_ fields */
> +#define FS_XFLAG_VALUES_MASK \
> + (FS_XFLAG_EXTSIZE | FS_XFLAG_COWEXTSIZE)
> +
> +/* Flags for directories */
> +#define FS_XFLAG_DIRONLY_MASK \
> + (FS_XFLAG_RTINHERIT | FS_XFLAG_NOSYMLINKS | FS_XFLAG_EXTSZINHERIT)
> +
> +/* Misc settable flags */
> +#define FS_XFLAG_MISC_MASK \
> + (FS_XFLAG_REALTIME | FS_XFLAG_NODEFRAG | FS_XFLAG_FILESTREAM)
> +
> +#define FS_XFLAGS_MASK \
> + (FS_XFLAG_COMMON | FS_XFLAG_RDONLY_MASK | FS_XFLAG_VALUES_MASK | \
> + FS_XFLAG_DIRONLY_MASK | FS_XFLAG_MISC_MASK)
> +
> /*
> * Merged interface for miscellaneous file attributes. 'flags' originates from
> * ext* and 'fsx_flags' from xfs. There's some overlap between the two, which
>
> --
> 2.47.2
>
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-06-30 16:20 ` [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
2025-07-01 12:34 ` Christian Brauner
2025-07-01 13:24 ` Jan Kara
@ 2025-07-01 18:43 ` Darrick J. Wong
2025-07-01 18:54 ` Pali Rohár
2025-07-02 12:40 ` Christian Brauner
2 siblings, 2 replies; 47+ messages in thread
From: Darrick J. Wong @ 2025-07-01 18:43 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon, Jun 30, 2025 at 06:20:16PM +0200, Andrey Albershteyn wrote:
> From: Andrey Albershteyn <aalbersh@redhat.com>
>
> 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 fsx_fileattr 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>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> ---
<snip syscall table>
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> index 62f08872d4ad..fda9d847eee5 100644
> --- a/fs/file_attr.c
> +++ b/fs/file_attr.c
> @@ -3,6 +3,10 @@
> #include <linux/security.h>
> #include <linux/fscrypt.h>
> #include <linux/fileattr.h>
> +#include <linux/syscalls.h>
> +#include <linux/namei.h>
> +
> +#include "internal.h"
>
> /**
> * fileattr_fill_xflags - initialize fileattr with xflags
> @@ -89,6 +93,19 @@ int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> }
> EXPORT_SYMBOL(vfs_fileattr_get);
>
> +static void fileattr_to_fsx_fileattr(const struct fileattr *fa,
> + struct fsx_fileattr *fsx)
Er... "fsx_fileattr" is the struct that the system call uses?
That's a little confusing considering that xfs already has a
xfs_fill_fsxattr function that actually fills a struct fileattr.
That could be renamed xfs_fill_fileattr.
I dunno. There's a part of me that would really rather that the
file_getattr and file_setattr syscalls operate on a struct file_attr.
More whining/bikeshedding to come.
<snip stuff that looks ok to me>
<<well, I still dislike the CLASS(fd, fd)(fd) syntax...>>
> diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
> index 0098b0ce8ccb..0784f2033ba4 100644
> --- a/include/uapi/linux/fs.h
> +++ b/include/uapi/linux/fs.h
> @@ -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 fsx_fileattr {
> + __u32 fsx_xflags; /* xflags field value (get/set) */
Should this to be __u64 from the start? Seeing as (a) this struct is
not already a multiple of 8 bytes and (b) it's likely that we'll have to
add a u64 field at some point. That would also address brauner's
comment about padding.
--D
> + __u32 fsx_extsize; /* extsize field value (get/set)*/
> + __u32 fsx_nextents; /* nextents field value (get) */
> + __u32 fsx_projid; /* project identifier (get/set) */
> + __u32 fsx_cowextsize; /* CoW extsize field value (get/set) */
> +};
> +
> +#define FSX_FILEATTR_SIZE_VER0 20
> +#define FSX_FILEATTR_SIZE_LATEST FSX_FILEATTR_SIZE_VER0
> +
> /*
> * Flags for the fsx_xflags field
> */
> diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
> index 580b4e246aec..d1ae5e92c615 100644
> --- a/scripts/syscall.tbl
> +++ b/scripts/syscall.tbl
> @@ -408,3 +408,5 @@
> 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
>
> --
> 2.47.2
>
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-01 18:43 ` Darrick J. Wong
@ 2025-07-01 18:54 ` Pali Rohár
2025-07-01 19:08 ` Darrick J. Wong
2025-07-02 12:40 ` Christian Brauner
1 sibling, 1 reply; 47+ messages in thread
From: Pali Rohár @ 2025-07-01 18:54 UTC (permalink / raw)
To: Darrick J. Wong
Cc: Andrey Albershteyn, Amir Goldstein, Arnd Bergmann,
Casey Schaufler, Christian Brauner, Jan Kara, Paul Moore,
linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Tuesday 01 July 2025 11:43:17 Darrick J. Wong wrote:
> On Mon, Jun 30, 2025 at 06:20:16PM +0200, Andrey Albershteyn wrote:
> > diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
> > index 0098b0ce8ccb..0784f2033ba4 100644
> > --- a/include/uapi/linux/fs.h
> > +++ b/include/uapi/linux/fs.h
> > @@ -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 fsx_fileattr {
> > + __u32 fsx_xflags; /* xflags field value (get/set) */
>
> Should this to be __u64 from the start? Seeing as (a) this struct is
> not already a multiple of 8 bytes and (b) it's likely that we'll have to
> add a u64 field at some point. That would also address brauner's
> comment about padding.
Hello!
As I have already mentioned, after this syscall API/ABI is finished, I'm
planning to prepare patches for changing just selected fields / flags by
introducing a new mask field, and support for additional flags used by
existing filesystems (like windows flags).
My idea is extending this structure for a new "u32 fsx_xflags_mask"
and new "u32 fsx_xflags2" + "u32 fsx_xflags2_mask". (field names are
just examples).
So in case you are extending the structure now, please consider if it
makes sense to add all members, so we do not have to define 2 or 3
structure versions in near feature.
Your idea of __u64 for fsx_xflags means that it will already cover the
"u32 fsx_xflags2" field.
> --D
>
> > + __u32 fsx_extsize; /* extsize field value (get/set)*/
> > + __u32 fsx_nextents; /* nextents field value (get) */
> > + __u32 fsx_projid; /* project identifier (get/set) */
> > + __u32 fsx_cowextsize; /* CoW extsize field value (get/set) */
> > +};
> > +
> > +#define FSX_FILEATTR_SIZE_VER0 20
> > +#define FSX_FILEATTR_SIZE_LATEST FSX_FILEATTR_SIZE_VER0
> > +
> > /*
> > * Flags for the fsx_xflags field
> > */
> > diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
> > index 580b4e246aec..d1ae5e92c615 100644
> > --- a/scripts/syscall.tbl
> > +++ b/scripts/syscall.tbl
> > @@ -408,3 +408,5 @@
> > 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
> >
> > --
> > 2.47.2
> >
> >
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-01 18:54 ` Pali Rohár
@ 2025-07-01 19:08 ` Darrick J. Wong
2025-07-01 19:17 ` Pali Rohár
0 siblings, 1 reply; 47+ messages in thread
From: Darrick J. Wong @ 2025-07-01 19:08 UTC (permalink / raw)
To: Pali Rohár
Cc: Andrey Albershteyn, Amir Goldstein, Arnd Bergmann,
Casey Schaufler, Christian Brauner, Jan Kara, Paul Moore,
linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Tue, Jul 01, 2025 at 08:54:57PM +0200, Pali Rohár wrote:
> On Tuesday 01 July 2025 11:43:17 Darrick J. Wong wrote:
> > On Mon, Jun 30, 2025 at 06:20:16PM +0200, Andrey Albershteyn wrote:
> > > diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
> > > index 0098b0ce8ccb..0784f2033ba4 100644
> > > --- a/include/uapi/linux/fs.h
> > > +++ b/include/uapi/linux/fs.h
> > > @@ -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 fsx_fileattr {
> > > + __u32 fsx_xflags; /* xflags field value (get/set) */
> >
> > Should this to be __u64 from the start? Seeing as (a) this struct is
> > not already a multiple of 8 bytes and (b) it's likely that we'll have to
> > add a u64 field at some point. That would also address brauner's
> > comment about padding.
>
> Hello!
>
> As I have already mentioned, after this syscall API/ABI is finished, I'm
> planning to prepare patches for changing just selected fields / flags by
> introducing a new mask field, and support for additional flags used by
> existing filesystems (like windows flags).
>
> My idea is extending this structure for a new "u32 fsx_xflags_mask"
> and new "u32 fsx_xflags2" + "u32 fsx_xflags2_mask". (field names are
> just examples).
>
> So in case you are extending the structure now, please consider if it
> makes sense to add all members, so we do not have to define 2 or 3
> structure versions in near feature.
>
> Your idea of __u64 for fsx_xflags means that it will already cover the
> "u32 fsx_xflags2" field.
Ah, ok, so that work *is* still coming. :)
Are you still planning to add masks for xflags bits that are clearable
and settable? i.e.
__u64 fa_xflags; /* state */
...
<end of V0 structure>
__u64 fa_xflags_mask; /* bits for setattr to examine */
__u64 fa_xflags_clearable; /* clearable bits */
__u64 fa_xflags_settable; /* settable bits */
I think it's easier just to define u64 in the V0 structure and then add
the three new fields in V1. What do you think?
--D
> > --D
> >
> > > + __u32 fsx_extsize; /* extsize field value (get/set)*/
> > > + __u32 fsx_nextents; /* nextents field value (get) */
> > > + __u32 fsx_projid; /* project identifier (get/set) */
> > > + __u32 fsx_cowextsize; /* CoW extsize field value (get/set) */
> > > +};
> > > +
> > > +#define FSX_FILEATTR_SIZE_VER0 20
> > > +#define FSX_FILEATTR_SIZE_LATEST FSX_FILEATTR_SIZE_VER0
> > > +
> > > /*
> > > * Flags for the fsx_xflags field
> > > */
> > > diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
> > > index 580b4e246aec..d1ae5e92c615 100644
> > > --- a/scripts/syscall.tbl
> > > +++ b/scripts/syscall.tbl
> > > @@ -408,3 +408,5 @@
> > > 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
> > >
> > > --
> > > 2.47.2
> > >
> > >
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-01 19:08 ` Darrick J. Wong
@ 2025-07-01 19:17 ` Pali Rohár
0 siblings, 0 replies; 47+ messages in thread
From: Pali Rohár @ 2025-07-01 19:17 UTC (permalink / raw)
To: Darrick J. Wong
Cc: Andrey Albershteyn, Amir Goldstein, Arnd Bergmann,
Casey Schaufler, Christian Brauner, Jan Kara, Paul Moore,
linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Tuesday 01 July 2025 12:08:57 Darrick J. Wong wrote:
> On Tue, Jul 01, 2025 at 08:54:57PM +0200, Pali Rohár wrote:
> > On Tuesday 01 July 2025 11:43:17 Darrick J. Wong wrote:
> > > On Mon, Jun 30, 2025 at 06:20:16PM +0200, Andrey Albershteyn wrote:
> > > > diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
> > > > index 0098b0ce8ccb..0784f2033ba4 100644
> > > > --- a/include/uapi/linux/fs.h
> > > > +++ b/include/uapi/linux/fs.h
> > > > @@ -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 fsx_fileattr {
> > > > + __u32 fsx_xflags; /* xflags field value (get/set) */
> > >
> > > Should this to be __u64 from the start? Seeing as (a) this struct is
> > > not already a multiple of 8 bytes and (b) it's likely that we'll have to
> > > add a u64 field at some point. That would also address brauner's
> > > comment about padding.
> >
> > Hello!
> >
> > As I have already mentioned, after this syscall API/ABI is finished, I'm
> > planning to prepare patches for changing just selected fields / flags by
> > introducing a new mask field, and support for additional flags used by
> > existing filesystems (like windows flags).
> >
> > My idea is extending this structure for a new "u32 fsx_xflags_mask"
> > and new "u32 fsx_xflags2" + "u32 fsx_xflags2_mask". (field names are
> > just examples).
> >
> > So in case you are extending the structure now, please consider if it
> > makes sense to add all members, so we do not have to define 2 or 3
> > structure versions in near feature.
> >
> > Your idea of __u64 for fsx_xflags means that it will already cover the
> > "u32 fsx_xflags2" field.
>
> Ah, ok, so that work *is* still coming. :)
Yes. I'm just waiting until this patch series is accepted.
In past I have already sent RFC patches to the list which modifies the
existing ioctl interface. So you can look at it if you want :-)
> Are you still planning to add masks for xflags bits that are clearable
> and settable? i.e.
>
> __u64 fa_xflags; /* state */
> ...
> <end of V0 structure>
>
> __u64 fa_xflags_mask; /* bits for setattr to examine */
> __u64 fa_xflags_clearable; /* clearable bits */
> __u64 fa_xflags_settable; /* settable bits */
>
> I think it's easier just to define u64 in the V0 structure and then add
> the three new fields in V1. What do you think?
I wanted the interface which would allow to atomically change specified
bit/flag without the need for get-modify-set. And I think that this
would not work as the fa_xflags requires the state.
My idea is following:
__u64 fa_xflags;
...
<end of V0 structure>
__u64 fa_xflags_mask;
The fa_xflags_mask will specify which bits from the fa_xflags and from
other fa_* fields in V0 struct are going to be changed.
> --D
>
> > > --D
> > >
> > > > + __u32 fsx_extsize; /* extsize field value (get/set)*/
> > > > + __u32 fsx_nextents; /* nextents field value (get) */
> > > > + __u32 fsx_projid; /* project identifier (get/set) */
> > > > + __u32 fsx_cowextsize; /* CoW extsize field value (get/set) */
> > > > +};
> > > > +
> > > > +#define FSX_FILEATTR_SIZE_VER0 20
> > > > +#define FSX_FILEATTR_SIZE_LATEST FSX_FILEATTR_SIZE_VER0
> > > > +
> > > > /*
> > > > * Flags for the fsx_xflags field
> > > > */
> > > > diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
> > > > index 580b4e246aec..d1ae5e92c615 100644
> > > > --- a/scripts/syscall.tbl
> > > > +++ b/scripts/syscall.tbl
> > > > @@ -408,3 +408,5 @@
> > > > 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
> > > >
> > > > --
> > > > 2.47.2
> > > >
> > > >
> >
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 5/6] fs: prepare for extending file_get/setattr()
2025-07-01 18:31 ` Darrick J. Wong
@ 2025-07-01 19:27 ` Amir Goldstein
2025-07-01 19:40 ` Darrick J. Wong
0 siblings, 1 reply; 47+ messages in thread
From: Amir Goldstein @ 2025-07-01 19:27 UTC (permalink / raw)
To: Darrick J. Wong
Cc: Andrey Albershteyn, Arnd Bergmann, Casey Schaufler,
Christian Brauner, Jan Kara, Pali Rohár, Paul Moore,
linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Tue, Jul 1, 2025 at 8:31 PM Darrick J. Wong <djwong@kernel.org> wrote:
>
> On Mon, Jun 30, 2025 at 06:20:15PM +0200, Andrey Albershteyn wrote:
> > From: Amir Goldstein <amir73il@gmail.com>
> >
> > We intend to add support for more xflags to selective filesystems and
> > We cannot rely on copy_struct_from_user() to detect this extension.
> >
> > In preparation of extending the API, do not allow setting xflags unknown
> > by this kernel version.
> >
> > Also do not pass the read-only flags and read-only field fsx_nextents to
> > filesystem.
> >
> > These changes should not affect existing chattr programs that use the
> > ioctl to get fsxattr before setting the new values.
> >
> > Link: https://lore.kernel.org/linux-fsdevel/20250216164029.20673-4-pali@kernel.org/
> > Cc: Pali Rohár <pali@kernel.org>
> > Cc: Andrey Albershteyn <aalbersh@redhat.com>
> > Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> > ---
> > fs/file_attr.c | 8 +++++++-
> > include/linux/fileattr.h | 20 ++++++++++++++++++++
> > 2 files changed, 27 insertions(+), 1 deletion(-)
> >
> > diff --git a/fs/file_attr.c b/fs/file_attr.c
> > index 4e85fa00c092..62f08872d4ad 100644
> > --- a/fs/file_attr.c
> > +++ b/fs/file_attr.c
> > @@ -99,9 +99,10 @@ EXPORT_SYMBOL(vfs_fileattr_get);
> > int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> > {
> > struct fsxattr xfa;
> > + __u32 mask = FS_XFLAGS_MASK;
> >
> > memset(&xfa, 0, sizeof(xfa));
> > - xfa.fsx_xflags = fa->fsx_xflags;
> > + xfa.fsx_xflags = fa->fsx_xflags & mask;
>
> I wonder, should it be an error if a filesystem sets an fsx_xflags bit
> outside of FS_XFLAGS_MASK? I guess that's one way to prevent
> filesystems from overriding the VFS bits. ;)
I think Pali has a plan on how to ensure that later
when the mask is provided via the API.
>
> Though couldn't that be:
>
> xfa.fsx_xflags = fa->fsx_xflags & FS_XFLAGS_MASK;
>
> instead? And same below?
>
Indeed. There is a reason for the var, because the next series
by Pali will use a user provided mask, which defaults to FS_XFLAGS_MASK,
so I left it this way.
I don't see a problem with it keeping as is, but if it bothers you
I guess we can re-add the var later.
> > xfa.fsx_extsize = fa->fsx_extsize;
> > xfa.fsx_nextents = fa->fsx_nextents;
> > xfa.fsx_projid = fa->fsx_projid;
> > @@ -118,11 +119,16 @@ static int copy_fsxattr_from_user(struct fileattr *fa,
> > struct fsxattr __user *ufa)
> > {
> > struct fsxattr xfa;
> > + __u32 mask = FS_XFLAGS_MASK;
> >
> > if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> > return -EFAULT;
> >
> > + if (xfa.fsx_xflags & ~mask)
> > + return -EINVAL;
>
> I wonder if you want EOPNOTSUPP here? We don't know how to support
> unknown xflags. OTOH if you all have beaten this to death while I was
> out then don't start another round just for me. :P
We have beaten this API almost to death for sure ;)
I don't remember if we discussed this specific aspect,
but I am personally in favor of
EOPNOTSUPP := the fs does not support the set/get operation
EINVAL := some flags provided as value is invalid
For example, if the get API provides you with a mask of the
valid flags that you can set, if you try to set flags outside of
that mask you get EINVAL.
That's my interpretation, but I agree that EOPNOTSUPP can also
make sense in this situation.
Thanks,
Amir.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 5/6] fs: prepare for extending file_get/setattr()
2025-07-01 19:27 ` Amir Goldstein
@ 2025-07-01 19:40 ` Darrick J. Wong
2025-07-01 19:54 ` Pali Rohár
0 siblings, 1 reply; 47+ messages in thread
From: Darrick J. Wong @ 2025-07-01 19:40 UTC (permalink / raw)
To: Amir Goldstein
Cc: Andrey Albershteyn, Arnd Bergmann, Casey Schaufler,
Christian Brauner, Jan Kara, Pali Rohár, Paul Moore,
linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Tue, Jul 01, 2025 at 09:27:38PM +0200, Amir Goldstein wrote:
> On Tue, Jul 1, 2025 at 8:31 PM Darrick J. Wong <djwong@kernel.org> wrote:
> >
> > On Mon, Jun 30, 2025 at 06:20:15PM +0200, Andrey Albershteyn wrote:
> > > From: Amir Goldstein <amir73il@gmail.com>
> > >
> > > We intend to add support for more xflags to selective filesystems and
> > > We cannot rely on copy_struct_from_user() to detect this extension.
> > >
> > > In preparation of extending the API, do not allow setting xflags unknown
> > > by this kernel version.
> > >
> > > Also do not pass the read-only flags and read-only field fsx_nextents to
> > > filesystem.
> > >
> > > These changes should not affect existing chattr programs that use the
> > > ioctl to get fsxattr before setting the new values.
> > >
> > > Link: https://lore.kernel.org/linux-fsdevel/20250216164029.20673-4-pali@kernel.org/
> > > Cc: Pali Rohár <pali@kernel.org>
> > > Cc: Andrey Albershteyn <aalbersh@redhat.com>
> > > Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> > > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> > > ---
> > > fs/file_attr.c | 8 +++++++-
> > > include/linux/fileattr.h | 20 ++++++++++++++++++++
> > > 2 files changed, 27 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/fs/file_attr.c b/fs/file_attr.c
> > > index 4e85fa00c092..62f08872d4ad 100644
> > > --- a/fs/file_attr.c
> > > +++ b/fs/file_attr.c
> > > @@ -99,9 +99,10 @@ EXPORT_SYMBOL(vfs_fileattr_get);
> > > int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> > > {
> > > struct fsxattr xfa;
> > > + __u32 mask = FS_XFLAGS_MASK;
> > >
> > > memset(&xfa, 0, sizeof(xfa));
> > > - xfa.fsx_xflags = fa->fsx_xflags;
> > > + xfa.fsx_xflags = fa->fsx_xflags & mask;
> >
> > I wonder, should it be an error if a filesystem sets an fsx_xflags bit
> > outside of FS_XFLAGS_MASK? I guess that's one way to prevent
> > filesystems from overriding the VFS bits. ;)
>
> I think Pali has a plan on how to ensure that later
> when the mask is provided via the API.
>
> >
> > Though couldn't that be:
> >
> > xfa.fsx_xflags = fa->fsx_xflags & FS_XFLAGS_MASK;
> >
> > instead? And same below?
> >
>
> Indeed. There is a reason for the var, because the next series
> by Pali will use a user provided mask, which defaults to FS_XFLAGS_MASK,
> so I left it this way.
>
> I don't see a problem with it keeping as is, but if it bothers you
> I guess we can re-add the var later.
Nah, it doesn't bother me that much.
> > > xfa.fsx_extsize = fa->fsx_extsize;
> > > xfa.fsx_nextents = fa->fsx_nextents;
> > > xfa.fsx_projid = fa->fsx_projid;
> > > @@ -118,11 +119,16 @@ static int copy_fsxattr_from_user(struct fileattr *fa,
> > > struct fsxattr __user *ufa)
> > > {
> > > struct fsxattr xfa;
> > > + __u32 mask = FS_XFLAGS_MASK;
> > >
> > > if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> > > return -EFAULT;
> > >
> > > + if (xfa.fsx_xflags & ~mask)
> > > + return -EINVAL;
> >
> > I wonder if you want EOPNOTSUPP here? We don't know how to support
> > unknown xflags. OTOH if you all have beaten this to death while I was
> > out then don't start another round just for me. :P
>
> We have beaten this API almost to death for sure ;)
> I don't remember if we discussed this specific aspect,
> but I am personally in favor of
> EOPNOTSUPP := the fs does not support the set/get operation
> EINVAL := some flags provided as value is invalid
>
> For example, if the get API provides you with a mask of the
> valid flags that you can set, if you try to set flags outside of
> that mask you get EINVAL.
>
> That's my interpretation, but I agree that EOPNOTSUPP can also
> make sense in this situation.
<nod> I think I'd rather EOPNOTSUPP for "bits are set that the kernel
doesn't recognize" and EINVAL (or maybe something else like
EPROTONOSUPPORT) for "fs driver will not let you change this bit".
At least for the syscall interface; we probably have to flatten that to
EOPNOTSUPP for both legacy ioctls.
--D
> Thanks,
> Amir.
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 5/6] fs: prepare for extending file_get/setattr()
2025-07-01 19:40 ` Darrick J. Wong
@ 2025-07-01 19:54 ` Pali Rohár
2025-07-02 7:03 ` Amir Goldstein
0 siblings, 1 reply; 47+ messages in thread
From: Pali Rohár @ 2025-07-01 19:54 UTC (permalink / raw)
To: Darrick J. Wong
Cc: Amir Goldstein, Andrey Albershteyn, Arnd Bergmann,
Casey Schaufler, Christian Brauner, Jan Kara, Paul Moore,
linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Tuesday 01 July 2025 12:40:02 Darrick J. Wong wrote:
> On Tue, Jul 01, 2025 at 09:27:38PM +0200, Amir Goldstein wrote:
> > On Tue, Jul 1, 2025 at 8:31 PM Darrick J. Wong <djwong@kernel.org> wrote:
> > >
> > > On Mon, Jun 30, 2025 at 06:20:15PM +0200, Andrey Albershteyn wrote:
> > > > From: Amir Goldstein <amir73il@gmail.com>
> > > >
> > > > We intend to add support for more xflags to selective filesystems and
> > > > We cannot rely on copy_struct_from_user() to detect this extension.
> > > >
> > > > In preparation of extending the API, do not allow setting xflags unknown
> > > > by this kernel version.
> > > >
> > > > Also do not pass the read-only flags and read-only field fsx_nextents to
> > > > filesystem.
> > > >
> > > > These changes should not affect existing chattr programs that use the
> > > > ioctl to get fsxattr before setting the new values.
> > > >
> > > > Link: https://lore.kernel.org/linux-fsdevel/20250216164029.20673-4-pali@kernel.org/
> > > > Cc: Pali Rohár <pali@kernel.org>
> > > > Cc: Andrey Albershteyn <aalbersh@redhat.com>
> > > > Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> > > > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> > > > ---
> > > > fs/file_attr.c | 8 +++++++-
> > > > include/linux/fileattr.h | 20 ++++++++++++++++++++
> > > > 2 files changed, 27 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/fs/file_attr.c b/fs/file_attr.c
> > > > index 4e85fa00c092..62f08872d4ad 100644
> > > > --- a/fs/file_attr.c
> > > > +++ b/fs/file_attr.c
> > > > @@ -99,9 +99,10 @@ EXPORT_SYMBOL(vfs_fileattr_get);
> > > > int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> > > > {
> > > > struct fsxattr xfa;
> > > > + __u32 mask = FS_XFLAGS_MASK;
> > > >
> > > > memset(&xfa, 0, sizeof(xfa));
> > > > - xfa.fsx_xflags = fa->fsx_xflags;
> > > > + xfa.fsx_xflags = fa->fsx_xflags & mask;
> > >
> > > I wonder, should it be an error if a filesystem sets an fsx_xflags bit
> > > outside of FS_XFLAGS_MASK? I guess that's one way to prevent
> > > filesystems from overriding the VFS bits. ;)
> >
> > I think Pali has a plan on how to ensure that later
> > when the mask is provided via the API.
> >
> > >
> > > Though couldn't that be:
> > >
> > > xfa.fsx_xflags = fa->fsx_xflags & FS_XFLAGS_MASK;
> > >
> > > instead? And same below?
> > >
> >
> > Indeed. There is a reason for the var, because the next series
> > by Pali will use a user provided mask, which defaults to FS_XFLAGS_MASK,
> > so I left it this way.
> >
> > I don't see a problem with it keeping as is, but if it bothers you
> > I guess we can re-add the var later.
>
> Nah, it doesn't bother me that much.
>
> > > > xfa.fsx_extsize = fa->fsx_extsize;
> > > > xfa.fsx_nextents = fa->fsx_nextents;
> > > > xfa.fsx_projid = fa->fsx_projid;
> > > > @@ -118,11 +119,16 @@ static int copy_fsxattr_from_user(struct fileattr *fa,
> > > > struct fsxattr __user *ufa)
> > > > {
> > > > struct fsxattr xfa;
> > > > + __u32 mask = FS_XFLAGS_MASK;
> > > >
> > > > if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> > > > return -EFAULT;
> > > >
> > > > + if (xfa.fsx_xflags & ~mask)
> > > > + return -EINVAL;
> > >
> > > I wonder if you want EOPNOTSUPP here? We don't know how to support
> > > unknown xflags. OTOH if you all have beaten this to death while I was
> > > out then don't start another round just for me. :P
> >
> > We have beaten this API almost to death for sure ;)
> > I don't remember if we discussed this specific aspect,
> > but I am personally in favor of
> > EOPNOTSUPP := the fs does not support the set/get operation
> > EINVAL := some flags provided as value is invalid
> >
> > For example, if the get API provides you with a mask of the
> > valid flags that you can set, if you try to set flags outside of
> > that mask you get EINVAL.
> >
> > That's my interpretation, but I agree that EOPNOTSUPP can also
> > make sense in this situation.
>
> <nod> I think I'd rather EOPNOTSUPP for "bits are set that the kernel
> doesn't recognize" and EINVAL (or maybe something else like
> EPROTONOSUPPORT) for "fs driver will not let you change this bit".
> At least for the syscall interface; we probably have to flatten that to
> EOPNOTSUPP for both legacy ioctls.
... and this starting to be complicated if the "fs driver" is network
based (as fs driver can support, but remote server not). See also:
https://lore.kernel.org/linux-fsdevel/20241224160535.pi6nazpugqkhvfns@pali/t/#u
For backup/restore application it would be very useful to distinguish between:
- "kernel does not support flag X"
- "target filesystem does not support flag X"
- "wrong structure was passed / syscall incorrectly called"
third option is bug in application - fatal error. second option is just
a warning for user (sorry, we cannot set NEW FEATURE on FAT32, but if
you would do restore to other fs, it is supported). and first option
happens when you run new application on older kernel version, it is an
recoverable error (or warning to user, but with more important level
then second option as switching to different FS would not help).
Could we return different errnos for these 3 situations?
> --D
>
> > Thanks,
> > Amir.
> >
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 5/6] fs: prepare for extending file_get/setattr()
2025-07-01 19:54 ` Pali Rohár
@ 2025-07-02 7:03 ` Amir Goldstein
2025-07-02 9:48 ` Amir Goldstein
0 siblings, 1 reply; 47+ messages in thread
From: Amir Goldstein @ 2025-07-02 7:03 UTC (permalink / raw)
To: Pali Rohár
Cc: Darrick J. Wong, Andrey Albershteyn, Arnd Bergmann,
Casey Schaufler, Christian Brauner, Jan Kara, Paul Moore,
linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Tue, Jul 1, 2025 at 9:54 PM Pali Rohár <pali@kernel.org> wrote:
>
> On Tuesday 01 July 2025 12:40:02 Darrick J. Wong wrote:
> > On Tue, Jul 01, 2025 at 09:27:38PM +0200, Amir Goldstein wrote:
> > > On Tue, Jul 1, 2025 at 8:31 PM Darrick J. Wong <djwong@kernel.org> wrote:
> > > >
> > > > On Mon, Jun 30, 2025 at 06:20:15PM +0200, Andrey Albershteyn wrote:
> > > > > From: Amir Goldstein <amir73il@gmail.com>
> > > > >
> > > > > We intend to add support for more xflags to selective filesystems and
> > > > > We cannot rely on copy_struct_from_user() to detect this extension.
> > > > >
> > > > > In preparation of extending the API, do not allow setting xflags unknown
> > > > > by this kernel version.
> > > > >
> > > > > Also do not pass the read-only flags and read-only field fsx_nextents to
> > > > > filesystem.
> > > > >
> > > > > These changes should not affect existing chattr programs that use the
> > > > > ioctl to get fsxattr before setting the new values.
> > > > >
> > > > > Link: https://lore.kernel.org/linux-fsdevel/20250216164029.20673-4-pali@kernel.org/
> > > > > Cc: Pali Rohár <pali@kernel.org>
> > > > > Cc: Andrey Albershteyn <aalbersh@redhat.com>
> > > > > Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> > > > > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> > > > > ---
> > > > > fs/file_attr.c | 8 +++++++-
> > > > > include/linux/fileattr.h | 20 ++++++++++++++++++++
> > > > > 2 files changed, 27 insertions(+), 1 deletion(-)
> > > > >
> > > > > diff --git a/fs/file_attr.c b/fs/file_attr.c
> > > > > index 4e85fa00c092..62f08872d4ad 100644
> > > > > --- a/fs/file_attr.c
> > > > > +++ b/fs/file_attr.c
> > > > > @@ -99,9 +99,10 @@ EXPORT_SYMBOL(vfs_fileattr_get);
> > > > > int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> > > > > {
> > > > > struct fsxattr xfa;
> > > > > + __u32 mask = FS_XFLAGS_MASK;
> > > > >
> > > > > memset(&xfa, 0, sizeof(xfa));
> > > > > - xfa.fsx_xflags = fa->fsx_xflags;
> > > > > + xfa.fsx_xflags = fa->fsx_xflags & mask;
> > > >
> > > > I wonder, should it be an error if a filesystem sets an fsx_xflags bit
> > > > outside of FS_XFLAGS_MASK? I guess that's one way to prevent
> > > > filesystems from overriding the VFS bits. ;)
> > >
> > > I think Pali has a plan on how to ensure that later
> > > when the mask is provided via the API.
> > >
> > > >
> > > > Though couldn't that be:
> > > >
> > > > xfa.fsx_xflags = fa->fsx_xflags & FS_XFLAGS_MASK;
> > > >
> > > > instead? And same below?
> > > >
> > >
> > > Indeed. There is a reason for the var, because the next series
> > > by Pali will use a user provided mask, which defaults to FS_XFLAGS_MASK,
> > > so I left it this way.
> > >
> > > I don't see a problem with it keeping as is, but if it bothers you
> > > I guess we can re-add the var later.
> >
> > Nah, it doesn't bother me that much.
> >
> > > > > xfa.fsx_extsize = fa->fsx_extsize;
> > > > > xfa.fsx_nextents = fa->fsx_nextents;
> > > > > xfa.fsx_projid = fa->fsx_projid;
> > > > > @@ -118,11 +119,16 @@ static int copy_fsxattr_from_user(struct fileattr *fa,
> > > > > struct fsxattr __user *ufa)
> > > > > {
> > > > > struct fsxattr xfa;
> > > > > + __u32 mask = FS_XFLAGS_MASK;
> > > > >
> > > > > if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> > > > > return -EFAULT;
> > > > >
> > > > > + if (xfa.fsx_xflags & ~mask)
> > > > > + return -EINVAL;
> > > >
> > > > I wonder if you want EOPNOTSUPP here? We don't know how to support
> > > > unknown xflags. OTOH if you all have beaten this to death while I was
> > > > out then don't start another round just for me. :P
> > >
> > > We have beaten this API almost to death for sure ;)
> > > I don't remember if we discussed this specific aspect,
> > > but I am personally in favor of
> > > EOPNOTSUPP := the fs does not support the set/get operation
> > > EINVAL := some flags provided as value is invalid
> > >
> > > For example, if the get API provides you with a mask of the
> > > valid flags that you can set, if you try to set flags outside of
> > > that mask you get EINVAL.
> > >
> > > That's my interpretation, but I agree that EOPNOTSUPP can also
> > > make sense in this situation.
> >
> > <nod> I think I'd rather EOPNOTSUPP for "bits are set that the kernel
> > doesn't recognize" and EINVAL (or maybe something else like
> > EPROTONOSUPPORT) for "fs driver will not let you change this bit".
> > At least for the syscall interface; we probably have to flatten that to
> > EOPNOTSUPP for both legacy ioctls.
Given the precedents of returning EOPNOTSUPP in xfs_fileattr_set()
and ext4_ioctl_setflags() for flags that cannot be set, I agree.
>
> ... and this starting to be complicated if the "fs driver" is network
> based (as fs driver can support, but remote server not). See also:
> https://lore.kernel.org/linux-fsdevel/20241224160535.pi6nazpugqkhvfns@pali/t/#u
>
> For backup/restore application it would be very useful to distinguish between:
> - "kernel does not support flag X"
> - "target filesystem does not support flag X"
> - "wrong structure was passed / syscall incorrectly called"
>
> third option is bug in application - fatal error. second option is just
> a warning for user (sorry, we cannot set NEW FEATURE on FAT32, but if
> you would do restore to other fs, it is supported). and first option
> happens when you run new application on older kernel version, it is an
> recoverable error (or warning to user, but with more important level
> then second option as switching to different FS would not help).
>
> Could we return different errnos for these 3 situations?
That would be nice, but actually according to your plan
the get API returns the mask of flags supported by the filesystem
(on that specific object even), so userspace in fact has a way to
distinguish between the first two EOPNOTSUPP cases.
Thanks,
Amir.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 2/6] lsm: introduce new hooks for setting/getting inode fsxattr
2025-07-01 18:18 ` Darrick J. Wong
@ 2025-07-02 8:47 ` Andrey Albershteyn
0 siblings, 0 replies; 47+ messages in thread
From: Andrey Albershteyn @ 2025-07-02 8:47 UTC (permalink / raw)
To: Darrick J. Wong
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Christian Brauner,
Jan Kara, Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On 2025-07-01 11:18:13, Darrick J. Wong wrote:
> On Mon, Jun 30, 2025 at 06:20:12PM +0200, Andrey Albershteyn wrote:
> > Introduce new hooks for setting and getting filesystem extended
> > attributes on inode (FS_IOC_FSGETXATTR).
> >
> > Cc: selinux@vger.kernel.org
> > Cc: Paul Moore <paul@paul-moore.com>
> >
> > Acked-by: Paul Moore <paul@paul-moore.com>
> > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
>
> I wonder, were FS_IOC_FS[GS]ETXATTR already covered by the
> security_file_ioctl hook?
looks like
> If so, will an out of date security policy
> on a 6.17 kernel now fail to check the new file_[gs]etattr syscalls?
Yeah, probably, not sure if policies can have 'don't allow unknown'
but this is probably will need to be updated in the policy
>
> Though AFAICT the future of managing these "extra" file attributes is
> the system call so it's probably appropriate to have an explicit
> callout to LSMs.
>
> Acked-by: "Darrick J. Wong" <djwong@kernel.org>
>
> --D
>
> > ---
> > fs/file_attr.c | 19 ++++++++++++++++---
> > include/linux/lsm_hook_defs.h | 2 ++
> > include/linux/security.h | 16 ++++++++++++++++
> > security/security.c | 30 ++++++++++++++++++++++++++++++
> > 4 files changed, 64 insertions(+), 3 deletions(-)
> >
> > diff --git a/fs/file_attr.c b/fs/file_attr.c
> > index 2910b7047721..be62d97cc444 100644
> > --- a/fs/file_attr.c
> > +++ b/fs/file_attr.c
> > @@ -76,10 +76,15 @@ EXPORT_SYMBOL(fileattr_fill_flags);
> > int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
> > {
> > struct inode *inode = d_inode(dentry);
> > + int error;
> >
> > if (!inode->i_op->fileattr_get)
> > return -ENOIOCTLCMD;
> >
> > + error = security_inode_file_getattr(dentry, fa);
> > + if (error)
> > + return error;
> > +
> > return inode->i_op->fileattr_get(dentry, fa);
> > }
> > EXPORT_SYMBOL(vfs_fileattr_get);
> > @@ -242,12 +247,20 @@ int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
> > } else {
> > fa->flags |= old_ma.flags & ~FS_COMMON_FL;
> > }
> > +
> > err = fileattr_set_prepare(inode, &old_ma, fa);
> > - if (!err)
> > - err = inode->i_op->fileattr_set(idmap, dentry, fa);
> > + if (err)
> > + goto out;
> > + err = security_inode_file_setattr(dentry, fa);
> > + if (err)
> > + goto out;
> > + err = inode->i_op->fileattr_set(idmap, dentry, fa);
> > + if (err)
> > + goto out;
> > }
> > +
> > +out:
> > inode_unlock(inode);
> > -
> > return err;
> > }
> > EXPORT_SYMBOL(vfs_fileattr_set);
> > diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> > index bf3bbac4e02a..9600a4350e79 100644
> > --- a/include/linux/lsm_hook_defs.h
> > +++ b/include/linux/lsm_hook_defs.h
> > @@ -157,6 +157,8 @@ LSM_HOOK(int, 0, inode_removexattr, struct mnt_idmap *idmap,
> > struct dentry *dentry, const char *name)
> > LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
> > const char *name)
> > +LSM_HOOK(int, 0, inode_file_setattr, struct dentry *dentry, struct fileattr *fa)
> > +LSM_HOOK(int, 0, inode_file_getattr, struct dentry *dentry, struct fileattr *fa)
> > LSM_HOOK(int, 0, inode_set_acl, struct mnt_idmap *idmap,
> > struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)
> > LSM_HOOK(void, LSM_RET_VOID, inode_post_set_acl, struct dentry *dentry,
> > diff --git a/include/linux/security.h b/include/linux/security.h
> > index dba349629229..9ed0d0e0c81f 100644
> > --- a/include/linux/security.h
> > +++ b/include/linux/security.h
> > @@ -451,6 +451,10 @@ int security_inode_listxattr(struct dentry *dentry);
> > int security_inode_removexattr(struct mnt_idmap *idmap,
> > struct dentry *dentry, const char *name);
> > void security_inode_post_removexattr(struct dentry *dentry, const char *name);
> > +int security_inode_file_setattr(struct dentry *dentry,
> > + struct fileattr *fa);
> > +int security_inode_file_getattr(struct dentry *dentry,
> > + struct fileattr *fa);
> > int security_inode_need_killpriv(struct dentry *dentry);
> > int security_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry);
> > int security_inode_getsecurity(struct mnt_idmap *idmap,
> > @@ -1052,6 +1056,18 @@ static inline void security_inode_post_removexattr(struct dentry *dentry,
> > const char *name)
> > { }
> >
> > +static inline int security_inode_file_setattr(struct dentry *dentry,
> > + struct fileattr *fa)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline int security_inode_file_getattr(struct dentry *dentry,
> > + struct fileattr *fa)
> > +{
> > + return 0;
> > +}
> > +
> > static inline int security_inode_need_killpriv(struct dentry *dentry)
> > {
> > return cap_inode_need_killpriv(dentry);
> > diff --git a/security/security.c b/security/security.c
> > index 596d41818577..711b4de40b8d 100644
> > --- a/security/security.c
> > +++ b/security/security.c
> > @@ -2622,6 +2622,36 @@ void security_inode_post_removexattr(struct dentry *dentry, const char *name)
> > call_void_hook(inode_post_removexattr, dentry, name);
> > }
> >
> > +/**
> > + * security_inode_file_setattr() - check if setting fsxattr is allowed
> > + * @dentry: file to set filesystem extended attributes on
> > + * @fa: extended attributes to set on the inode
> > + *
> > + * Called when file_setattr() syscall or FS_IOC_FSSETXATTR ioctl() is called on
> > + * inode
> > + *
> > + * Return: Returns 0 if permission is granted.
> > + */
> > +int security_inode_file_setattr(struct dentry *dentry, struct fileattr *fa)
> > +{
> > + return call_int_hook(inode_file_setattr, dentry, fa);
> > +}
> > +
> > +/**
> > + * security_inode_file_getattr() - check if retrieving fsxattr is allowed
> > + * @dentry: file to retrieve filesystem extended attributes from
> > + * @fa: extended attributes to get
> > + *
> > + * Called when file_getattr() syscall or FS_IOC_FSGETXATTR ioctl() is called on
> > + * inode
> > + *
> > + * Return: Returns 0 if permission is granted.
> > + */
> > +int security_inode_file_getattr(struct dentry *dentry, struct fileattr *fa)
> > +{
> > + return call_int_hook(inode_file_getattr, dentry, fa);
> > +}
> > +
> > /**
> > * security_inode_need_killpriv() - Check if security_inode_killpriv() required
> > * @dentry: associated dentry
> >
> > --
> > 2.47.2
> >
> >
>
--
- Andrey
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-01 12:34 ` Christian Brauner
@ 2025-07-02 9:13 ` Amir Goldstein
0 siblings, 0 replies; 47+ messages in thread
From: Amir Goldstein @ 2025-07-02 9:13 UTC (permalink / raw)
To: Christian Brauner
Cc: Andrey Albershteyn, Arnd Bergmann, Casey Schaufler, Jan Kara,
Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
> > +/*
> > + * 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 fsx_fileattr {
> > + __u32 fsx_xflags; /* xflags field value (get/set) */
> > + __u32 fsx_extsize; /* extsize field value (get/set)*/
> > + __u32 fsx_nextents; /* nextents field value (get) */
> > + __u32 fsx_projid; /* project identifier (get/set) */
> > + __u32 fsx_cowextsize; /* CoW extsize field value (get/set) */
>
> This misses a:
>
> __u32 __spare;
>
> so there's no holes in the struct. :)
Adding __spare and not verifying that it is zeroed gets us to the
point that we are not able to replace __spare with a real field later.
I suggest to resolve this hole as Darrick and Pali suggested by making it
__u64 fsx_xflags
w.r.t Darrick's comment, I kind of like it that the name for the UAPI
struct (fsxattr)
differs from the name of the kernel internal representation (fileattr), but
I agree that fsx_fileattr does not give a good hint on what it is.
I think that renaming struct fsx_fileattr to struct fsxattr64 along
with changing the
width of fsx_xflags will help reduce the confusion of users.
What do you guys think?
Thanks,
Amir.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 5/6] fs: prepare for extending file_get/setattr()
2025-07-02 7:03 ` Amir Goldstein
@ 2025-07-02 9:48 ` Amir Goldstein
2025-07-02 12:24 ` Christian Brauner
0 siblings, 1 reply; 47+ messages in thread
From: Amir Goldstein @ 2025-07-02 9:48 UTC (permalink / raw)
To: Pali Rohár
Cc: Darrick J. Wong, Andrey Albershteyn, Arnd Bergmann,
Casey Schaufler, Christian Brauner, Jan Kara, Paul Moore,
linux-api, linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Wed, Jul 2, 2025 at 9:03 AM Amir Goldstein <amir73il@gmail.com> wrote:
>
> On Tue, Jul 1, 2025 at 9:54 PM Pali Rohár <pali@kernel.org> wrote:
> >
> > On Tuesday 01 July 2025 12:40:02 Darrick J. Wong wrote:
> > > On Tue, Jul 01, 2025 at 09:27:38PM +0200, Amir Goldstein wrote:
> > > > On Tue, Jul 1, 2025 at 8:31 PM Darrick J. Wong <djwong@kernel.org> wrote:
> > > > >
> > > > > On Mon, Jun 30, 2025 at 06:20:15PM +0200, Andrey Albershteyn wrote:
> > > > > > From: Amir Goldstein <amir73il@gmail.com>
> > > > > >
> > > > > > We intend to add support for more xflags to selective filesystems and
> > > > > > We cannot rely on copy_struct_from_user() to detect this extension.
> > > > > >
> > > > > > In preparation of extending the API, do not allow setting xflags unknown
> > > > > > by this kernel version.
> > > > > >
> > > > > > Also do not pass the read-only flags and read-only field fsx_nextents to
> > > > > > filesystem.
> > > > > >
> > > > > > These changes should not affect existing chattr programs that use the
> > > > > > ioctl to get fsxattr before setting the new values.
> > > > > >
> > > > > > Link: https://lore.kernel.org/linux-fsdevel/20250216164029.20673-4-pali@kernel.org/
> > > > > > Cc: Pali Rohár <pali@kernel.org>
> > > > > > Cc: Andrey Albershteyn <aalbersh@redhat.com>
> > > > > > Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> > > > > > Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
> > > > > > ---
> > > > > > fs/file_attr.c | 8 +++++++-
> > > > > > include/linux/fileattr.h | 20 ++++++++++++++++++++
> > > > > > 2 files changed, 27 insertions(+), 1 deletion(-)
> > > > > >
> > > > > > diff --git a/fs/file_attr.c b/fs/file_attr.c
> > > > > > index 4e85fa00c092..62f08872d4ad 100644
> > > > > > --- a/fs/file_attr.c
> > > > > > +++ b/fs/file_attr.c
> > > > > > @@ -99,9 +99,10 @@ EXPORT_SYMBOL(vfs_fileattr_get);
> > > > > > int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
> > > > > > {
> > > > > > struct fsxattr xfa;
> > > > > > + __u32 mask = FS_XFLAGS_MASK;
> > > > > >
> > > > > > memset(&xfa, 0, sizeof(xfa));
> > > > > > - xfa.fsx_xflags = fa->fsx_xflags;
> > > > > > + xfa.fsx_xflags = fa->fsx_xflags & mask;
> > > > >
> > > > > I wonder, should it be an error if a filesystem sets an fsx_xflags bit
> > > > > outside of FS_XFLAGS_MASK? I guess that's one way to prevent
> > > > > filesystems from overriding the VFS bits. ;)
> > > >
> > > > I think Pali has a plan on how to ensure that later
> > > > when the mask is provided via the API.
> > > >
> > > > >
> > > > > Though couldn't that be:
> > > > >
> > > > > xfa.fsx_xflags = fa->fsx_xflags & FS_XFLAGS_MASK;
> > > > >
> > > > > instead? And same below?
> > > > >
> > > >
> > > > Indeed. There is a reason for the var, because the next series
> > > > by Pali will use a user provided mask, which defaults to FS_XFLAGS_MASK,
> > > > so I left it this way.
> > > >
> > > > I don't see a problem with it keeping as is, but if it bothers you
> > > > I guess we can re-add the var later.
> > >
> > > Nah, it doesn't bother me that much.
> > >
> > > > > > xfa.fsx_extsize = fa->fsx_extsize;
> > > > > > xfa.fsx_nextents = fa->fsx_nextents;
> > > > > > xfa.fsx_projid = fa->fsx_projid;
> > > > > > @@ -118,11 +119,16 @@ static int copy_fsxattr_from_user(struct fileattr *fa,
> > > > > > struct fsxattr __user *ufa)
> > > > > > {
> > > > > > struct fsxattr xfa;
> > > > > > + __u32 mask = FS_XFLAGS_MASK;
> > > > > >
> > > > > > if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> > > > > > return -EFAULT;
> > > > > >
> > > > > > + if (xfa.fsx_xflags & ~mask)
> > > > > > + return -EINVAL;
> > > > >
> > > > > I wonder if you want EOPNOTSUPP here? We don't know how to support
> > > > > unknown xflags. OTOH if you all have beaten this to death while I was
> > > > > out then don't start another round just for me. :P
> > > >
> > > > We have beaten this API almost to death for sure ;)
> > > > I don't remember if we discussed this specific aspect,
> > > > but I am personally in favor of
> > > > EOPNOTSUPP := the fs does not support the set/get operation
> > > > EINVAL := some flags provided as value is invalid
> > > >
> > > > For example, if the get API provides you with a mask of the
> > > > valid flags that you can set, if you try to set flags outside of
> > > > that mask you get EINVAL.
> > > >
> > > > That's my interpretation, but I agree that EOPNOTSUPP can also
> > > > make sense in this situation.
> > >
> > > <nod> I think I'd rather EOPNOTSUPP for "bits are set that the kernel
> > > doesn't recognize" and EINVAL (or maybe something else like
> > > EPROTONOSUPPORT) for "fs driver will not let you change this bit".
> > > At least for the syscall interface; we probably have to flatten that to
> > > EOPNOTSUPP for both legacy ioctls.
>
> Given the precedents of returning EOPNOTSUPP in xfs_fileattr_set()
> and ext4_ioctl_setflags() for flags that cannot be set, I agree.
>
Wait, I misparsed what you wrote, so I think I "agreed" only to the
first part of your suggestion.
My claim is that unlike the xfs_has_v3inodes() check in
xfs_ioctl_setattr_xflags(),
ext4/f2fs etc return EOPNOTSUPP for various flags depending on supported fs
features (e.g. casefold,dax,encryption), so I think it will be hard to
impose a strict rule
where "fs does not support the feature" returns EINVAL in the syscalls API.
Therefore, I propose to change the code in this patch to
return EOPNOTSUPP for flags that kernel does not support
and with coming changes from Pali, it will also return the same
EOPNOTSUPP for flags that the fs instance does not support.
Christian,
Can you please amend the return value in the following chunk:
@@ -119,11 +120,16 @@ static int copy_fsxattr_from_user(struct fileattr *fa,
struct fsxattr __user *ufa)
{
struct fsxattr xfa;
+ __u32 mask = FS_XFLAGS_MASK;
if (copy_from_user(&xfa, ufa, sizeof(xfa)))
return -EFAULT;
+ if (xfa.fsx_xflags & ~mask)
+ return -EOPNOTSUPP;
+
Thanks,
Amir.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 5/6] fs: prepare for extending file_get/setattr()
2025-07-02 9:48 ` Amir Goldstein
@ 2025-07-02 12:24 ` Christian Brauner
0 siblings, 0 replies; 47+ messages in thread
From: Christian Brauner @ 2025-07-02 12:24 UTC (permalink / raw)
To: Amir Goldstein
Cc: Pali Rohár, Darrick J. Wong, Andrey Albershteyn,
Arnd Bergmann, Casey Schaufler, Jan Kara, Paul Moore, linux-api,
linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
> Christian,
>
> Can you please amend the return value in the following chunk:
>
> @@ -119,11 +120,16 @@ static int copy_fsxattr_from_user(struct fileattr *fa,
> struct fsxattr __user *ufa)
> {
> struct fsxattr xfa;
> + __u32 mask = FS_XFLAGS_MASK;
>
> if (copy_from_user(&xfa, ufa, sizeof(xfa)))
> return -EFAULT;
>
> + if (xfa.fsx_xflags & ~mask)
> + return -EOPNOTSUPP;
> +
Done.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-01 18:43 ` Darrick J. Wong
2025-07-01 18:54 ` Pali Rohár
@ 2025-07-02 12:40 ` Christian Brauner
2025-07-02 13:43 ` Amir Goldstein
1 sibling, 1 reply; 47+ messages in thread
From: Christian Brauner @ 2025-07-02 12:40 UTC (permalink / raw)
To: Darrick J. Wong
Cc: Andrey Albershteyn, Amir Goldstein, Arnd Bergmann,
Casey Schaufler, Jan Kara, Pali Rohár, Paul Moore, linux-api,
linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
> Er... "fsx_fileattr" is the struct that the system call uses?
>
> That's a little confusing considering that xfs already has a
> xfs_fill_fsxattr function that actually fills a struct fileattr.
> That could be renamed xfs_fill_fileattr.
>
> I dunno. There's a part of me that would really rather that the
> file_getattr and file_setattr syscalls operate on a struct file_attr.
Agreed, I'm pretty sure I suggested this during an earlier review. Fits
in line with struct mount_attr and others. Fwiw, struct fileattr (the
kernel internal thing) should've really been struct file_kattr or struct
kernel_file_attr. This is a common pattern now:
struct mount_attr vs struct mount_kattr
struct clone_args vs struct kernel_clone_kargs
etc.
>
> More whining/bikeshedding to come.
>
> <snip stuff that looks ok to me>
>
> <<well, I still dislike the CLASS(fd, fd)(fd) syntax...>>
Noted, and duly ignored...
>
> > diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
> > index 0098b0ce8ccb..0784f2033ba4 100644
> > --- a/include/uapi/linux/fs.h
> > +++ b/include/uapi/linux/fs.h
> > @@ -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 fsx_fileattr {
> > + __u32 fsx_xflags; /* xflags field value (get/set) */
>
> Should this to be __u64 from the start? Seeing as (a) this struct is
Agreed. I changed that.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-02 12:40 ` Christian Brauner
@ 2025-07-02 13:43 ` Amir Goldstein
2025-07-02 18:37 ` Darrick J. Wong
0 siblings, 1 reply; 47+ messages in thread
From: Amir Goldstein @ 2025-07-02 13:43 UTC (permalink / raw)
To: Christian Brauner
Cc: Darrick J. Wong, Andrey Albershteyn, Arnd Bergmann,
Casey Schaufler, Jan Kara, Pali Rohár, Paul Moore, linux-api,
linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Wed, Jul 2, 2025 at 2:40 PM Christian Brauner <brauner@kernel.org> wrote:
>
> > Er... "fsx_fileattr" is the struct that the system call uses?
> >
> > That's a little confusing considering that xfs already has a
> > xfs_fill_fsxattr function that actually fills a struct fileattr.
> > That could be renamed xfs_fill_fileattr.
> >
> > I dunno. There's a part of me that would really rather that the
> > file_getattr and file_setattr syscalls operate on a struct file_attr.
>
> Agreed, I'm pretty sure I suggested this during an earlier review. Fits
> in line with struct mount_attr and others. Fwiw, struct fileattr (the
> kernel internal thing) should've really been struct file_kattr or struct
> kernel_file_attr. This is a common pattern now:
>
> struct mount_attr vs struct mount_kattr
>
> struct clone_args vs struct kernel_clone_kargs
>
> etc.
>file_attr
I can see the allure, but we have a long history here with fsxattr,
so I think it serves the users better to reference this history with
fsxattr64.
That, and also, avoid the churn of s/fileattr/file_kattr/
If you want to do this renaming, please do it in the same PR
because I don't like the idea of having both file_attr and fileattr
in the tree for an unknown period.
Thanks,
Amir.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-02 13:43 ` Amir Goldstein
@ 2025-07-02 18:37 ` Darrick J. Wong
2025-07-03 8:28 ` Christian Brauner
0 siblings, 1 reply; 47+ messages in thread
From: Darrick J. Wong @ 2025-07-02 18:37 UTC (permalink / raw)
To: Amir Goldstein
Cc: Christian Brauner, Andrey Albershteyn, Arnd Bergmann,
Casey Schaufler, Jan Kara, Pali Rohár, Paul Moore, linux-api,
linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Wed, Jul 02, 2025 at 03:43:28PM +0200, Amir Goldstein wrote:
> On Wed, Jul 2, 2025 at 2:40 PM Christian Brauner <brauner@kernel.org> wrote:
> >
> > > Er... "fsx_fileattr" is the struct that the system call uses?
> > >
> > > That's a little confusing considering that xfs already has a
> > > xfs_fill_fsxattr function that actually fills a struct fileattr.
> > > That could be renamed xfs_fill_fileattr.
> > >
> > > I dunno. There's a part of me that would really rather that the
> > > file_getattr and file_setattr syscalls operate on a struct file_attr.
> >
> > Agreed, I'm pretty sure I suggested this during an earlier review. Fits
> > in line with struct mount_attr and others. Fwiw, struct fileattr (the
> > kernel internal thing) should've really been struct file_kattr or struct
> > kernel_file_attr. This is a common pattern now:
> >
> > struct mount_attr vs struct mount_kattr
> >
> > struct clone_args vs struct kernel_clone_kargs
> >
> > etc.
> >file_attr
>
> I can see the allure, but we have a long history here with fsxattr,
> so I think it serves the users better to reference this history with
> fsxattr64.
<shrug> XFS has a long history with 'struct fsxattr' (the structure you
passed to XFS_IOC_FSGETXATTR) but the rest of the kernel needn't be so
fixated upon the historical name. ext4/f2fs/overlay afaict are just
going along for the ride.
IOWs I like brauner's struct file_attr and struct file_kattr
suggestions.
> That, and also, avoid the churn of s/fileattr/file_kattr/
> If you want to do this renaming, please do it in the same PR
> because I don't like the idea of having both file_attr and fileattr
> in the tree for an unknown period.
But yeah, that ought to be a treewide change done at the same time.
--D
>
> Thanks,
> Amir.
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-02 18:37 ` Darrick J. Wong
@ 2025-07-03 8:28 ` Christian Brauner
2025-07-03 8:42 ` Amir Goldstein
0 siblings, 1 reply; 47+ messages in thread
From: Christian Brauner @ 2025-07-03 8:28 UTC (permalink / raw)
To: Darrick J. Wong, Amir Goldstein, Jan Kara
Cc: Andrey Albershteyn, Arnd Bergmann, Casey Schaufler,
Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
[-- Attachment #1: Type: text/plain, Size: 1945 bytes --]
On Wed, Jul 02, 2025 at 11:37:50AM -0700, Darrick J. Wong wrote:
> On Wed, Jul 02, 2025 at 03:43:28PM +0200, Amir Goldstein wrote:
> > On Wed, Jul 2, 2025 at 2:40 PM Christian Brauner <brauner@kernel.org> wrote:
> > >
> > > > Er... "fsx_fileattr" is the struct that the system call uses?
> > > >
> > > > That's a little confusing considering that xfs already has a
> > > > xfs_fill_fsxattr function that actually fills a struct fileattr.
> > > > That could be renamed xfs_fill_fileattr.
> > > >
> > > > I dunno. There's a part of me that would really rather that the
> > > > file_getattr and file_setattr syscalls operate on a struct file_attr.
> > >
> > > Agreed, I'm pretty sure I suggested this during an earlier review. Fits
> > > in line with struct mount_attr and others. Fwiw, struct fileattr (the
> > > kernel internal thing) should've really been struct file_kattr or struct
> > > kernel_file_attr. This is a common pattern now:
> > >
> > > struct mount_attr vs struct mount_kattr
> > >
> > > struct clone_args vs struct kernel_clone_kargs
> > >
> > > etc.
> > >file_attr
> >
> > I can see the allure, but we have a long history here with fsxattr,
> > so I think it serves the users better to reference this history with
> > fsxattr64.
>
> <shrug> XFS has a long history with 'struct fsxattr' (the structure you
> passed to XFS_IOC_FSGETXATTR) but the rest of the kernel needn't be so
> fixated upon the historical name. ext4/f2fs/overlay afaict are just
> going along for the ride.
>
> IOWs I like brauner's struct file_attr and struct file_kattr
> suggestions.
>
> > That, and also, avoid the churn of s/fileattr/file_kattr/
> > If you want to do this renaming, please do it in the same PR
> > because I don't like the idea of having both file_attr and fileattr
> > in the tree for an unknown period.
>
> But yeah, that ought to be a treewide change done at the same time.
Why do you all hate me? ;)
See the appended patch.
[-- Attachment #2: 0001-tree-wide-s-struct-fileattr-struct-file_kattr-g.patch --]
[-- Type: text/x-diff, Size: 45188 bytes --]
From 3a5f5c281e7993342cf285285fa0a496d4fca2b7 Mon Sep 17 00:00:00 2001
From: Christian Brauner <brauner@kernel.org>
Date: Thu, 3 Jul 2025 09:36:41 +0200
Subject: [PATCH] tree-wide: s/struct fileattr/struct file_kattr/g
Now that we expose struct file_attr as our uapi struct rename all the
internal struct to struct file_kattr to clearly communicate that it is a
kernel internal struct. This is similar to struct mount_{k}attr and
others.
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
Documentation/filesystems/locking.rst | 4 ++--
Documentation/filesystems/vfs.rst | 4 ++--
fs/bcachefs/fs.c | 4 ++--
fs/btrfs/ioctl.c | 4 ++--
fs/btrfs/ioctl.h | 6 ++---
fs/ecryptfs/inode.c | 4 ++--
fs/efivarfs/inode.c | 4 ++--
fs/ext2/ext2.h | 4 ++--
fs/ext2/ioctl.c | 4 ++--
fs/ext4/ext4.h | 4 ++--
fs/ext4/ioctl.c | 4 ++--
fs/f2fs/f2fs.h | 4 ++--
fs/f2fs/file.c | 4 ++--
fs/file_attr.c | 34 +++++++++++++--------------
fs/fuse/fuse_i.h | 4 ++--
fs/fuse/ioctl.c | 4 ++--
fs/gfs2/file.c | 4 ++--
fs/gfs2/inode.h | 4 ++--
fs/hfsplus/hfsplus_fs.h | 4 ++--
fs/hfsplus/inode.c | 4 ++--
fs/jfs/ioctl.c | 4 ++--
fs/jfs/jfs_inode.h | 4 ++--
fs/nilfs2/ioctl.c | 4 ++--
fs/nilfs2/nilfs.h | 4 ++--
fs/ocfs2/ioctl.c | 4 ++--
fs/ocfs2/ioctl.h | 4 ++--
fs/orangefs/inode.c | 4 ++--
fs/overlayfs/copy_up.c | 4 ++--
fs/overlayfs/inode.c | 12 +++++-----
fs/overlayfs/overlayfs.h | 10 ++++----
fs/overlayfs/util.c | 2 +-
fs/ubifs/ioctl.c | 4 ++--
fs/ubifs/ubifs.h | 4 ++--
fs/xfs/xfs_ioctl.c | 18 +++++++-------
fs/xfs/xfs_ioctl.h | 4 ++--
include/linux/fileattr.h | 14 +++++------
include/linux/fs.h | 6 ++---
include/linux/lsm_hook_defs.h | 4 ++--
include/linux/security.h | 8 +++----
include/uapi/linux/fs.h | 2 +-
mm/shmem.c | 4 ++--
security/security.c | 4 ++--
security/selinux/hooks.c | 4 ++--
43 files changed, 122 insertions(+), 122 deletions(-)
diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst
index 2e567e341c3b..2ff02653d7cc 100644
--- a/Documentation/filesystems/locking.rst
+++ b/Documentation/filesystems/locking.rst
@@ -87,8 +87,8 @@ prototypes::
int (*tmpfile) (struct mnt_idmap *, struct inode *,
struct file *, umode_t);
int (*fileattr_set)(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
- int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
+ int (*fileattr_get)(struct dentry *dentry, struct file_kattr *fa);
struct posix_acl * (*get_acl)(struct mnt_idmap *, struct dentry *, int);
struct offset_ctx *(*get_offset_ctx)(struct inode *inode);
diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst
index fd32a9a17bfb..f2bbf4def123 100644
--- a/Documentation/filesystems/vfs.rst
+++ b/Documentation/filesystems/vfs.rst
@@ -515,8 +515,8 @@ As of kernel 2.6.22, the following members are defined:
struct posix_acl * (*get_acl)(struct mnt_idmap *, struct dentry *, int);
int (*set_acl)(struct mnt_idmap *, struct dentry *, struct posix_acl *, int);
int (*fileattr_set)(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
- int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
+ int (*fileattr_get)(struct dentry *dentry, struct file_kattr *fa);
struct offset_ctx *(*get_offset_ctx)(struct inode *inode);
};
diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c
index 85d13f800165..7c4de887629c 100644
--- a/fs/bcachefs/fs.c
+++ b/fs/bcachefs/fs.c
@@ -1619,7 +1619,7 @@ static const __maybe_unused unsigned bch_flags_to_xflags[] = {
};
static int bch2_fileattr_get(struct dentry *dentry,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
struct bch_inode_info *inode = to_bch_ei(d_inode(dentry));
struct bch_fs *c = inode->v.i_sb->s_fs_info;
@@ -1682,7 +1682,7 @@ static int fssetxattr_inode_update_fn(struct btree_trans *trans,
static int bch2_fileattr_set(struct mnt_idmap *idmap,
struct dentry *dentry,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
struct bch_inode_info *inode = to_bch_ei(d_inode(dentry));
struct bch_fs *c = inode->v.i_sb->s_fs_info;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 913acef3f0a9..ffb28bfba4fa 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -245,7 +245,7 @@ static int btrfs_check_ioctl_vol_args2_subvol_name(const struct btrfs_ioctl_vol_
* Set flags/xflags from the internal inode flags. The remaining items of
* fsxattr are zeroed.
*/
-int btrfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int btrfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
const struct btrfs_inode *inode = BTRFS_I(d_inode(dentry));
@@ -254,7 +254,7 @@ int btrfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
int btrfs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct btrfs_inode *inode = BTRFS_I(d_inode(dentry));
struct btrfs_root *root = inode->root;
diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h
index e08ea446cf48..ccf6bed9cc24 100644
--- a/fs/btrfs/ioctl.h
+++ b/fs/btrfs/ioctl.h
@@ -8,7 +8,7 @@
struct file;
struct dentry;
struct mnt_idmap;
-struct fileattr;
+struct file_kattr;
struct io_uring_cmd;
struct btrfs_inode;
struct btrfs_fs_info;
@@ -16,9 +16,9 @@ struct btrfs_ioctl_balance_args;
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
long btrfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
-int btrfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+int btrfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
int btrfs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
int btrfs_ioctl_get_supported_features(void __user *arg);
void btrfs_sync_inode_flags_to_i_flags(struct btrfs_inode *inode);
void btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info,
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 493d7f194956..d83416af17b4 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -1124,13 +1124,13 @@ static int ecryptfs_removexattr(struct dentry *dentry, struct inode *inode,
return rc;
}
-static int ecryptfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+static int ecryptfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
return vfs_fileattr_get(ecryptfs_dentry_to_lower(dentry), fa);
}
static int ecryptfs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
int rc;
diff --git a/fs/efivarfs/inode.c b/fs/efivarfs/inode.c
index 98a7299a9ee9..2891614abf8d 100644
--- a/fs/efivarfs/inode.c
+++ b/fs/efivarfs/inode.c
@@ -138,7 +138,7 @@ const struct inode_operations efivarfs_dir_inode_operations = {
};
static int
-efivarfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+efivarfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
unsigned int i_flags;
unsigned int flags = 0;
@@ -154,7 +154,7 @@ efivarfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
static int
efivarfs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
unsigned int i_flags = 0;
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h
index 4025f875252a..cf97b76e9fd3 100644
--- a/fs/ext2/ext2.h
+++ b/fs/ext2/ext2.h
@@ -750,9 +750,9 @@ extern int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 len);
/* ioctl.c */
-extern int ext2_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+extern int ext2_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
extern int ext2_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
extern long ext2_ioctl(struct file *, unsigned int, unsigned long);
extern long ext2_compat_ioctl(struct file *, unsigned int, unsigned long);
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 44e04484e570..c3fea55b8efa 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -18,7 +18,7 @@
#include <linux/uaccess.h>
#include <linux/fileattr.h>
-int ext2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int ext2_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct ext2_inode_info *ei = EXT2_I(d_inode(dentry));
@@ -28,7 +28,7 @@ int ext2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
int ext2_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct ext2_inode_info *ei = EXT2_I(inode);
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 18373de980f2..7d962e7f388a 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3103,8 +3103,8 @@ extern int ext4_ind_remove_space(handle_t *handle, struct inode *inode,
extern long ext4_ioctl(struct file *, unsigned int, unsigned long);
extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long);
int ext4_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
-int ext4_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
+int ext4_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
extern void ext4_reset_inode_seed(struct inode *inode);
int ext4_update_overhead(struct super_block *sb, bool force);
int ext4_force_shutdown(struct super_block *sb, u32 flags);
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 5668a17458ae..84e3c73952d7 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -980,7 +980,7 @@ static long ext4_ioctl_group_add(struct file *file,
return err;
}
-int ext4_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int ext4_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct ext4_inode_info *ei = EXT4_I(inode);
@@ -997,7 +997,7 @@ int ext4_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
int ext4_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
u32 flags = fa->flags;
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 9333a22b9a01..c78464792ceb 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3615,9 +3615,9 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count);
int f2fs_do_shutdown(struct f2fs_sb_info *sbi, unsigned int flag,
bool readonly, bool need_lock);
int f2fs_precache_extents(struct inode *inode);
-int f2fs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+int f2fs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
int f2fs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int f2fs_transfer_project_quota(struct inode *inode, kprojid_t kprojid);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 6bd3de64f2a8..90180ca22abd 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -3356,7 +3356,7 @@ static int f2fs_ioc_setproject(struct inode *inode, __u32 projid)
}
#endif
-int f2fs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int f2fs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct f2fs_inode_info *fi = F2FS_I(inode);
@@ -3380,7 +3380,7 @@ int f2fs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
int f2fs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
u32 fsflags = fa->flags, mask = F2FS_SETTABLE_FS_FL;
diff --git a/fs/file_attr.c b/fs/file_attr.c
index 21d6a0607345..17745c89e2be 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -17,7 +17,7 @@
* Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All
* other fields are zeroed.
*/
-void fileattr_fill_xflags(struct fileattr *fa, u32 xflags)
+void fileattr_fill_xflags(struct file_kattr *fa, u32 xflags)
{
memset(fa, 0, sizeof(*fa));
fa->fsx_valid = true;
@@ -47,7 +47,7 @@ EXPORT_SYMBOL(fileattr_fill_xflags);
* Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
* All other fields are zeroed.
*/
-void fileattr_fill_flags(struct fileattr *fa, u32 flags)
+void fileattr_fill_flags(struct file_kattr *fa, u32 flags)
{
memset(fa, 0, sizeof(*fa));
fa->flags_valid = true;
@@ -78,7 +78,7 @@ EXPORT_SYMBOL(fileattr_fill_flags);
*
* Return: 0 on success, or a negative error on failure.
*/
-int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int vfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
int error;
@@ -94,7 +94,7 @@ 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,
+static void fileattr_to_file_attr(const struct file_kattr *fa,
struct file_attr *fattr)
{
__u32 mask = FS_XFLAGS_MASK;
@@ -114,7 +114,7 @@ static void fileattr_to_file_attr(const struct fileattr *fa,
*
* Return: 0 on success, or -EFAULT on failure.
*/
-int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
+int copy_fsxattr_to_user(const struct file_kattr *fa, struct fsxattr __user *ufa)
{
struct fsxattr xfa;
__u32 mask = FS_XFLAGS_MASK;
@@ -134,7 +134,7 @@ 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)
+ struct file_kattr *fa)
{
__u32 mask = FS_XFLAGS_MASK;
@@ -150,7 +150,7 @@ static int file_attr_to_fileattr(const struct file_attr *fattr,
return 0;
}
-static int copy_fsxattr_from_user(struct fileattr *fa,
+static int copy_fsxattr_from_user(struct file_kattr *fa,
struct fsxattr __user *ufa)
{
struct fsxattr xfa;
@@ -179,8 +179,8 @@ static int copy_fsxattr_from_user(struct fileattr *fa,
* Note: must be called with inode lock held.
*/
static int fileattr_set_prepare(struct inode *inode,
- const struct fileattr *old_ma,
- struct fileattr *fa)
+ const struct file_kattr *old_ma,
+ struct file_kattr *fa)
{
int err;
@@ -263,10 +263,10 @@ static int fileattr_set_prepare(struct inode *inode,
* Return: 0 on success, or a negative error on failure.
*/
int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
- struct fileattr old_ma = {};
+ struct file_kattr old_ma = {};
int err;
if (!inode->i_op->fileattr_set)
@@ -308,7 +308,7 @@ EXPORT_SYMBOL(vfs_fileattr_set);
int ioctl_getflags(struct file *file, unsigned int __user *argp)
{
- struct fileattr fa = { .flags_valid = true }; /* hint only */
+ struct file_kattr fa = { .flags_valid = true }; /* hint only */
int err;
err = vfs_fileattr_get(file->f_path.dentry, &fa);
@@ -324,7 +324,7 @@ int ioctl_setflags(struct file *file, unsigned int __user *argp)
{
struct mnt_idmap *idmap = file_mnt_idmap(file);
struct dentry *dentry = file->f_path.dentry;
- struct fileattr fa;
+ struct file_kattr fa;
unsigned int flags;
int err;
@@ -345,7 +345,7 @@ EXPORT_SYMBOL(ioctl_setflags);
int ioctl_fsgetxattr(struct file *file, void __user *argp)
{
- struct fileattr fa = { .fsx_valid = true }; /* hint only */
+ struct file_kattr fa = { .fsx_valid = true }; /* hint only */
int err;
err = vfs_fileattr_get(file->f_path.dentry, &fa);
@@ -362,7 +362,7 @@ int ioctl_fssetxattr(struct file *file, void __user *argp)
{
struct mnt_idmap *idmap = file_mnt_idmap(file);
struct dentry *dentry = file->f_path.dentry;
- struct fileattr fa;
+ struct file_kattr fa;
int err;
err = copy_fsxattr_from_user(&fa, argp);
@@ -387,7 +387,7 @@ SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename,
struct filename *name __free(putname) = NULL;
unsigned int lookup_flags = 0;
struct file_attr fattr;
- struct fileattr fa;
+ struct file_kattr fa;
int error;
BUILD_BUG_ON(sizeof(struct file_attr) < FILE_ATTR_SIZE_VER0);
@@ -442,7 +442,7 @@ SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename,
struct filename *name __free(putname) = NULL;
unsigned int lookup_flags = 0;
struct file_attr fattr;
- struct fileattr fa;
+ struct file_kattr fa;
int error;
BUILD_BUG_ON(sizeof(struct file_attr) < FILE_ATTR_SIZE_VER0);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index b54f4f57789f..501f64ceeab3 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1486,9 +1486,9 @@ void fuse_dax_cancel_work(struct fuse_conn *fc);
long fuse_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
long fuse_file_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);
-int fuse_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+int fuse_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
int fuse_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
/* iomode.c */
int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff);
diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c
index f2692f7d5932..57032eadca6c 100644
--- a/fs/fuse/ioctl.c
+++ b/fs/fuse/ioctl.c
@@ -502,7 +502,7 @@ static void fuse_priv_ioctl_cleanup(struct inode *inode, struct fuse_file *ff)
fuse_file_release(inode, ff, O_RDONLY, NULL, S_ISDIR(inode->i_mode));
}
-int fuse_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int fuse_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct fuse_file *ff;
@@ -542,7 +542,7 @@ int fuse_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
int fuse_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct fuse_file *ff;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index fd1147aa3891..65f4371f428c 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -155,7 +155,7 @@ static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
return fsflags;
}
-int gfs2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int gfs2_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -276,7 +276,7 @@ static int do_gfs2_set_flags(struct inode *inode, u32 reqflags, u32 mask)
}
int gfs2_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
u32 fsflags = fa->flags, gfsflags = 0;
diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h
index eafe123617e6..dd970e644fe0 100644
--- a/fs/gfs2/inode.h
+++ b/fs/gfs2/inode.h
@@ -107,9 +107,9 @@ loff_t gfs2_seek_hole(struct file *file, loff_t offset);
extern const struct file_operations gfs2_file_fops_nolock;
extern const struct file_operations gfs2_dir_fops_nolock;
-int gfs2_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+int gfs2_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
int gfs2_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
void gfs2_set_inode_flags(struct inode *inode);
#ifdef CONFIG_GFS2_FS_LOCKING_DLM
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 2f089bff0095..927db2b8b17c 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -489,9 +489,9 @@ int hfsplus_getattr(struct mnt_idmap *idmap, const struct path *path,
unsigned int query_flags);
int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,
int datasync);
-int hfsplus_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+int hfsplus_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
int hfsplus_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
/* ioctl.c */
long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index f331e9574217..3ec0b33808c0 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -654,7 +654,7 @@ int hfsplus_cat_write_inode(struct inode *inode)
return res;
}
-int hfsplus_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int hfsplus_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
@@ -673,7 +673,7 @@ int hfsplus_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
int hfsplus_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index f7bd7e8f5be4..563f148be8af 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -57,7 +57,7 @@ static long jfs_map_ext2(unsigned long flags, int from)
return mapped;
}
-int jfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int jfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct jfs_inode_info *jfs_inode = JFS_IP(d_inode(dentry));
unsigned int flags = jfs_inode->mode2 & JFS_FL_USER_VISIBLE;
@@ -71,7 +71,7 @@ int jfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
int jfs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct jfs_inode_info *jfs_inode = JFS_IP(inode);
diff --git a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h
index ea80661597ac..2c6c81c8cb9f 100644
--- a/fs/jfs/jfs_inode.h
+++ b/fs/jfs/jfs_inode.h
@@ -9,9 +9,9 @@ struct fid;
extern struct inode *ialloc(struct inode *, umode_t);
extern int jfs_fsync(struct file *, loff_t, loff_t, int);
-extern int jfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+extern int jfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
extern int jfs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
extern long jfs_ioctl(struct file *, unsigned int, unsigned long);
extern struct inode *jfs_iget(struct super_block *, unsigned long);
extern int jfs_commit_inode(struct inode *, int);
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index a66d62a51f77..3288c3b4be9e 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -118,7 +118,7 @@ static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs,
*
* Return: always 0 as success.
*/
-int nilfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int nilfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
@@ -136,7 +136,7 @@ int nilfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
* Return: 0 on success, or a negative error code on failure.
*/
int nilfs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct nilfs_transaction_info ti;
diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h
index cb6ed54accd7..f466daa39440 100644
--- a/fs/nilfs2/nilfs.h
+++ b/fs/nilfs2/nilfs.h
@@ -268,9 +268,9 @@ int nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de,
extern int nilfs_sync_file(struct file *, loff_t, loff_t, int);
/* ioctl.c */
-int nilfs_fileattr_get(struct dentry *dentry, struct fileattr *m);
+int nilfs_fileattr_get(struct dentry *dentry, struct file_kattr *m);
int nilfs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
long nilfs_ioctl(struct file *, unsigned int, unsigned long);
long nilfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *, struct nilfs_argv *,
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 7ae96fb8807a..db14c92302a1 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -62,7 +62,7 @@ static inline int o2info_coherent(struct ocfs2_info_request *req)
return (!(req->ir_flags & OCFS2_INFO_FL_NON_COHERENT));
}
-int ocfs2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int ocfs2_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
unsigned int flags;
@@ -83,7 +83,7 @@ int ocfs2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
int ocfs2_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
unsigned int flags = fa->flags;
diff --git a/fs/ocfs2/ioctl.h b/fs/ocfs2/ioctl.h
index 48a5fdfe87a1..4a1c2313b429 100644
--- a/fs/ocfs2/ioctl.h
+++ b/fs/ocfs2/ioctl.h
@@ -11,9 +11,9 @@
#ifndef OCFS2_IOCTL_PROTO_H
#define OCFS2_IOCTL_PROTO_H
-int ocfs2_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+int ocfs2_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
int ocfs2_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg);
diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c
index 08a6f372a352..926d1659902d 100644
--- a/fs/orangefs/inode.c
+++ b/fs/orangefs/inode.c
@@ -887,7 +887,7 @@ int orangefs_update_time(struct inode *inode, int flags)
return __orangefs_setattr(inode, &iattr);
}
-static int orangefs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+static int orangefs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
u64 val = 0;
int ret;
@@ -908,7 +908,7 @@ static int orangefs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
static int orangefs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
u64 val = 0;
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 2c646b7076d0..74817e1ece19 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -171,8 +171,8 @@ int ovl_copy_xattr(struct super_block *sb, const struct path *oldpath, struct de
static int ovl_copy_fileattr(struct inode *inode, const struct path *old,
const struct path *new)
{
- struct fileattr oldfa = { .flags_valid = true };
- struct fileattr newfa = { .flags_valid = true };
+ struct file_kattr oldfa = { .flags_valid = true };
+ struct file_kattr newfa = { .flags_valid = true };
int err;
err = ovl_real_fileattr_get(old, &oldfa);
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index cf3581dc1034..ecb9f2019395 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -610,7 +610,7 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
* Introducing security_inode_fileattr_get/set() hooks would solve this issue
* properly.
*/
-static int ovl_security_fileattr(const struct path *realpath, struct fileattr *fa,
+static int ovl_security_fileattr(const struct path *realpath, struct file_kattr *fa,
bool set)
{
struct file *file;
@@ -637,7 +637,7 @@ static int ovl_security_fileattr(const struct path *realpath, struct fileattr *f
return err;
}
-int ovl_real_fileattr_set(const struct path *realpath, struct fileattr *fa)
+int ovl_real_fileattr_set(const struct path *realpath, struct file_kattr *fa)
{
int err;
@@ -649,7 +649,7 @@ int ovl_real_fileattr_set(const struct path *realpath, struct fileattr *fa)
}
int ovl_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct path upperpath;
@@ -697,7 +697,7 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
}
/* Convert inode protection flags to fileattr flags */
-static void ovl_fileattr_prot_flags(struct inode *inode, struct fileattr *fa)
+static void ovl_fileattr_prot_flags(struct inode *inode, struct file_kattr *fa)
{
BUILD_BUG_ON(OVL_PROT_FS_FLAGS_MASK & ~FS_COMMON_FL);
BUILD_BUG_ON(OVL_PROT_FSX_FLAGS_MASK & ~FS_XFLAG_COMMON);
@@ -712,7 +712,7 @@ static void ovl_fileattr_prot_flags(struct inode *inode, struct fileattr *fa)
}
}
-int ovl_real_fileattr_get(const struct path *realpath, struct fileattr *fa)
+int ovl_real_fileattr_get(const struct path *realpath, struct file_kattr *fa)
{
int err;
@@ -723,7 +723,7 @@ int ovl_real_fileattr_get(const struct path *realpath, struct fileattr *fa)
return vfs_fileattr_get(realpath->dentry, fa);
}
-int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int ovl_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct path realpath;
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 8baaba0a3fe5..e19d91f22186 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -815,7 +815,7 @@ void ovl_copyattr(struct inode *to);
void ovl_check_protattr(struct inode *inode, struct dentry *upper);
int ovl_set_protattr(struct inode *inode, struct dentry *upper,
- struct fileattr *fa);
+ struct file_kattr *fa);
static inline void ovl_copyflags(struct inode *from, struct inode *to)
{
@@ -847,11 +847,11 @@ struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
/* file.c */
extern const struct file_operations ovl_file_operations;
-int ovl_real_fileattr_get(const struct path *realpath, struct fileattr *fa);
-int ovl_real_fileattr_set(const struct path *realpath, struct fileattr *fa);
-int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+int ovl_real_fileattr_get(const struct path *realpath, struct file_kattr *fa);
+int ovl_real_fileattr_set(const struct path *realpath, struct file_kattr *fa);
+int ovl_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
int ovl_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
struct ovl_file;
struct ovl_file *ovl_file_alloc(struct file *realfile);
void ovl_file_free(struct ovl_file *of);
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index dcccb4b4a66c..607860f199a8 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -959,7 +959,7 @@ void ovl_check_protattr(struct inode *inode, struct dentry *upper)
}
int ovl_set_protattr(struct inode *inode, struct dentry *upper,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
struct ovl_fs *ofs = OVL_FS(inode->i_sb);
char buf[OVL_PROTATTR_MAX];
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 2c99349cf537..79536b2e3d7a 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -130,7 +130,7 @@ static int setflags(struct inode *inode, int flags)
return err;
}
-int ubifs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+int ubifs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
int flags = ubifs2ioctl(ubifs_inode(inode)->flags);
@@ -145,7 +145,7 @@ int ubifs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
int ubifs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
int flags = fa->flags;
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 256dbaeeb0de..5db45c9e26ee 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -2073,9 +2073,9 @@ int ubifs_recover_size(struct ubifs_info *c, bool in_place);
void ubifs_destroy_size_tree(struct ubifs_info *c);
/* ioctl.c */
-int ubifs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+int ubifs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
int ubifs_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
void ubifs_set_inode_flags(struct inode *inode);
#ifdef CONFIG_COMPAT
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index d250f7f74e3b..6d573e736a67 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -444,7 +444,7 @@ static void
xfs_fill_fsxattr(
struct xfs_inode *ip,
int whichfork,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
@@ -496,7 +496,7 @@ xfs_ioc_fsgetxattra(
xfs_inode_t *ip,
void __user *arg)
{
- struct fileattr fa;
+ struct file_kattr fa;
xfs_ilock(ip, XFS_ILOCK_SHARED);
xfs_fill_fsxattr(ip, XFS_ATTR_FORK, &fa);
@@ -508,7 +508,7 @@ xfs_ioc_fsgetxattra(
int
xfs_fileattr_get(
struct dentry *dentry,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
struct xfs_inode *ip = XFS_I(d_inode(dentry));
@@ -526,7 +526,7 @@ static int
xfs_ioctl_setattr_xflags(
struct xfs_trans *tp,
struct xfs_inode *ip,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
struct xfs_mount *mp = ip->i_mount;
bool rtflag = (fa->fsx_xflags & FS_XFLAG_REALTIME);
@@ -582,7 +582,7 @@ xfs_ioctl_setattr_xflags(
static void
xfs_ioctl_setattr_prepare_dax(
struct xfs_inode *ip,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
struct xfs_mount *mp = ip->i_mount;
struct inode *inode = VFS_I(ip);
@@ -642,7 +642,7 @@ xfs_ioctl_setattr_get_trans(
static int
xfs_ioctl_setattr_check_extsize(
struct xfs_inode *ip,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
struct xfs_mount *mp = ip->i_mount;
xfs_failaddr_t failaddr;
@@ -684,7 +684,7 @@ xfs_ioctl_setattr_check_extsize(
static int
xfs_ioctl_setattr_check_cowextsize(
struct xfs_inode *ip,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
struct xfs_mount *mp = ip->i_mount;
xfs_failaddr_t failaddr;
@@ -709,7 +709,7 @@ xfs_ioctl_setattr_check_cowextsize(
static int
xfs_ioctl_setattr_check_projid(
struct xfs_inode *ip,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
if (!fa->fsx_valid)
return 0;
@@ -725,7 +725,7 @@ int
xfs_fileattr_set(
struct mnt_idmap *idmap,
struct dentry *dentry,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
struct xfs_inode *ip = XFS_I(d_inode(dentry));
struct xfs_mount *mp = ip->i_mount;
diff --git a/fs/xfs/xfs_ioctl.h b/fs/xfs/xfs_ioctl.h
index 12124946f347..f5a831e9de4a 100644
--- a/fs/xfs/xfs_ioctl.h
+++ b/fs/xfs/xfs_ioctl.h
@@ -17,13 +17,13 @@ xfs_ioc_swapext(
extern int
xfs_fileattr_get(
struct dentry *dentry,
- struct fileattr *fa);
+ struct file_kattr *fa);
extern int
xfs_fileattr_set(
struct mnt_idmap *idmap,
struct dentry *dentry,
- struct fileattr *fa);
+ struct file_kattr *fa);
extern long
xfs_file_ioctl(
diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h
index e2a2f4ae242d..f89dcfad3f8f 100644
--- a/include/linux/fileattr.h
+++ b/include/linux/fileattr.h
@@ -40,7 +40,7 @@
* is handled by the VFS helpers, so filesystems are free to implement just one
* or both of these sub-interfaces.
*/
-struct fileattr {
+struct file_kattr {
u32 flags; /* flags (FS_IOC_GETFLAGS/FS_IOC_SETFLAGS) */
/* struct fsxattr: */
u32 fsx_xflags; /* xflags field value (get/set) */
@@ -53,10 +53,10 @@ struct fileattr {
bool fsx_valid:1;
};
-int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa);
+int copy_fsxattr_to_user(const struct file_kattr *fa, struct fsxattr __user *ufa);
-void fileattr_fill_xflags(struct fileattr *fa, u32 xflags);
-void fileattr_fill_flags(struct fileattr *fa, u32 flags);
+void fileattr_fill_xflags(struct file_kattr *fa, u32 xflags);
+void fileattr_fill_flags(struct file_kattr *fa, u32 flags);
/**
* fileattr_has_fsx - check for extended flags/attributes
@@ -65,16 +65,16 @@ void fileattr_fill_flags(struct fileattr *fa, u32 flags);
* Return: true if any attributes are present that are not represented in
* ->flags.
*/
-static inline bool fileattr_has_fsx(const struct fileattr *fa)
+static inline bool fileattr_has_fsx(const struct file_kattr *fa)
{
return fa->fsx_valid &&
((fa->fsx_xflags & ~FS_XFLAG_COMMON) || fa->fsx_extsize != 0 ||
fa->fsx_projid != 0 || fa->fsx_cowextsize != 0);
}
-int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
+int vfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
- struct fileattr *fa);
+ struct file_kattr *fa);
int ioctl_getflags(struct file *file, unsigned int __user *argp);
int ioctl_setflags(struct file *file, unsigned int __user *argp);
int ioctl_fsgetxattr(struct file *file, void __user *argp);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 96c7925a6551..0c58617645ea 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -80,7 +80,7 @@ struct fsnotify_mark_connector;
struct fsnotify_sb_info;
struct fs_context;
struct fs_parameter_spec;
-struct fileattr;
+struct file_kattr;
struct iomap_ops;
extern void __init inode_init(void);
@@ -2254,8 +2254,8 @@ struct inode_operations {
int (*set_acl)(struct mnt_idmap *, struct dentry *,
struct posix_acl *, int);
int (*fileattr_set)(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa);
- int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa);
+ struct dentry *dentry, struct file_kattr *fa);
+ int (*fileattr_get)(struct dentry *dentry, struct file_kattr *fa);
struct offset_ctx *(*get_offset_ctx)(struct inode *inode);
} ____cacheline_aligned;
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 9600a4350e79..fd11fffdd3c3 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -157,8 +157,8 @@ LSM_HOOK(int, 0, inode_removexattr, struct mnt_idmap *idmap,
struct dentry *dentry, const char *name)
LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
const char *name)
-LSM_HOOK(int, 0, inode_file_setattr, struct dentry *dentry, struct fileattr *fa)
-LSM_HOOK(int, 0, inode_file_getattr, struct dentry *dentry, struct fileattr *fa)
+LSM_HOOK(int, 0, inode_file_setattr, struct dentry *dentry, struct file_kattr *fa)
+LSM_HOOK(int, 0, inode_file_getattr, struct dentry *dentry, struct file_kattr *fa)
LSM_HOOK(int, 0, inode_set_acl, struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)
LSM_HOOK(void, LSM_RET_VOID, inode_post_set_acl, struct dentry *dentry,
diff --git a/include/linux/security.h b/include/linux/security.h
index 9ed0d0e0c81f..b95b5540c429 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -452,9 +452,9 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name);
void security_inode_post_removexattr(struct dentry *dentry, const char *name);
int security_inode_file_setattr(struct dentry *dentry,
- struct fileattr *fa);
+ struct file_kattr *fa);
int security_inode_file_getattr(struct dentry *dentry,
- struct fileattr *fa);
+ struct file_kattr *fa);
int security_inode_need_killpriv(struct dentry *dentry);
int security_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry);
int security_inode_getsecurity(struct mnt_idmap *idmap,
@@ -1057,13 +1057,13 @@ static inline void security_inode_post_removexattr(struct dentry *dentry,
{ }
static inline int security_inode_file_setattr(struct dentry *dentry,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
return 0;
}
static inline int security_inode_file_getattr(struct dentry *dentry,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
return 0;
}
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 9663dbdda181..6e136c9c6a22 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -151,7 +151,7 @@ struct fsxattr {
/*
* Variable size structure for file_[sg]et_attr().
*
- * Note. This is alternative to the structure 'struct fileattr'/'struct fsxattr'.
+ * Note. This is alternative to the structure 'struct file_kattr'/'struct fsxattr'.
* As this structure is passed to/from userspace with its size, this can
* be versioned based on the size.
*/
diff --git a/mm/shmem.c b/mm/shmem.c
index 0c5fb4ffa03a..6311fe35c577 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -4183,7 +4183,7 @@ static const char *shmem_get_link(struct dentry *dentry, struct inode *inode,
#ifdef CONFIG_TMPFS_XATTR
-static int shmem_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+static int shmem_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct shmem_inode_info *info = SHMEM_I(d_inode(dentry));
@@ -4193,7 +4193,7 @@ static int shmem_fileattr_get(struct dentry *dentry, struct fileattr *fa)
}
static int shmem_fileattr_set(struct mnt_idmap *idmap,
- struct dentry *dentry, struct fileattr *fa)
+ struct dentry *dentry, struct file_kattr *fa)
{
struct inode *inode = d_inode(dentry);
struct shmem_inode_info *info = SHMEM_I(inode);
diff --git a/security/security.c b/security/security.c
index 711b4de40b8d..a5766cbf6f7c 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2632,7 +2632,7 @@ void security_inode_post_removexattr(struct dentry *dentry, const char *name)
*
* Return: Returns 0 if permission is granted.
*/
-int security_inode_file_setattr(struct dentry *dentry, struct fileattr *fa)
+int security_inode_file_setattr(struct dentry *dentry, struct file_kattr *fa)
{
return call_int_hook(inode_file_setattr, dentry, fa);
}
@@ -2647,7 +2647,7 @@ int security_inode_file_setattr(struct dentry *dentry, struct fileattr *fa)
*
* Return: Returns 0 if permission is granted.
*/
-int security_inode_file_getattr(struct dentry *dentry, struct fileattr *fa)
+int security_inode_file_getattr(struct dentry *dentry, struct file_kattr *fa)
{
return call_int_hook(inode_file_getattr, dentry, fa);
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index be7aca2269fa..0dadce2267c1 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3481,13 +3481,13 @@ static int selinux_inode_removexattr(struct mnt_idmap *idmap,
}
static int selinux_inode_file_setattr(struct dentry *dentry,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
}
static int selinux_inode_file_getattr(struct dentry *dentry,
- struct fileattr *fa)
+ struct file_kattr *fa)
{
return dentry_has_perm(current_cred(), dentry, FILE__GETATTR);
}
--
2.47.2
^ permalink raw reply related [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-03 8:28 ` Christian Brauner
@ 2025-07-03 8:42 ` Amir Goldstein
2025-07-03 8:46 ` Christian Brauner
0 siblings, 1 reply; 47+ messages in thread
From: Amir Goldstein @ 2025-07-03 8:42 UTC (permalink / raw)
To: Christian Brauner
Cc: Darrick J. Wong, Jan Kara, Andrey Albershteyn, Arnd Bergmann,
Casey Schaufler, Pali Rohár, Paul Moore, linux-api,
linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Thu, Jul 3, 2025 at 10:28 AM Christian Brauner <brauner@kernel.org> wrote:
>
> On Wed, Jul 02, 2025 at 11:37:50AM -0700, Darrick J. Wong wrote:
> > On Wed, Jul 02, 2025 at 03:43:28PM +0200, Amir Goldstein wrote:
> > > On Wed, Jul 2, 2025 at 2:40 PM Christian Brauner <brauner@kernel.org> wrote:
> > > >
> > > > > Er... "fsx_fileattr" is the struct that the system call uses?
> > > > >
> > > > > That's a little confusing considering that xfs already has a
> > > > > xfs_fill_fsxattr function that actually fills a struct fileattr.
> > > > > That could be renamed xfs_fill_fileattr.
> > > > >
> > > > > I dunno. There's a part of me that would really rather that the
> > > > > file_getattr and file_setattr syscalls operate on a struct file_attr.
> > > >
> > > > Agreed, I'm pretty sure I suggested this during an earlier review. Fits
> > > > in line with struct mount_attr and others. Fwiw, struct fileattr (the
> > > > kernel internal thing) should've really been struct file_kattr or struct
> > > > kernel_file_attr. This is a common pattern now:
> > > >
> > > > struct mount_attr vs struct mount_kattr
> > > >
> > > > struct clone_args vs struct kernel_clone_kargs
> > > >
> > > > etc.
> > > >file_attr
> > >
> > > I can see the allure, but we have a long history here with fsxattr,
> > > so I think it serves the users better to reference this history with
> > > fsxattr64.
> >
> > <shrug> XFS has a long history with 'struct fsxattr' (the structure you
> > passed to XFS_IOC_FSGETXATTR) but the rest of the kernel needn't be so
> > fixated upon the historical name. ext4/f2fs/overlay afaict are just
> > going along for the ride.
> >
> > IOWs I like brauner's struct file_attr and struct file_kattr
> > suggestions.
> >
> > > That, and also, avoid the churn of s/fileattr/file_kattr/
> > > If you want to do this renaming, please do it in the same PR
> > > because I don't like the idea of having both file_attr and fileattr
> > > in the tree for an unknown period.
> >
> > But yeah, that ought to be a treewide change done at the same time.
>
> Why do you all hate me? ;)
> See the appended patch.
This looks obviously fine, but I wonder how much conflicts that would
cause in linux-next?
It may just be small enough to get by.
Thanks,
Amir.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-03 8:42 ` Amir Goldstein
@ 2025-07-03 8:46 ` Christian Brauner
2025-07-03 22:35 ` Darrick J. Wong
0 siblings, 1 reply; 47+ messages in thread
From: Christian Brauner @ 2025-07-03 8:46 UTC (permalink / raw)
To: Amir Goldstein
Cc: Darrick J. Wong, Jan Kara, Andrey Albershteyn, Arnd Bergmann,
Casey Schaufler, Pali Rohár, Paul Moore, linux-api,
linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Thu, Jul 03, 2025 at 10:42:27AM +0200, Amir Goldstein wrote:
> On Thu, Jul 3, 2025 at 10:28 AM Christian Brauner <brauner@kernel.org> wrote:
> >
> > On Wed, Jul 02, 2025 at 11:37:50AM -0700, Darrick J. Wong wrote:
> > > On Wed, Jul 02, 2025 at 03:43:28PM +0200, Amir Goldstein wrote:
> > > > On Wed, Jul 2, 2025 at 2:40 PM Christian Brauner <brauner@kernel.org> wrote:
> > > > >
> > > > > > Er... "fsx_fileattr" is the struct that the system call uses?
> > > > > >
> > > > > > That's a little confusing considering that xfs already has a
> > > > > > xfs_fill_fsxattr function that actually fills a struct fileattr.
> > > > > > That could be renamed xfs_fill_fileattr.
> > > > > >
> > > > > > I dunno. There's a part of me that would really rather that the
> > > > > > file_getattr and file_setattr syscalls operate on a struct file_attr.
> > > > >
> > > > > Agreed, I'm pretty sure I suggested this during an earlier review. Fits
> > > > > in line with struct mount_attr and others. Fwiw, struct fileattr (the
> > > > > kernel internal thing) should've really been struct file_kattr or struct
> > > > > kernel_file_attr. This is a common pattern now:
> > > > >
> > > > > struct mount_attr vs struct mount_kattr
> > > > >
> > > > > struct clone_args vs struct kernel_clone_kargs
> > > > >
> > > > > etc.
> > > > >file_attr
> > > >
> > > > I can see the allure, but we have a long history here with fsxattr,
> > > > so I think it serves the users better to reference this history with
> > > > fsxattr64.
> > >
> > > <shrug> XFS has a long history with 'struct fsxattr' (the structure you
> > > passed to XFS_IOC_FSGETXATTR) but the rest of the kernel needn't be so
> > > fixated upon the historical name. ext4/f2fs/overlay afaict are just
> > > going along for the ride.
> > >
> > > IOWs I like brauner's struct file_attr and struct file_kattr
> > > suggestions.
> > >
> > > > That, and also, avoid the churn of s/fileattr/file_kattr/
> > > > If you want to do this renaming, please do it in the same PR
> > > > because I don't like the idea of having both file_attr and fileattr
> > > > in the tree for an unknown period.
> > >
> > > But yeah, that ought to be a treewide change done at the same time.
> >
> > Why do you all hate me? ;)
> > See the appended patch.
>
> This looks obviously fine, but I wonder how much conflicts that would
> cause in linux-next?
> It may just be small enough to get by.
With such changes that's always a possibility but really I'll just
provide a branch with the resolutions for Linus to pull.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-03 8:46 ` Christian Brauner
@ 2025-07-03 22:35 ` Darrick J. Wong
0 siblings, 0 replies; 47+ messages in thread
From: Darrick J. Wong @ 2025-07-03 22:35 UTC (permalink / raw)
To: Christian Brauner
Cc: Amir Goldstein, Jan Kara, Andrey Albershteyn, Arnd Bergmann,
Casey Schaufler, Pali Rohár, Paul Moore, linux-api,
linux-fsdevel, linux-kernel, linux-xfs, selinux,
Andrey Albershteyn
On Thu, Jul 03, 2025 at 10:46:30AM +0200, Christian Brauner wrote:
> On Thu, Jul 03, 2025 at 10:42:27AM +0200, Amir Goldstein wrote:
> > On Thu, Jul 3, 2025 at 10:28 AM Christian Brauner <brauner@kernel.org> wrote:
> > >
> > > On Wed, Jul 02, 2025 at 11:37:50AM -0700, Darrick J. Wong wrote:
> > > > On Wed, Jul 02, 2025 at 03:43:28PM +0200, Amir Goldstein wrote:
> > > > > On Wed, Jul 2, 2025 at 2:40 PM Christian Brauner <brauner@kernel.org> wrote:
> > > > > >
> > > > > > > Er... "fsx_fileattr" is the struct that the system call uses?
> > > > > > >
> > > > > > > That's a little confusing considering that xfs already has a
> > > > > > > xfs_fill_fsxattr function that actually fills a struct fileattr.
> > > > > > > That could be renamed xfs_fill_fileattr.
> > > > > > >
> > > > > > > I dunno. There's a part of me that would really rather that the
> > > > > > > file_getattr and file_setattr syscalls operate on a struct file_attr.
> > > > > >
> > > > > > Agreed, I'm pretty sure I suggested this during an earlier review. Fits
> > > > > > in line with struct mount_attr and others. Fwiw, struct fileattr (the
> > > > > > kernel internal thing) should've really been struct file_kattr or struct
> > > > > > kernel_file_attr. This is a common pattern now:
> > > > > >
> > > > > > struct mount_attr vs struct mount_kattr
> > > > > >
> > > > > > struct clone_args vs struct kernel_clone_kargs
> > > > > >
> > > > > > etc.
> > > > > >file_attr
> > > > >
> > > > > I can see the allure, but we have a long history here with fsxattr,
> > > > > so I think it serves the users better to reference this history with
> > > > > fsxattr64.
> > > >
> > > > <shrug> XFS has a long history with 'struct fsxattr' (the structure you
> > > > passed to XFS_IOC_FSGETXATTR) but the rest of the kernel needn't be so
> > > > fixated upon the historical name. ext4/f2fs/overlay afaict are just
> > > > going along for the ride.
> > > >
> > > > IOWs I like brauner's struct file_attr and struct file_kattr
> > > > suggestions.
> > > >
> > > > > That, and also, avoid the churn of s/fileattr/file_kattr/
> > > > > If you want to do this renaming, please do it in the same PR
> > > > > because I don't like the idea of having both file_attr and fileattr
> > > > > in the tree for an unknown period.
> > > >
> > > > But yeah, that ought to be a treewide change done at the same time.
> > >
> > > Why do you all hate me? ;)
> > > See the appended patch.
> >
> > This looks obviously fine, but I wonder how much conflicts that would
> > cause in linux-next?
> > It may just be small enough to get by.
>
> With such changes that's always a possibility but really I'll just
> provide a branch with the resolutions for Linus to pull.
<nod> That looks good to me. :)
At worst you can always ask Linus "Hey I want to do a treewide name
change of $X to $Y, can I stuff that in at the very end of the merge
window?" and IME he'll let you do that. Even better if someone keeps
him supplied with fresh change patches.
--D
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-01 12:29 ` Christian Brauner
@ 2025-07-07 12:05 ` Andrey Albershteyn
2025-07-07 12:19 ` Christian Brauner
0 siblings, 1 reply; 47+ messages in thread
From: Andrey Albershteyn @ 2025-07-07 12:05 UTC (permalink / raw)
To: Christian Brauner
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Jan Kara,
Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On 2025-07-01 14:29:42, Christian Brauner wrote:
> On Mon, Jun 30, 2025 at 06:20:10PM +0200, Andrey Albershteyn wrote:
> > This patchset introduced two new syscalls file_getattr() and
> > file_setattr(). These syscalls are similar to FS_IOC_FSSETXATTR ioctl()
> > except they use *at() semantics. Therefore, there's no need to open the
> > file to get a fd.
> >
> > These syscalls allow userspace to set filesystem inode attributes on
> > special files. One of the usage examples is XFS quota projects.
> >
> > XFS has project quotas which could be attached to a directory. All
> > new inodes in these directories inherit project ID set on parent
> > directory.
> >
> > The project is created from userspace by opening and calling
> > FS_IOC_FSSETXATTR on each inode. This is not possible for special
> > files such as FIFO, SOCK, BLK etc. Therefore, some inodes are left
> > with empty project ID. Those inodes then are not shown in the quota
> > accounting but still exist in the directory. This is not critical but in
> > the case when special files are created in the directory with already
> > existing project quota, these new inodes inherit extended attributes.
> > This creates a mix of special files with and without attributes.
> > Moreover, special files with attributes don't have a possibility to
> > become clear or change the attributes. This, in turn, prevents userspace
> > from re-creating quota project on these existing files.
>
> Only small nits I'm going to comment on that I can fix myself.
> Otherwise looks great.
>
Hi Christian,
Let me know if you would like a new revision with all the comments
included (and your patch on file_kattr rename) or you good with
applying them while commit
--
- Andrey
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-07 12:05 ` Andrey Albershteyn
@ 2025-07-07 12:19 ` Christian Brauner
2025-07-07 12:27 ` Andrey Albershteyn
0 siblings, 1 reply; 47+ messages in thread
From: Christian Brauner @ 2025-07-07 12:19 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Jan Kara,
Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On Mon, Jul 07, 2025 at 02:05:10PM +0200, Andrey Albershteyn wrote:
> On 2025-07-01 14:29:42, Christian Brauner wrote:
> > On Mon, Jun 30, 2025 at 06:20:10PM +0200, Andrey Albershteyn wrote:
> > > This patchset introduced two new syscalls file_getattr() and
> > > file_setattr(). These syscalls are similar to FS_IOC_FSSETXATTR ioctl()
> > > except they use *at() semantics. Therefore, there's no need to open the
> > > file to get a fd.
> > >
> > > These syscalls allow userspace to set filesystem inode attributes on
> > > special files. One of the usage examples is XFS quota projects.
> > >
> > > XFS has project quotas which could be attached to a directory. All
> > > new inodes in these directories inherit project ID set on parent
> > > directory.
> > >
> > > The project is created from userspace by opening and calling
> > > FS_IOC_FSSETXATTR on each inode. This is not possible for special
> > > files such as FIFO, SOCK, BLK etc. Therefore, some inodes are left
> > > with empty project ID. Those inodes then are not shown in the quota
> > > accounting but still exist in the directory. This is not critical but in
> > > the case when special files are created in the directory with already
> > > existing project quota, these new inodes inherit extended attributes.
> > > This creates a mix of special files with and without attributes.
> > > Moreover, special files with attributes don't have a possibility to
> > > become clear or change the attributes. This, in turn, prevents userspace
> > > from re-creating quota project on these existing files.
> >
> > Only small nits I'm going to comment on that I can fix myself.
> > Otherwise looks great.
> >
>
> Hi Christian,
>
> Let me know if you would like a new revision with all the comments
> included (and your patch on file_kattr rename) or you good with
> applying them while commit
It's all been in -next for a few days already. :)
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls
2025-06-30 16:20 [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
` (7 preceding siblings ...)
2025-07-01 12:29 ` Christian Brauner
@ 2025-07-07 12:19 ` Christian Brauner
8 siblings, 0 replies; 47+ messages in thread
From: Christian Brauner @ 2025-07-07 12:19 UTC (permalink / raw)
To: Andrey Albershteyn
Cc: Christian Brauner, linux-api, linux-fsdevel, linux-kernel,
linux-xfs, selinux, Andrey Albershteyn, Amir Goldstein,
Arnd Bergmann, Casey Schaufler, Jan Kara, Pali Rohár,
Paul Moore
On Mon, 30 Jun 2025 18:20:10 +0200, Andrey Albershteyn wrote:
> This patchset introduced two new syscalls file_getattr() and
> file_setattr(). These syscalls are similar to FS_IOC_FSSETXATTR ioctl()
> except they use *at() semantics. Therefore, there's no need to open the
> file to get a fd.
>
> These syscalls allow userspace to set filesystem inode attributes on
> special files. One of the usage examples is XFS quota projects.
>
> [...]
Applied to the vfs-6.17.fileattr branch of the vfs/vfs.git tree.
Patches in the vfs-6.17.fileattr branch should appear in linux-next soon.
Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.
It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.
Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.
tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs-6.17.fileattr
[1/6] fs: split fileattr related helpers into separate file
https://git.kernel.org/vfs/vfs/c/2f952c9e8fe1
[2/6] lsm: introduce new hooks for setting/getting inode fsxattr
https://git.kernel.org/vfs/vfs/c/defdd02d783c
[3/6] selinux: implement inode_file_[g|s]etattr hooks
https://git.kernel.org/vfs/vfs/c/bd14e462bb52
[4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP
https://git.kernel.org/vfs/vfs/c/474b155adf39
[5/6] fs: prepare for extending file_get/setattr()
https://git.kernel.org/vfs/vfs/c/276e136bff7e
[6/6] fs: introduce file_getattr and file_setattr syscalls
https://git.kernel.org/vfs/vfs/c/be7efb2d20d6
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls
2025-07-07 12:19 ` Christian Brauner
@ 2025-07-07 12:27 ` Andrey Albershteyn
0 siblings, 0 replies; 47+ messages in thread
From: Andrey Albershteyn @ 2025-07-07 12:27 UTC (permalink / raw)
To: Christian Brauner
Cc: Amir Goldstein, Arnd Bergmann, Casey Schaufler, Jan Kara,
Pali Rohár, Paul Moore, linux-api, linux-fsdevel,
linux-kernel, linux-xfs, selinux, Andrey Albershteyn
On 2025-07-07 14:19:25, Christian Brauner wrote:
> On Mon, Jul 07, 2025 at 02:05:10PM +0200, Andrey Albershteyn wrote:
> > On 2025-07-01 14:29:42, Christian Brauner wrote:
> > > On Mon, Jun 30, 2025 at 06:20:10PM +0200, Andrey Albershteyn wrote:
> > > > This patchset introduced two new syscalls file_getattr() and
> > > > file_setattr(). These syscalls are similar to FS_IOC_FSSETXATTR ioctl()
> > > > except they use *at() semantics. Therefore, there's no need to open the
> > > > file to get a fd.
> > > >
> > > > These syscalls allow userspace to set filesystem inode attributes on
> > > > special files. One of the usage examples is XFS quota projects.
> > > >
> > > > XFS has project quotas which could be attached to a directory. All
> > > > new inodes in these directories inherit project ID set on parent
> > > > directory.
> > > >
> > > > The project is created from userspace by opening and calling
> > > > FS_IOC_FSSETXATTR on each inode. This is not possible for special
> > > > files such as FIFO, SOCK, BLK etc. Therefore, some inodes are left
> > > > with empty project ID. Those inodes then are not shown in the quota
> > > > accounting but still exist in the directory. This is not critical but in
> > > > the case when special files are created in the directory with already
> > > > existing project quota, these new inodes inherit extended attributes.
> > > > This creates a mix of special files with and without attributes.
> > > > Moreover, special files with attributes don't have a possibility to
> > > > become clear or change the attributes. This, in turn, prevents userspace
> > > > from re-creating quota project on these existing files.
> > >
> > > Only small nits I'm going to comment on that I can fix myself.
> > > Otherwise looks great.
> > >
> >
> > Hi Christian,
> >
> > Let me know if you would like a new revision with all the comments
> > included (and your patch on file_kattr rename) or you good with
> > applying them while commit
>
> It's all been in -next for a few days already. :)
>
Oh sorry, missed that, thanks!
--
- Andrey
^ permalink raw reply [flat|nested] 47+ messages in thread
end of thread, other threads:[~2025-07-07 12:27 UTC | newest]
Thread overview: 47+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-30 16:20 [PATCH v6 0/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
2025-06-30 16:20 ` [PATCH v6 1/6] fs: split fileattr related helpers into separate file Andrey Albershteyn
2025-07-01 5:39 ` Amir Goldstein
2025-07-01 12:38 ` Jan Kara
2025-07-01 18:13 ` Darrick J. Wong
2025-06-30 16:20 ` [PATCH v6 2/6] lsm: introduce new hooks for setting/getting inode fsxattr Andrey Albershteyn
2025-07-01 12:39 ` Jan Kara
2025-07-01 18:18 ` Darrick J. Wong
2025-07-02 8:47 ` Andrey Albershteyn
2025-06-30 16:20 ` [PATCH v6 3/6] selinux: implement inode_file_[g|s]etattr hooks Andrey Albershteyn
2025-06-30 16:20 ` [PATCH v6 4/6] fs: make vfs_fileattr_[get|set] return -EOPNOSUPP Andrey Albershteyn
2025-06-30 18:05 ` Pali Rohár
2025-07-01 6:05 ` Amir Goldstein
2025-07-01 12:51 ` Jan Kara
2025-07-01 14:16 ` Amir Goldstein
2025-07-01 12:52 ` Jan Kara
2025-07-01 18:18 ` Darrick J. Wong
2025-06-30 16:20 ` [PATCH v6 5/6] fs: prepare for extending file_get/setattr() Andrey Albershteyn
2025-07-01 13:06 ` Jan Kara
2025-07-01 18:31 ` Darrick J. Wong
2025-07-01 19:27 ` Amir Goldstein
2025-07-01 19:40 ` Darrick J. Wong
2025-07-01 19:54 ` Pali Rohár
2025-07-02 7:03 ` Amir Goldstein
2025-07-02 9:48 ` Amir Goldstein
2025-07-02 12:24 ` Christian Brauner
2025-06-30 16:20 ` [PATCH v6 6/6] fs: introduce file_getattr and file_setattr syscalls Andrey Albershteyn
2025-07-01 12:34 ` Christian Brauner
2025-07-02 9:13 ` Amir Goldstein
2025-07-01 13:24 ` Jan Kara
2025-07-01 18:43 ` Darrick J. Wong
2025-07-01 18:54 ` Pali Rohár
2025-07-01 19:08 ` Darrick J. Wong
2025-07-01 19:17 ` Pali Rohár
2025-07-02 12:40 ` Christian Brauner
2025-07-02 13:43 ` Amir Goldstein
2025-07-02 18:37 ` Darrick J. Wong
2025-07-03 8:28 ` Christian Brauner
2025-07-03 8:42 ` Amir Goldstein
2025-07-03 8:46 ` Christian Brauner
2025-07-03 22:35 ` Darrick J. Wong
2025-07-01 6:11 ` [PATCH v6 0/6] " Amir Goldstein
2025-07-01 12:29 ` Christian Brauner
2025-07-07 12:05 ` Andrey Albershteyn
2025-07-07 12:19 ` Christian Brauner
2025-07-07 12:27 ` Andrey Albershteyn
2025-07-07 12:19 ` Christian Brauner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).