diff --git a/.gitignore b/.gitignore
index cce0795ea81878e88957324377b81051905471a6..9127af3c6bec0991300ec729ba09400f05c3b118 100644
--- a/.gitignore
+++ b/.gitignore
@@ -212,6 +212,7 @@ tags
 /src/fiemap-fault
 /src/min_dio_alignment
 /src/dio-writeback-race
+/src/fsxattrat
 
 # Symlinked files
 /tests/generic/035.out
diff --git a/configure.ac b/configure.ac
index f3c8c643f0eb9e72183ed17b5eb9a63c33d8bbe7..be2072c2caf4cb65a1ee37b15ec62048a1bef21b 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_FSXATTRAT
 
 AC_CHECK_FUNCS([renameat2])
 AC_CHECK_FUNCS([reallocarray])
diff --git a/include/builddefs.in b/include/builddefs.in
index 96d5ed25b3e2d4cfc826f3470e69603550054ce7..533848c2b45e24f5ee9ef39e719f5199e5e73034 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_FSXATTRAT = @have_fsxattrat@
 
 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 ed8fe6e32ae00aea9571fff13362754111ecca71..7b828c647dc54f059a6a8571cb858d5b6dfb5405 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 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)
+  ])
diff --git a/src/Makefile b/src/Makefile
index 6ac72b3662570dd9bbcb90d4b23df931b7cd5b9e..365e324d91eb828ffa42d515ee49a842044d6267 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -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
diff --git a/src/fsxattrat.c b/src/fsxattrat.c
new file mode 100644
index 0000000000000000000000000000000000000000..a339de66381ee361dfda5bcb598440a31c5f35b5
--- /dev/null
+++ b/src/fsxattrat.c
@@ -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;
+}
