From: Andrey Albershteyn <aalbersh@redhat.com>
To: fstests@vger.kernel.org
Cc: zlang@redhat.com, linux-fsdevel@vger.kernel.org,
linux-xfs@vger.kernel.org,
Andrey Albershteyn <aalbersh@kernel.org>
Subject: [PATCH v2 1/3] file_attr: introduce program to set/get fsxattr
Date: Wed, 27 Aug 2025 17:16:15 +0200 [thread overview]
Message-ID: <20250827-xattrat-syscall-v2-1-ba489b5bc17a@kernel.org> (raw)
In-Reply-To: <20250827-xattrat-syscall-v2-0-ba489b5bc17a@kernel.org>
This programs uses newly introduced file_getattr and file_setattr
syscalls. This program is partially a test of invalid options. This will
be used further in the test.
Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org>
---
.gitignore | 1 +
configure.ac | 1 +
include/builddefs.in | 1 +
m4/package_libcdev.m4 | 16 +++
src/Makefile | 5 +
src/file_attr.c | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 292 insertions(+)
diff --git a/.gitignore b/.gitignore
index 6948fd602f95..82c57f415301 100644
--- a/.gitignore
+++ b/.gitignore
@@ -211,6 +211,7 @@ tags
/src/min_dio_alignment
/src/dio-writeback-race
/src/unlink-fsync
+/src/file_attr
# Symlinked files
/tests/generic/035.out
diff --git a/configure.ac b/configure.ac
index f3c8c643f0eb..6fe54e8e1d54 100644
--- a/configure.ac
+++ b/configure.ac
@@ -73,6 +73,7 @@ AC_HAVE_RLIMIT_NOFILE
AC_NEED_INTERNAL_XFS_IOC_EXCHANGE_RANGE
AC_HAVE_FICLONE
AC_HAVE_TRIVIAL_AUTO_VAR_INIT
+AC_HAVE_FILE_ATTR
AC_CHECK_FUNCS([renameat2])
AC_CHECK_FUNCS([reallocarray])
diff --git a/include/builddefs.in b/include/builddefs.in
index 96d5ed25b3e2..821237339cc7 100644
--- a/include/builddefs.in
+++ b/include/builddefs.in
@@ -74,6 +74,7 @@ HAVE_BMV_OF_SHARED = @have_bmv_of_shared@
HAVE_RLIMIT_NOFILE = @have_rlimit_nofile@
NEED_INTERNAL_XFS_IOC_EXCHANGE_RANGE = @need_internal_xfs_ioc_exchange_range@
HAVE_FICLONE = @have_ficlone@
+HAVE_FILE_ATTR = @have_file_attr@
GCCFLAGS = -std=gnu11 -funsigned-char -fno-strict-aliasing -Wall
SANITIZER_CFLAGS += @autovar_init_cflags@
diff --git a/m4/package_libcdev.m4 b/m4/package_libcdev.m4
index ed8fe6e32ae0..e68a70f7d87e 100644
--- a/m4/package_libcdev.m4
+++ b/m4/package_libcdev.m4
@@ -86,3 +86,19 @@ AC_DEFUN([AC_HAVE_TRIVIAL_AUTO_VAR_INIT],
CFLAGS="${OLD_CFLAGS}"
AC_SUBST(autovar_init_cflags)
])
+
+#
+# Check if we have a file_getattr/file_setattr system call (Linux)
+#
+AC_DEFUN([AC_HAVE_FILE_ATTR],
+ [ AC_MSG_CHECKING([for file_getattr/file_setattr syscalls])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#define _GNU_SOURCE
+#include <sys/syscall.h>
+#include <unistd.h>
+ ]], [[
+ syscall(__NR_file_getattr, 0, 0, 0, 0, 0, 0);
+ ]])],[have_file_attr=yes
+ AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+ AC_SUBST(have_file_attr)
+ ])
diff --git a/src/Makefile b/src/Makefile
index 7080e34896c3..db4223156fc2 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -62,6 +62,11 @@ ifeq ($(HAVE_FALLOCATE), true)
LCFLAGS += -DHAVE_FALLOCATE
endif
+ifeq ($(HAVE_FILE_ATTR), yes)
+LINUX_TARGETS += file_attr
+LCFLAGS += -DHAVE_FILE_ATTR
+endif
+
ifeq ($(PKG_PLATFORM),linux)
TARGETS += $(LINUX_TARGETS)
endif
diff --git a/src/file_attr.c b/src/file_attr.c
new file mode 100644
index 000000000000..9baf0e9a12ee
--- /dev/null
+++ b/src/file_attr.c
@@ -0,0 +1,268 @@
+#include "global.h"
+#include <sys/syscall.h>
+#include <getopt.h>
+#include <errno.h>
+#include <linux/fs.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifndef HAVE_FILE_ATTR
+#define __NR_file_getattr 468
+#define __NR_file_setattr 469
+
+struct file_attr {
+ __u32 fa_xflags; /* xflags field value (get/set) */
+ __u32 fa_extsize; /* extsize field value (get/set)*/
+ __u32 fa_nextents; /* nextents field value (get) */
+ __u32 fa_projid; /* project identifier (get/set) */
+ __u32 fa_cowextsize; /* CoW extsize field value (get/set) */
+};
+
+#endif
+
+#define SPECIAL_FILE(x) \
+ (S_ISCHR((x)) \
+ || S_ISBLK((x)) \
+ || S_ISFIFO((x)) \
+ || S_ISLNK((x)) \
+ || S_ISSOCK((x)))
+
+static struct option long_options[] = {
+ {"set", no_argument, 0, 's' },
+ {"get", no_argument, 0, 'g' },
+ {"no-follow", no_argument, 0, 'n' },
+ {"at-cwd", no_argument, 0, 'a' },
+ {"set-nodump", no_argument, 0, 'd' },
+ {"invalid-at", no_argument, 0, 'i' },
+ {"too-big-arg", no_argument, 0, 'b' },
+ {"too-small-arg", no_argument, 0, 'm' },
+ {"new-fsx-flag", no_argument, 0, 'x' },
+ {0, 0, 0, 0 }
+};
+
+static struct xflags {
+ uint flag;
+ char *shortname;
+ char *longname;
+} xflags[] = {
+ { FS_XFLAG_REALTIME, "r", "realtime" },
+ { FS_XFLAG_PREALLOC, "p", "prealloc" },
+ { FS_XFLAG_IMMUTABLE, "i", "immutable" },
+ { FS_XFLAG_APPEND, "a", "append-only" },
+ { FS_XFLAG_SYNC, "s", "sync" },
+ { FS_XFLAG_NOATIME, "A", "no-atime" },
+ { FS_XFLAG_NODUMP, "d", "no-dump" },
+ { FS_XFLAG_RTINHERIT, "t", "rt-inherit" },
+ { FS_XFLAG_PROJINHERIT, "P", "proj-inherit" },
+ { FS_XFLAG_NOSYMLINKS, "n", "nosymlinks" },
+ { FS_XFLAG_EXTSIZE, "e", "extsize" },
+ { FS_XFLAG_EXTSZINHERIT, "E", "extsz-inherit" },
+ { FS_XFLAG_NODEFRAG, "f", "no-defrag" },
+ { FS_XFLAG_FILESTREAM, "S", "filestream" },
+ { FS_XFLAG_DAX, "x", "dax" },
+ { FS_XFLAG_COWEXTSIZE, "C", "cowextsize" },
+ { FS_XFLAG_HASATTR, "X", "has-xattr" },
+ { 0, NULL, NULL }
+};
+
+static int
+file_getattr(
+ int dfd,
+ const char *filename,
+ struct file_attr *fsx,
+ size_t usize,
+ unsigned int at_flags)
+{
+ return syscall(__NR_file_getattr, dfd, filename, fsx, usize, at_flags);
+}
+
+static int
+file_setattr(
+ int dfd,
+ const char *filename,
+ struct file_attr *fsx,
+ size_t usize,
+ unsigned int at_flags)
+{
+ return syscall(__NR_file_setattr, dfd, filename, fsx, usize, at_flags);
+}
+
+static void
+print_xflags(
+ uint flags,
+ int verbose,
+ int dofname,
+ const char *fname,
+ int dobraces,
+ int doeol)
+{
+ struct xflags *p;
+ int first = 1;
+
+ if (dobraces)
+ fputs("[", stdout);
+ for (p = xflags; p->flag; p++) {
+ if (flags & p->flag) {
+ if (verbose) {
+ if (first)
+ first = 0;
+ else
+ fputs(", ", stdout);
+ fputs(p->longname, stdout);
+ } else {
+ fputs(p->shortname, stdout);
+ }
+ } else if (!verbose) {
+ fputs("-", stdout);
+ }
+ }
+ if (dobraces)
+ fputs("]", stdout);
+ if (dofname)
+ printf(" %s ", fname);
+ if (doeol)
+ fputs("\n", stdout);
+}
+
+int main(int argc, char *argv[])
+{
+ int error;
+ int c;
+ const char *path = NULL;
+ const char *path1 = NULL;
+ const char *path2 = NULL;
+ unsigned int at_flags = 0;
+ unsigned int fa_xflags = 0;
+ int action = 0; /* 0 get; 1 set */
+ struct file_attr fsx = { };
+ int fa_size = sizeof(struct file_attr);
+ struct stat status;
+ int fd;
+ int at_fdcwd = 0;
+ int unknwon_fa_flag = 0;
+
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long_only(argc, argv, "", long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 's':
+ action = 1;
+ break;
+ case 'g':
+ action = 0;
+ break;
+ case 'n':
+ at_flags |= AT_SYMLINK_NOFOLLOW;
+ break;
+ case 'a':
+ at_fdcwd = 1;
+ break;
+ case 'd':
+ fa_xflags |= FS_XFLAG_NODUMP;
+ break;
+ case 'i':
+ at_flags |= (1 << 25);
+ break;
+ case 'b':
+ fa_size = getpagesize() + 1; /* max size if page size */
+ break;
+ case 'm':
+ fa_size = 19; /* VER0 size of fsxattr is 20 */
+ break;
+ case 'x':
+ unknwon_fa_flag = (1 << 27);
+ break;
+ default:
+ goto usage;
+ }
+ }
+
+ if (!path1 && optind < argc)
+ path1 = argv[optind++];
+ if (!path2 && optind < argc)
+ path2 = argv[optind++];
+
+ if (at_fdcwd) {
+ fd = AT_FDCWD;
+ path = path1;
+ } else if (!path2) {
+ error = stat(path1, &status);
+ if (error) {
+ fprintf(stderr,
+"Can not get file status of %s: %s\n", path1, strerror(errno));
+ return error;
+ }
+
+ if (SPECIAL_FILE(status.st_mode)) {
+ fprintf(stderr,
+"Can not open special file %s without parent dir: %s\n", path1, strerror(errno));
+ return errno;
+ }
+
+ fd = open(path1, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "Can not open %s: %s\n", path1,
+ strerror(errno));
+ return errno;
+ }
+ } else {
+ fd = open(path1, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "Can not open %s: %s\n", path1,
+ strerror(errno));
+ return errno;
+ }
+ path = path2;
+ }
+
+ if (!path)
+ at_flags |= AT_EMPTY_PATH;
+
+ error = file_getattr(fd, path, &fsx, fa_size,
+ at_flags);
+ if (error) {
+ fprintf(stderr, "Can not get fsxattr on %s: %s\n", path,
+ strerror(errno));
+ return error;
+ }
+ if (action) {
+ fsx.fa_xflags |= (fa_xflags | unknwon_fa_flag);
+
+ error = file_setattr(fd, path, &fsx, fa_size,
+ at_flags);
+ if (error) {
+ fprintf(stderr, "Can not set fsxattr on %s: %s\n", path,
+ strerror(errno));
+ return error;
+ }
+ } else {
+ if (path2)
+ print_xflags(fsx.fa_xflags, 0, 1, path, 0, 1);
+ else
+ print_xflags(fsx.fa_xflags, 0, 1, path1, 0, 1);
+ }
+
+ return error;
+
+usage:
+ printf("Usage: %s [options]\n", argv[0]);
+ printf("Options:\n");
+ printf("\t--get, -g\t\tget filesystem inode attributes\n");
+ printf("\t--set, -s\t\tset filesystem inode attributes\n");
+ printf("\t--at-cwd, -a\t\topen file at current working directory\n");
+ printf("\t--no-follow, -n\t\tdon't follow symlinks\n");
+ printf("\t--set-nodump, -d\t\tset FS_XFLAG_NODUMP on an inode\n");
+ printf("\t--invalid-at, -i\t\tUse invalida AT_* flag\n");
+ printf("\t--too-big-arg, -b\t\tSet fsxattr size bigger than PAGE_SIZE\n");
+ printf("\t--too-small-arg, -m\t\tSet fsxattr size to 19 bytes\n");
+ printf("\t--new-fsx-flag, -x\t\tUse unknown fa_flags flag\n");
+
+ return 1;
+}
--
2.49.0
next prev parent reply other threads:[~2025-08-27 15:16 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-27 15:14 Tests for file_getattr()/file_setattr() and xfsprogs update Andrey Albershteyn
2025-08-27 15:15 ` [PATCH v2 0/4] xfsprogs: utilize file_getattr() and file_setattr() Andrey Albershteyn
2025-08-27 15:15 ` [PATCH v2 1/4] libfrog: add wrappers for file_getattr/file_setattr syscalls Andrey Albershteyn
2025-08-28 14:34 ` Darrick J. Wong
2025-08-27 15:15 ` [PATCH v2 2/4] xfs_quota: utilize file_setattr to set prjid on special files Andrey Albershteyn
2025-08-28 14:39 ` Darrick J. Wong
2025-08-29 15:42 ` Andrey Albershteyn
2025-08-27 15:15 ` [PATCH v2 3/4] xfs_io: make ls/chattr work with " Andrey Albershteyn
2025-08-28 14:40 ` Darrick J. Wong
2025-08-27 15:15 ` [PATCH v2 4/4] xfs_db: use file_setattr to copy attributes on special files with rdump Andrey Albershteyn
2025-08-28 14:41 ` Darrick J. Wong
2025-08-27 15:16 ` [PATCH v2 0/3] Test file_getattr and file_setattr syscalls Andrey Albershteyn
2025-08-27 15:16 ` Andrey Albershteyn [this message]
2025-08-28 14:49 ` [PATCH v2 1/3] file_attr: introduce program to set/get fsxattr Darrick J. Wong
2025-08-27 15:16 ` [PATCH v2 2/3] generic: introduce test to test file_getattr/file_setattr syscalls Andrey Albershteyn
2025-08-28 14:50 ` Darrick J. Wong
2025-08-27 15:16 ` [PATCH v2 3/3] xfs: test quota's project ID on special files Andrey Albershteyn
2025-08-28 14:51 ` Darrick J. Wong
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250827-xattrat-syscall-v2-1-ba489b5bc17a@kernel.org \
--to=aalbersh@redhat.com \
--cc=aalbersh@kernel.org \
--cc=fstests@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-xfs@vger.kernel.org \
--cc=zlang@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).