@@ -212,6 +212,7 @@ tags
/src/fiemap-fault
/src/min_dio_alignment
/src/dio-writeback-race
+/src/fsxattrat
# Symlinked files
/tests/generic/035.out
@@ -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_FSXATTRAT
AC_CHECK_FUNCS([renameat2])
AC_CHECK_FUNCS([reallocarray])
@@ -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_FSXATTRAT = @have_fsxattrat@
GCCFLAGS = -std=gnu11 -funsigned-char -fno-strict-aliasing -Wall
SANITIZER_CFLAGS += @autovar_init_cflags@
@@ -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 getfsxattrat/setfsxattrat system call (Linux)
+#
+AC_DEFUN([AC_HAVE_FSXATTRAT],
+ [ AC_MSG_CHECKING([for getfsxattrat/setfsxattrat])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#define _GNU_SOURCE
+#include <sys/syscall.h>
+#include <unistd.h>
+ ]], [[
+ syscall(__NR_getfsxattrat, 0, 0, 0, 0, 0, 0);
+ ]])],[have_fsxattrat=yes
+ AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+ AC_SUBST(have_fsxattrat)
+ ])
@@ -61,6 +61,10 @@ ifeq ($(HAVE_FALLOCATE), true)
LCFLAGS += -DHAVE_FALLOCATE
endif
+ifeq ($(HAVE_FSXATTRAT), yes)
+LINUX_TARGETS += fsxattrat
+endif
+
ifeq ($(PKG_PLATFORM),linux)
TARGETS += $(LINUX_TARGETS)
endif
new file mode 100644
@@ -0,0 +1,246 @@
+#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>
+
+#if !defined(SYS_getfsxattrat) && defined(__x86_64__)
+#define SYS_getfsxattrat 467
+#define SYS_setfsxattrat 468
+#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' },
+ {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
+getfsxattrat(
+ int dfd,
+ const char *filename,
+ struct fsxattr *fsx,
+ size_t usize,
+ unsigned int at_flags)
+{
+ return syscall(SYS_getfsxattrat, dfd, filename, fsx, usize, at_flags);
+}
+
+static int
+setfsxattrat(
+ int dfd,
+ const char *filename,
+ struct fsxattr *fsx,
+ size_t usize,
+ unsigned int at_flags)
+{
+ return syscall(SYS_setfsxattrat, dfd, filename, fsx, usize, at_flags);
+}
+
+void
+printxattr(
+ 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 fsx_xflags = 0;
+ int action = 0; /* 0 get; 1 set */
+ struct fsxattr fsx = { 0 };
+ struct stat status;
+ int fd;
+ int at_fdcwd = 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':
+ fsx_xflags |= FS_XFLAG_NODUMP;
+ break;
+ default:
+ goto usage;
+ }
+ }
+
+ if (!path1 && optind < argc)
+ path1 = argv[optind++];
+ if (!path2 && optind < argc)
+ path2 = argv[optind++];
+
+ if (!path1)
+ goto usage;
+
+ 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;
+ }
+ at_flags |= AT_EMPTY_PATH;
+ } 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 (action) {
+ error = getfsxattrat(fd, path, &fsx, sizeof(struct fsxattr),
+ at_flags);
+ if (error) {
+ fprintf(stderr, "Can not get fsxattr on %s: %s\n", path,
+ strerror(errno));
+ return error;
+ }
+
+ fsx.fsx_xflags |= fsx_xflags;
+
+ error = setfsxattrat(fd, path, &fsx, sizeof(struct fsxattr),
+ at_flags);
+ if (error) {
+ fprintf(stderr, "Can not set fsxattr on %s: %s\n", path,
+ strerror(errno));
+ return error;
+ }
+ } else {
+ error = getfsxattrat(fd, path, &fsx, sizeof(struct fsxattr),
+ at_flags);
+ if (error) {
+ fprintf(stderr, "Can not get fsxattr on %s: %s\n", path,
+ strerror(errno));
+ return error;
+ }
+
+ if (path2)
+ printxattr(fsx.fsx_xflags, 0, 1, path, 0, 1);
+ else
+ printxattr(fsx.fsx_xflags, 0, 1, path1, 0, 1);
+ }
+
+ return error;
+
+usage:
+ printf("Usage: %s [options]\n", argv[0]);
+ printf("Options:\n");
+ printf("\t--get\t\tget filesystem inode attributes\n");
+ printf("\t--set\t\tset filesystem inode attributes\n");
+ printf("\t--at-cwd\t\topen file at current working directory\n");
+ printf("\t--no-follow\t\tdon't follow symlinks\n");
+ printf("\t--set-nodump\t\tset FS_XFLAG_NODUMP on an inode\n");
+
+ return 1;
+}
This programs uses newly introduced getfsxattrat and setfsxattrat syscalls. These syscalls comparing to FS_IOC_SETFSXATTR can work with special files via *at() semantic. Signed-off-by: Andrey Albershteyn <aalbersh@kernel.org> --- .gitignore | 1 + configure.ac | 1 + include/builddefs.in | 1 + m4/package_libcdev.m4 | 16 ++++ src/Makefile | 4 + src/fsxattrat.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 269 insertions(+)