Re: [PATCH 1/3] file_attr: introduce program to set/get fsxattr

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 2025-08-11 08:23:40, Darrick J. Wong wrote:
> On Fri, Aug 08, 2025 at 09:31:56PM +0200, Andrey Albershteyn wrote:
> > 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@xxxxxxxxxx>
> > ---
> >  .gitignore            |   1 +
> >  configure.ac          |   1 +
> >  include/builddefs.in  |   1 +
> >  m4/package_libcdev.m4 |  16 +++
> >  src/Makefile          |   5 +
> >  src/file_attr.c       | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++
> >  6 files changed, 301 insertions(+)
> > 
> > diff --git a/.gitignore b/.gitignore
> > index 4fd817243dca..1a578eab1ea0 100644
> > --- a/.gitignore
> > +++ b/.gitignore
> > @@ -210,6 +210,7 @@ tags
> >  /src/fiemap-fault
> >  /src/min_dio_alignment
> >  /src/dio-writeback-race
> > +/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 6ac72b366257..f3137acf687f 100644
> > --- a/src/Makefile
> > +++ b/src/Makefile
> > @@ -61,6 +61,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..9756ab265a57
> > --- /dev/null
> > +++ b/src/file_attr.c
> > @@ -0,0 +1,277 @@
> > +#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);
> > +}
> > +
> > +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 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;
> > +
> > +	if (action) {
> > +		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;
> > +		}
> > +
> > +		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 {
> > +		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;
> > +		}
> 
> Can the file_getattr be lifted above the if (action) ?

yup

> 
> > +
> > +		if (path2)
> > +			printxattr(fsx.fa_xflags, 0, 1, path, 0, 1);
> > +		else
> > +			printxattr(fsx.fa_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");
> > +	printf("\t--invalid-at\t\tUse invalida AT_* flag\n");
> > +	printf("\t--too-big-arg\t\tSet fsxattr size bigger than PAGE_SIZE\n");
> > +	printf("\t--too-small-arg\t\tSet fsxattr size to 27 bytes\n");
> 
> 27?  I thought you put in 19 above?

ops, forgot to update it here

> 
> Also it'd be nice if the help listed the short and long versions.
> 
> (Or skip the short cli switches ;))

will add short versions

> 
> --D
> 
> > +	printf("\t--new-fsx-flag\t\tUse unknown fa_flags flag\n");
> > +
> > +	return 1;
> > +}
> > 
> > -- 
> > 2.49.0
> > 
> > 
> 

-- 
- Andrey





[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux