* [PATCH 1/7] libfrog: support editing filesystem property sets
2024-08-06 18:18 ` [PATCHSET v30.10 1/3] xfsprogs: " Darrick J. Wong
@ 2024-08-06 18:19 ` Darrick J. Wong
2024-08-06 18:19 ` [PATCH 2/7] xfs_io: edit filesystem properties Darrick J. Wong
` (5 subsequent siblings)
6 siblings, 0 replies; 28+ messages in thread
From: Darrick J. Wong @ 2024-08-06 18:19 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, Dave Chinner, hch, dchinner, fstests,
linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add some library functions so that spaceman and scrub can share the same
code to edit and retrieve filesystem properties.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Acked-by: Dave Chinner <dchinner@redhat.com>
---
libfrog/Makefile | 7 ++
libfrog/fsproperties.c | 39 +++++++++
libfrog/fsproperties.h | 53 +++++++++++++
libfrog/fsprops.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++
libfrog/fsprops.h | 34 ++++++++
5 files changed, 335 insertions(+)
create mode 100644 libfrog/fsproperties.c
create mode 100644 libfrog/fsproperties.h
create mode 100644 libfrog/fsprops.c
create mode 100644 libfrog/fsprops.h
diff --git a/libfrog/Makefile b/libfrog/Makefile
index 0b5b23893..acddc894e 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -20,6 +20,7 @@ convert.c \
crc32.c \
file_exchange.c \
fsgeom.c \
+fsproperties.c \
getparents.c \
histogram.c \
list_sort.c \
@@ -47,6 +48,7 @@ dahashselftest.h \
div64.h \
file_exchange.h \
fsgeom.h \
+fsproperties.h \
getparents.h \
histogram.h \
logging.h \
@@ -60,6 +62,11 @@ workqueue.h
LSRCFILES += gen_crc32table.c
+ifeq ($(HAVE_LIBATTR),yes)
+CFILES+=fsprops.c
+HFILES+=fsprops.h
+endif
+
LDIRT = gen_crc32table crc32table.h
default: ltdepend $(LTLIBRARY)
diff --git a/libfrog/fsproperties.c b/libfrog/fsproperties.c
new file mode 100644
index 000000000..c317d15c1
--- /dev/null
+++ b/libfrog/fsproperties.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include <string.h>
+#include "xfs.h"
+#include "libfrog/fsgeom.h"
+#include "libfrog/fsproperties.h"
+#include "list.h"
+
+/* Return the offset of a string in a string table, or -1 if not found. */
+static inline int
+__fsprops_lookup(
+ const char *values[],
+ unsigned int nr_values,
+ const char *value)
+{
+ unsigned int i;
+
+ for (i = 0; i < nr_values; i++) {
+ if (values[i] && !strcmp(value, values[i]))
+ return i;
+ }
+
+ return -1;
+}
+
+#define fsprops_lookup(values, value) \
+ __fsprops_lookup((values), ARRAY_SIZE(values), (value))
+
+/* Return true if a fs property name=value tuple is allowed. */
+bool
+fsprop_validate(
+ const char *name,
+ const char *value)
+{
+ return true;
+}
diff --git a/libfrog/fsproperties.h b/libfrog/fsproperties.h
new file mode 100644
index 000000000..b1ac4cdd7
--- /dev/null
+++ b/libfrog/fsproperties.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __LIBFROG_FSPROPERTIES_H__
+#define __LIBFROG_FSPROPERTIES_H__
+
+/* Name space for filesystem properties. */
+#define FSPROP_NAMESPACE "trusted."
+
+/*
+ * All filesystem property xattr names must have this string after the
+ * namespace. For example, the VFS xattr calls should use the name
+ * "trusted.xfs:fubar". The xfs xattr ioctls would set ATTR_ROOT and use the
+ * name "xfs:fubar".
+ */
+#define FSPROP_NAME_PREFIX "xfs:"
+
+/* Maximum size the value of a filesystem property. */
+#define FSPROP_MAX_VALUELEN (65536)
+
+static inline int
+fsprop_name_to_attr_name(
+ const char *prop_name,
+ char **attr_name)
+{
+ return asprintf(attr_name, FSPROP_NAME_PREFIX "%s", prop_name);
+}
+
+static inline const char *
+attr_name_to_fsprop_name(
+ const char *attr_name)
+{
+ const size_t bytes = sizeof(FSPROP_NAME_PREFIX) - 1;
+ unsigned int i;
+
+ for (i = 0; i < bytes; i++) {
+ if (attr_name[i] == 0)
+ return NULL;
+ }
+
+ if (memcmp(attr_name, FSPROP_NAME_PREFIX, bytes) != 0)
+ return NULL;
+
+ return attr_name + bytes;
+}
+
+bool fsprop_validate(const char *name, const char *value);
+
+/* Specific Filesystem Properties */
+
+#endif /* __LIBFROG_FSPROPERTIES_H__ */
diff --git a/libfrog/fsprops.c b/libfrog/fsprops.c
new file mode 100644
index 000000000..88046b7a0
--- /dev/null
+++ b/libfrog/fsprops.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "xfs.h"
+#include "handle.h"
+#include "libfrog/fsgeom.h"
+#include "libfrog/paths.h"
+#include "libfrog/bulkstat.h"
+#include "libfrog/fsprops.h"
+#include "libfrog/fsproperties.h"
+
+#include <attr/attributes.h>
+
+/*
+ * Given an xfd and a mount table path, get us the handle for the root dir so
+ * we can set filesystem properties.
+ */
+int
+fsprops_open_handle(
+ struct xfs_fd *xfd,
+ struct fs_path *fs_path,
+ struct fsprops_handle *fph)
+{
+ struct xfs_bulkstat bulkstat;
+ struct stat sb;
+ int ret;
+
+ /* fs properties only supported on V5 filesystems */
+ if (!(xfd->fsgeom.flags & XFS_FSOP_GEOM_FLAGS_V5SB)) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+
+ ret = -xfrog_bulkstat_single(xfd, XFS_BULK_IREQ_SPECIAL_ROOT,
+ XFS_BULK_IREQ_SPECIAL, &bulkstat);
+ if (ret)
+ return ret;
+
+ ret = fstat(xfd->fd, &sb);
+ if (ret)
+ return ret;
+
+ if (sb.st_ino != bulkstat.bs_ino) {
+ errno = ESRMNT;
+ return -1;
+ }
+
+ return fd_to_handle(xfd->fd, &fph->hanp, &fph->hlen);
+}
+
+/* Free a fsproperties handle. */
+void
+fsprops_free_handle(
+ struct fsprops_handle *fph)
+{
+ if (fph->hanp)
+ free_handle(fph->hanp, fph->hlen);
+ fph->hanp = NULL;
+ fph->hlen = 0;
+}
+
+/* Call the given callback on every fs property accessible through the handle. */
+int
+fsprops_walk_names(
+ struct fsprops_handle *fph,
+ fsprops_name_walk_fn walk_fn,
+ void *priv)
+{
+ struct attrlist_cursor cur = { };
+ char attrbuf[XFS_XATTR_LIST_MAX];
+ struct attrlist *attrlist = (struct attrlist *)attrbuf;
+ int ret;
+
+ memset(attrbuf, 0, XFS_XATTR_LIST_MAX);
+
+ while ((ret = attr_list_by_handle(fph->hanp, fph->hlen, attrbuf,
+ XFS_XATTR_LIST_MAX, XFS_IOC_ATTR_ROOT,
+ &cur)) == 0) {
+ unsigned int i;
+
+ for (i = 0; i < attrlist->al_count; i++) {
+ struct attrlist_ent *ent = ATTR_ENTRY(attrlist, i);
+ const char *p =
+ attr_name_to_fsprop_name(ent->a_name);
+
+ if (p) {
+ ret = walk_fn(fph, p, ent->a_valuelen, priv);
+ if (ret)
+ break;
+ }
+ }
+
+ if (!attrlist->al_more)
+ break;
+ }
+
+ return ret;
+}
+
+/* Retrieve the value of a specific fileystem property. */
+int
+fsprops_get(
+ struct fsprops_handle *fph,
+ const char *name,
+ void *valuebuf,
+ size_t *valuelen)
+{
+ struct xfs_attr_multiop ops = {
+ .am_opcode = ATTR_OP_GET,
+ .am_attrvalue = valuebuf,
+ .am_length = *valuelen,
+ .am_flags = XFS_IOC_ATTR_ROOT,
+ };
+ int ret;
+
+ ret = fsprop_name_to_attr_name(name, (char **)&ops.am_attrname);
+ if (ret < 0)
+ return ret;
+
+ ret = attr_multi_by_handle(fph->hanp, fph->hlen, &ops, 1, 0);
+ if (ret < 0)
+ goto out_name;
+
+ if (ops.am_error) {
+ errno = -ops.am_error;
+ ret = -1;
+ goto out_name;
+ }
+
+ *valuelen = ops.am_length;
+out_name:
+ free(ops.am_attrname);
+ return ret;
+}
+
+/* Set the value of a specific fileystem property. */
+int
+fsprops_set(
+ struct fsprops_handle *fph,
+ const char *name,
+ void *valuebuf,
+ size_t valuelen)
+{
+ struct xfs_attr_multiop ops = {
+ .am_opcode = ATTR_OP_SET,
+ .am_attrvalue = valuebuf,
+ .am_length = valuelen,
+ .am_flags = XFS_IOC_ATTR_ROOT,
+ };
+ int ret;
+
+ ret = fsprop_name_to_attr_name(name, (char **)&ops.am_attrname);
+ if (ret < 0)
+ return ret;
+
+ ret = attr_multi_by_handle(fph->hanp, fph->hlen, &ops, 1, 0);
+ if (ret < 0)
+ goto out_name;
+
+ if (ops.am_error) {
+ errno = -ops.am_error;
+ ret = -1;
+ goto out_name;
+ }
+
+out_name:
+ free(ops.am_attrname);
+ return ret;
+}
+
+/* Unset the value of a specific fileystem property. */
+int
+fsprops_remove(
+ struct fsprops_handle *fph,
+ const char *name)
+{
+ struct xfs_attr_multiop ops = {
+ .am_opcode = ATTR_OP_REMOVE,
+ .am_flags = XFS_IOC_ATTR_ROOT,
+ };
+ int ret;
+
+ ret = fsprop_name_to_attr_name(name, (char **)&ops.am_attrname);
+ if (ret < 0)
+ return ret;
+
+ ret = attr_multi_by_handle(fph->hanp, fph->hlen, &ops, 1, 0);
+ if (ret < 0)
+ goto out_name;
+
+ if (ops.am_error) {
+ errno = -ops.am_error;
+ ret = -1;
+ goto out_name;
+ }
+
+out_name:
+ free(ops.am_attrname);
+ return ret;
+}
diff --git a/libfrog/fsprops.h b/libfrog/fsprops.h
new file mode 100644
index 000000000..9276f2425
--- /dev/null
+++ b/libfrog/fsprops.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __LIBFROG_FSPROPS_H__
+#define __LIBFROG_FSPROPS_H__
+
+/* Edit and view filesystem property sets. */
+
+struct fsprops_handle {
+ void *hanp;
+ size_t hlen;
+};
+
+struct xfs_fd;
+struct fs_path;
+
+int fsprops_open_handle(struct xfs_fd *xfd, struct fs_path *fspath,
+ struct fsprops_handle *fph);
+void fsprops_free_handle(struct fsprops_handle *fph);
+
+typedef int (*fsprops_name_walk_fn)(struct fsprops_handle *fph,
+ const char *name, size_t valuelen, void *priv);
+
+int fsprops_walk_names(struct fsprops_handle *fph, fsprops_name_walk_fn walk_fn,
+ void *priv);
+int fsprops_get(struct fsprops_handle *fph, const char *name, void *attrbuf,
+ size_t *attrlen);
+int fsprops_set(struct fsprops_handle *fph, const char *name, void *attrbuf,
+ size_t attrlen);
+int fsprops_remove(struct fsprops_handle *fph, const char *name);
+
+#endif /* __LIBFROG_FSPROPS_H__ */
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH 2/7] xfs_io: edit filesystem properties
2024-08-06 18:18 ` [PATCHSET v30.10 1/3] xfsprogs: " Darrick J. Wong
2024-08-06 18:19 ` [PATCH 1/7] libfrog: support editing filesystem property sets Darrick J. Wong
@ 2024-08-06 18:19 ` Darrick J. Wong
2024-08-07 16:08 ` Christoph Hellwig
2024-08-06 18:20 ` [PATCH 3/7] xfs_db: improve getting and setting extended attributes Darrick J. Wong
` (4 subsequent siblings)
6 siblings, 1 reply; 28+ messages in thread
From: Darrick J. Wong @ 2024-08-06 18:19 UTC (permalink / raw)
To: djwong, cem; +Cc: Dave Chinner, hch, dchinner, fstests, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add some new subcommands to xfs_io so that users can administer
filesystem properties.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>Acked-by: Dave Chinner <dchinner@redhat.com>
---
io/Makefile | 1
io/fsproperties.c | 365 +++++++++++++++++++++++++++++++++++++++++++++++++++++
io/init.c | 1
io/io.h | 1
man/man8/xfs_io.8 | 16 ++
5 files changed, 383 insertions(+), 1 deletion(-)
create mode 100644 io/fsproperties.c
diff --git a/io/Makefile b/io/Makefile
index 3192b813c..0bdd05b57 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -20,6 +20,7 @@ CFILES = \
fiemap.c \
file.c \
freeze.c \
+ fsproperties.c \
fsuuid.c \
fsync.c \
getrusage.c \
diff --git a/io/fsproperties.c b/io/fsproperties.c
new file mode 100644
index 000000000..79837132e
--- /dev/null
+++ b/io/fsproperties.c
@@ -0,0 +1,365 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "platform_defs.h"
+#include "command.h"
+#include "init.h"
+#include "libfrog/paths.h"
+#include "input.h"
+#include "libfrog/fsgeom.h"
+#include "handle.h"
+#include "io.h"
+#include "libfrog/fsprops.h"
+#include "libfrog/fsproperties.h"
+
+static void
+listfsprops_help(void)
+{
+ printf(_(
+"Print the names of the filesystem properties stored in this filesystem.\n"
+"\n"));
+}
+
+static int
+fileio_to_fsprops_handle(
+ struct fileio *file,
+ struct fsprops_handle *fph)
+{
+ struct xfs_fd xfd = XFS_FD_INIT(file->fd);
+ struct fs_path *fs;
+ void *hanp = NULL;
+ size_t hlen;
+ int ret;
+
+ /*
+ * Look up the mount point info for the open file, which confirms that
+ * we were passed a mount point.
+ */
+ fs = fs_table_lookup(file->name, FS_MOUNT_POINT);
+ if (!fs) {
+ fprintf(stderr, _("%s: Not a XFS mount point.\n"),
+ file->name);
+ goto bad;
+ }
+
+ /*
+ * Register the mountpoint in the fsfd cache so we can use handle
+ * functions later.
+ */
+ ret = path_to_fshandle(fs->fs_dir, &hanp, &hlen);
+ if (ret) {
+ perror(fs->fs_dir);
+ goto bad;
+ }
+
+ ret = -xfd_prepare_geometry(&xfd);
+ if (ret) {
+ perror(file->name);
+ goto free_handle;
+ }
+
+ ret = fsprops_open_handle(&xfd, &file->fs_path, fph);
+ if (ret) {
+ if (errno == ESRMNT)
+ fprintf(stderr, _("%s: Not a XFS mount point.\n"),
+ file->name);
+ else
+ perror(file->name);
+ goto free_handle;
+ }
+
+ free_handle(hanp, hlen);
+ return 0;
+free_handle:
+ free_handle(hanp, hlen);
+bad:
+ exitcode = 1;
+ return 1;
+}
+
+static int
+print_fsprop(
+ struct fsprops_handle *fph,
+ const char *name,
+ size_t unused,
+ void *priv)
+{
+ bool *print_values = priv;
+ char valuebuf[FSPROP_MAX_VALUELEN];
+ size_t valuelen = FSPROP_MAX_VALUELEN;
+ int ret;
+
+ if (!(*print_values)) {
+ printf("%s\n", name);
+ return 0;
+ }
+
+ ret = fsprops_get(fph, name, valuebuf, &valuelen);
+ if (ret)
+ return ret;
+
+ printf("%s=%.*s\n", name, (int)valuelen, valuebuf);
+ return 0;
+}
+
+static int
+listfsprops_f(
+ int argc,
+ char **argv)
+{
+ struct fsprops_handle fph = { };
+ bool print_values = false;
+ int c;
+ int ret;
+
+ while ((c = getopt(argc, argv, "v")) != EOF) {
+ switch (c) {
+ case 'v':
+ print_values = true;
+ break;
+ default:
+ exitcode = 1;
+ listfsprops_help();
+ return 0;
+ }
+ }
+
+ ret = fileio_to_fsprops_handle(file, &fph);
+ if (ret)
+ return 1;
+
+ ret = fsprops_walk_names(&fph, print_fsprop, &print_values);
+ if (ret) {
+ perror(file->name);
+ exitcode = 1;
+ }
+
+ fsprops_free_handle(&fph);
+ return 0;
+}
+
+static struct cmdinfo listfsprops_cmd = {
+ .name = "listfsprops",
+ .cfunc = listfsprops_f,
+ .argmin = 0,
+ .argmax = -1,
+ .flags = CMD_NOMAP_OK,
+ .args = "",
+ .help = listfsprops_help,
+};
+
+static void
+getfsprops_help(void)
+{
+ printf(_(
+"Print the values of filesystem properties stored in this filesystem.\n"
+"\n"
+"Pass property names as the arguments.\n"
+"\n"));
+}
+
+static int
+getfsprops_f(
+ int argc,
+ char **argv)
+{
+ struct fsprops_handle fph = { };
+ int c;
+ int ret;
+
+ while ((c = getopt(argc, argv, "")) != EOF) {
+ switch (c) {
+ default:
+ exitcode = 1;
+ getfsprops_help();
+ return 0;
+ }
+ }
+
+ ret = fileio_to_fsprops_handle(file, &fph);
+ if (ret)
+ return ret;
+
+ for (c = optind; c < argc; c++) {
+ char valuebuf[FSPROP_MAX_VALUELEN];
+ size_t valuelen = FSPROP_MAX_VALUELEN;
+
+ ret = fsprops_get(&fph, argv[c], valuebuf, &valuelen);
+ if (ret) {
+ perror(argv[c]);
+ exitcode = 1;
+ break;
+ }
+
+ printf("%s=%.*s\n", argv[c], (int)valuelen, valuebuf);
+ }
+
+ fsprops_free_handle(&fph);
+ return 0;
+}
+
+static struct cmdinfo getfsprops_cmd = {
+ .name = "getfsprops",
+ .cfunc = getfsprops_f,
+ .argmin = 0,
+ .argmax = -1,
+ .flags = CMD_NOMAP_OK,
+ .args = "",
+ .help = getfsprops_help,
+};
+
+static void
+setfsprops_help(void)
+{
+ printf(_(
+"Set values of filesystem properties stored in this filesystem.\n"
+"\n"
+" -f Do not try to validate property value.\n"
+"\n"
+"Provide name=value tuples as the arguments.\n"
+"\n"));
+}
+
+static int
+setfsprops_f(
+ int argc,
+ char **argv)
+{
+ struct fsprops_handle fph = { };
+ bool force = false;
+ int c;
+ int ret;
+
+ while ((c = getopt(argc, argv, "f")) != EOF) {
+ switch (c) {
+ case 'f':
+ force = true;
+ break;
+ default:
+ exitcode = 1;
+ getfsprops_help();
+ return 0;
+ }
+ }
+
+ ret = fileio_to_fsprops_handle(file, &fph);
+ if (ret)
+ return ret;
+
+ for (c = optind; c < argc; c ++) {
+ char *equals = strchr(argv[c], '=');
+
+ if (!equals) {
+ fprintf(stderr, _("%s: property value required.\n"),
+ argv[c]);
+ exitcode = 1;
+ break;
+ }
+
+ *equals = 0;
+
+ if (!force && !fsprop_validate(argv[c], equals + 1)) {
+ fprintf(stderr, _("%s: invalid value \"%s\".\n"),
+ argv[c], equals + 1);
+ *equals = '=';
+ exitcode = 1;
+ break;
+ }
+
+ ret = fsprops_set(&fph, argv[c], equals + 1,
+ strlen(equals + 1));
+ if (ret) {
+ perror(argv[c]);
+ *equals = '=';
+ exitcode = 1;
+ break;
+ }
+
+ printf("%s=%s\n", argv[c], equals + 1);
+ *equals = '=';
+ }
+
+ fsprops_free_handle(&fph);
+ return 0;
+}
+
+static struct cmdinfo setfsprops_cmd = {
+ .name = "setfsprops",
+ .cfunc = setfsprops_f,
+ .argmin = 0,
+ .argmax = -1,
+ .flags = CMD_NOMAP_OK,
+ .args = "",
+ .help = setfsprops_help,
+};
+
+static void
+removefsprops_help(void)
+{
+ printf(_(
+"Unset a filesystem property.\n"
+"\n"
+"Pass property names as the arguments.\n"
+"\n"));
+}
+
+static int
+removefsprops_f(
+ int argc,
+ char **argv)
+{
+ struct fsprops_handle fph = { };
+ int c;
+ int ret;
+
+ while ((c = getopt(argc, argv, "")) != EOF) {
+ switch (c) {
+ default:
+ exitcode = 1;
+ getfsprops_help();
+ return 0;
+ }
+ }
+
+ ret = fileio_to_fsprops_handle(file, &fph);
+ if (ret)
+ return ret;
+
+ for (c = optind; c < argc; c++) {
+ ret = fsprops_remove(&fph, argv[c]);
+ if (ret) {
+ perror(argv[c]);
+ exitcode = 1;
+ break;
+ }
+ }
+
+ fsprops_free_handle(&fph);
+ return 0;
+}
+
+static struct cmdinfo removefsprops_cmd = {
+ .name = "removefsprops",
+ .cfunc = removefsprops_f,
+ .argmin = 0,
+ .argmax = -1,
+ .flags = CMD_NOMAP_OK,
+ .args = "",
+ .help = removefsprops_help,
+};
+
+void
+fsprops_init(void)
+{
+ listfsprops_cmd.oneline = _("list file system properties");
+ getfsprops_cmd.oneline = _("print file system properties");
+ setfsprops_cmd.oneline = _("set file system properties");
+ removefsprops_cmd.oneline = _("unset file system properties");
+
+ add_command(&listfsprops_cmd);
+ add_command(&getfsprops_cmd);
+ add_command(&setfsprops_cmd);
+ add_command(&removefsprops_cmd);
+}
diff --git a/io/init.c b/io/init.c
index 37e0f093c..5727f7351 100644
--- a/io/init.c
+++ b/io/init.c
@@ -89,6 +89,7 @@ init_commands(void)
utimes_init();
crc32cselftest_init();
exchangerange_init();
+ fsprops_init();
}
/*
diff --git a/io/io.h b/io/io.h
index fdb715ff0..8c5e59100 100644
--- a/io/io.h
+++ b/io/io.h
@@ -150,3 +150,4 @@ extern void repair_init(void);
extern void crc32cselftest_init(void);
extern void bulkstat_init(void);
void exchangerange_init(void);
+void fsprops_init(void);
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 657bdaec4..303c64478 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -1578,7 +1578,21 @@ Print the sysfs or debugfs path for the mounted filesystem.
The
.B -d
option selects debugfs instead of sysfs.
-
+.TP
+.BI "getfsprops " name " [ " names "... ]"
+Retrieve the values of the given filesystem properties.
+.TP
+.BI "listfsprops [ " \-v " ]"
+List all filesystem properties that have been stored in the filesystem.
+If the
+.B \-v
+flag is specified, prints the values of the properties.
+.TP
+.BI "setfsprops " name = value " [ " name = value "... ]"
+Set the given filesystem properties to the specified values.
+.TP
+.BI "removefsprops " name " [ " names "... ]"
+Remove the given filesystem properties.
.SH OTHER COMMANDS
.TP
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH 2/7] xfs_io: edit filesystem properties
2024-08-06 18:19 ` [PATCH 2/7] xfs_io: edit filesystem properties Darrick J. Wong
@ 2024-08-07 16:08 ` Christoph Hellwig
0 siblings, 0 replies; 28+ messages in thread
From: Christoph Hellwig @ 2024-08-07 16:08 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: cem, Dave Chinner, hch, fstests, linux-xfs
On Tue, Aug 06, 2024 at 11:19:50AM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> Add some new subcommands to xfs_io so that users can administer
> filesystem properties.
s/some //?
>
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>Acked-by: Dave Chinner <dchinner@redhat.com>
Missing newline here.
> + if (!(*print_values)) {
The inner set of braces here should not be needed.
Otherwise looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 3/7] xfs_db: improve getting and setting extended attributes
2024-08-06 18:18 ` [PATCHSET v30.10 1/3] xfsprogs: " Darrick J. Wong
2024-08-06 18:19 ` [PATCH 1/7] libfrog: support editing filesystem property sets Darrick J. Wong
2024-08-06 18:19 ` [PATCH 2/7] xfs_io: edit filesystem properties Darrick J. Wong
@ 2024-08-06 18:20 ` Darrick J. Wong
2024-08-07 16:10 ` Christoph Hellwig
2024-08-06 18:20 ` [PATCH 4/7] libxfs: hoist listxattr from xfs_repair Darrick J. Wong
` (3 subsequent siblings)
6 siblings, 1 reply; 28+ messages in thread
From: Darrick J. Wong @ 2024-08-06 18:20 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, Christoph Hellwig, Dave Chinner, hch, dchinner,
fstests, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add an attr_get command to retrieve the value of an xattr from a file;
and extend the attr_set command to allow passing of string values.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Acked-by: Dave Chinner <dchinner@redhat.com>
---
db/attrset.c | 262 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
man/man8/xfs_db.8 | 40 ++++++++
2 files changed, 293 insertions(+), 9 deletions(-)
diff --git a/db/attrset.c b/db/attrset.c
index 81d530055..9e53e63c9 100644
--- a/db/attrset.c
+++ b/db/attrset.c
@@ -17,20 +17,43 @@
#include "inode.h"
#include "malloc.h"
#include <sys/xattr.h>
+#include "libfrog/fsproperties.h"
+static int attr_get_f(int argc, char **argv);
static int attr_set_f(int argc, char **argv);
static int attr_remove_f(int argc, char **argv);
+static void attrget_help(void);
static void attrset_help(void);
+static const cmdinfo_t attr_get_cmd =
+ { "attr_get", "aget", attr_get_f, 1, -1, 0,
+ N_("[-r|-s|-u|-p|-Z] name"),
+ N_("get the named attribute on the current inode"), attrget_help };
static const cmdinfo_t attr_set_cmd =
{ "attr_set", "aset", attr_set_f, 1, -1, 0,
- N_("[-r|-s|-u|-p] [-n] [-R|-C] [-v n] name"),
+ N_("[-r|-s|-u|-p|-Z] [-n] [-R|-C] [-v n] name"),
N_("set the named attribute on the current inode"), attrset_help };
static const cmdinfo_t attr_remove_cmd =
{ "attr_remove", "aremove", attr_remove_f, 1, -1, 0,
- N_("[-r|-s|-u|-p] [-n] name"),
+ N_("[-r|-s|-u|-p|-Z] [-n] name"),
N_("remove the named attribute from the current inode"), attrset_help };
+static void
+attrget_help(void)
+{
+ dbprintf(_(
+"\n"
+" The attr_get command provide interfaces for retrieving the values of extended\n"
+" attributes of a file. This command requires attribute names to be specified.\n"
+" There are 4 namespace flags:\n"
+" -r -- 'root'\n"
+" -u -- 'user' (default)\n"
+" -s -- 'secure'\n"
+" -p -- 'parent'\n"
+" -Z -- fs property\n"
+"\n"));
+}
+
static void
attrset_help(void)
{
@@ -45,10 +68,15 @@ attrset_help(void)
" -u -- 'user' (default)\n"
" -s -- 'secure'\n"
" -p -- 'parent'\n"
+" -Z -- fs property\n"
"\n"
" For attr_set, these options further define the type of set operation:\n"
" -C -- 'create' - create attribute, fail if it already exists\n"
" -R -- 'replace' - replace attribute, fail if it does not exist\n"
+"\n"
+" If the attribute value is a string, it can be specified after the\n"
+" attribute name.\n"
+"\n"
" The backward compatibility mode 'noattr2' can be emulated (-n) also.\n"
"\n"));
}
@@ -59,6 +87,7 @@ attrset_init(void)
if (!expert_mode)
return;
+ add_command(&attr_get_cmd);
add_command(&attr_set_cmd);
add_command(&attr_remove_cmd);
}
@@ -106,6 +135,58 @@ get_buf_from_file(
LIBXFS_ATTR_ROOT | \
LIBXFS_ATTR_PARENT)
+static bool
+adjust_fsprop_attr_name(
+ struct xfs_da_args *args,
+ bool *free_name)
+{
+ const char *o = (const char *)args->name;
+ char *p;
+ int ret;
+
+ if ((args->attr_filter & LIBXFS_ATTR_NS) != LIBXFS_ATTR_ROOT) {
+ dbprintf(_("fs properties must be ATTR_ROOT\n"));
+ return false;
+ }
+
+ ret = fsprop_name_to_attr_name(o, &p);
+ if (ret < 0) {
+ dbprintf(_("could not allocate fs property name string\n"));
+ return false;
+ }
+ args->namelen = ret;
+ args->name = (const uint8_t *)p;
+
+ if (*free_name)
+ free((void *)o);
+ *free_name = true;
+
+ if (args->namelen > MAXNAMELEN) {
+ dbprintf(_("%s: name too long\n"), args->name);
+ return false;
+ }
+
+ if (args->valuelen > ATTR_MAX_VALUELEN) {
+ dbprintf(_("%s: value too long\n"), args->name);
+ return false;
+ }
+
+ return true;
+}
+
+static void
+print_fsprop(
+ struct xfs_da_args *args)
+{
+ const char *p =
+ attr_name_to_fsprop_name((const char *)args->name);
+
+ if (p)
+ printf("%s=%.*s\n", p, args->valuelen, (char *)args->value);
+ else
+ fprintf(stderr, _("%s: not a fs property?\n"), args->name);
+}
+
static int
attr_set_f(
int argc,
@@ -119,7 +200,9 @@ attr_set_f(
char *sp;
char *name_from_file = NULL;
char *value_from_file = NULL;
+ bool free_name = false;
enum xfs_attr_update op = XFS_ATTRUPDATE_UPSERT;
+ bool fsprop = false;
int c;
int error;
@@ -132,9 +215,12 @@ attr_set_f(
return 0;
}
- while ((c = getopt(argc, argv, "ruspCRnN:v:V:")) != EOF) {
+ while ((c = getopt(argc, argv, "ruspCRnN:v:V:Z")) != EOF) {
switch (c) {
/* namespaces */
+ case 'Z':
+ fsprop = true;
+ fallthrough;
case 'r':
args.attr_filter &= ~LIBXFS_ATTR_NS;
args.attr_filter |= LIBXFS_ATTR_ROOT;
@@ -213,9 +299,10 @@ attr_set_f(
if (!args.name)
return 0;
+ free_name = true;
args.namelen = namelen;
} else {
- if (optind != argc - 1) {
+ if (optind != argc - 1 && optind != argc - 2) {
dbprintf(_("too few options for attr_set (no name given)\n"));
return 0;
}
@@ -250,6 +337,25 @@ attr_set_f(
goto out;
}
memset(args.value, 'v', args.valuelen);
+ } else if (optind == argc - 2) {
+ args.valuelen = strlen(argv[optind + 1]);
+ args.value = strdup(argv[optind + 1]);
+ if (!args.value) {
+ dbprintf(_("cannot allocate buffer (%d)\n"),
+ args.valuelen);
+ goto out;
+ }
+ }
+
+ if (fsprop) {
+ if (!fsprop_validate((const char *)args.name, args.value)) {
+ dbprintf(_("%s: invalid value \"%s\"\n"),
+ args.name, args.value);
+ goto out;
+ }
+
+ if (!adjust_fsprop_attr_name(&args, &free_name))
+ goto out;
}
if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &args.dp)) {
@@ -269,6 +375,9 @@ attr_set_f(
goto out;
}
+ if (fsprop)
+ print_fsprop(&args);
+
/* refresh with updated inode contents */
set_cur_inode(iocur_top->ino);
@@ -277,7 +386,7 @@ attr_set_f(
libxfs_irele(args.dp);
if (args.value)
free(args.value);
- if (name_from_file)
+ if (free_name)
free((void *)args.name);
return 0;
}
@@ -293,6 +402,8 @@ attr_remove_f(
.op_flags = XFS_DA_OP_OKNOENT,
};
char *name_from_file = NULL;
+ bool free_name = false;
+ bool fsprop = false;
int c;
int error;
@@ -305,9 +416,12 @@ attr_remove_f(
return 0;
}
- while ((c = getopt(argc, argv, "ruspnN:")) != EOF) {
+ while ((c = getopt(argc, argv, "ruspnN:Z")) != EOF) {
switch (c) {
/* namespaces */
+ case 'Z':
+ fsprop = true;
+ fallthrough;
case 'r':
args.attr_filter &= ~LIBXFS_ATTR_NS;
args.attr_filter |= LIBXFS_ATTR_ROOT;
@@ -354,6 +468,7 @@ attr_remove_f(
if (!args.name)
return 0;
+ free_name = true;
args.namelen = namelen;
} else {
if (optind != argc - 1) {
@@ -374,6 +489,9 @@ attr_remove_f(
}
}
+ if (fsprop && !adjust_fsprop_attr_name(&args, &free_name))
+ goto out;
+
if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &args.dp)) {
dbprintf(_("failed to iget inode %llu\n"),
(unsigned long long)iocur_top->ino);
@@ -398,7 +516,137 @@ attr_remove_f(
out:
if (args.dp)
libxfs_irele(args.dp);
- if (name_from_file)
+ if (free_name)
+ free((void *)args.name);
+ return 0;
+}
+
+static int
+attr_get_f(
+ int argc,
+ char **argv)
+{
+ struct xfs_da_args args = {
+ .geo = mp->m_attr_geo,
+ .whichfork = XFS_ATTR_FORK,
+ .op_flags = XFS_DA_OP_OKNOENT,
+ };
+ char *name_from_file = NULL;
+ bool free_name = false;
+ bool fsprop = false;
+ int c;
+ int error;
+
+ if (cur_typ == NULL) {
+ dbprintf(_("no current type\n"));
+ return 0;
+ }
+ if (cur_typ->typnm != TYP_INODE) {
+ dbprintf(_("current type is not inode\n"));
+ return 0;
+ }
+
+ while ((c = getopt(argc, argv, "ruspN:Z")) != EOF) {
+ switch (c) {
+ /* namespaces */
+ case 'Z':
+ fsprop = true;
+ fallthrough;
+ case 'r':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
+ args.attr_filter |= LIBXFS_ATTR_ROOT;
+ break;
+ case 'u':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
+ break;
+ case 's':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
+ args.attr_filter |= LIBXFS_ATTR_SECURE;
+ break;
+ case 'p':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
+ args.attr_filter |= XFS_ATTR_PARENT;
+ break;
+
+ case 'N':
+ name_from_file = optarg;
+ break;
+ default:
+ dbprintf(_("bad option for attr_get command\n"));
+ return 0;
+ }
+ }
+
+ if (name_from_file) {
+ int namelen;
+
+ if (optind != argc) {
+ dbprintf(_("too many options for attr_get (no name needed)\n"));
+ return 0;
+ }
+
+ args.name = get_buf_from_file(name_from_file, MAXNAMELEN,
+ &namelen);
+ if (!args.name)
+ return 0;
+
+ free_name = true;
+ args.namelen = namelen;
+ } else {
+ if (optind != argc - 1) {
+ dbprintf(_("too few options for attr_get (no name given)\n"));
+ return 0;
+ }
+
+ args.name = (const unsigned char *)argv[optind];
+ if (!args.name) {
+ dbprintf(_("invalid name\n"));
+ return 0;
+ }
+
+ args.namelen = strlen(argv[optind]);
+ if (args.namelen >= MAXNAMELEN) {
+ dbprintf(_("name too long\n"));
+ goto out;
+ }
+ }
+
+ if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &args.dp)) {
+ dbprintf(_("failed to iget inode %llu\n"),
+ (unsigned long long)iocur_top->ino);
+ goto out;
+ }
+
+ if (fsprop && !adjust_fsprop_attr_name(&args, &free_name))
+ goto out;
+
+ args.owner = iocur_top->ino;
+ libxfs_attr_sethash(&args);
+
+ /*
+ * Look up attr value with a maximally long length and a null buffer
+ * to return the value and the correct length.
+ */
+ args.valuelen = XATTR_SIZE_MAX;
+ error = -libxfs_attr_get(&args);
+ if (error) {
+ dbprintf(_("failed to get attr %s on inode %llu: %s\n"),
+ args.name, (unsigned long long)iocur_top->ino,
+ strerror(error));
+ goto out;
+ }
+
+ if (fsprop)
+ print_fsprop(&args);
+ else
+ printf("%.*s\n", args.valuelen, (char *)args.value);
+
+out:
+ if (args.dp)
+ libxfs_irele(args.dp);
+ if (args.value)
+ free(args.value);
+ if (free_name)
free((void *)args.name);
return 0;
}
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index 9f6fea574..f0865b2df 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -184,7 +184,35 @@ Displays the length, free block count, per-AG reservation size, and per-AG
reservation usage for a given AG.
If no argument is given, display information for all AGs.
.TP
-.BI "attr_remove [\-p|\-r|\-u|\-s] [\-n] [\-N " namefile "|" name "] "
+.BI "attr_get [\-p|\-r|\-u|\-s|\-Z] [\-N " namefile "|" name "] "
+Print the value of the specified extended attribute from the current file.
+.RS 1.0i
+.TP 0.4i
+.B \-p
+Sets the attribute in the parent namespace.
+Only one namespace option can be specified.
+.TP
+.B \-r
+Sets the attribute in the root namespace.
+Only one namespace option can be specified.
+.TP
+.B \-u
+Sets the attribute in the user namespace.
+Only one namespace option can be specified.
+.TP
+.B \-s
+Sets the attribute in the secure namespace.
+Only one namespace option can be specified.
+.TP
+.B \-Z
+Sets a filesystem property in the root namespace.
+Only one namespace option can be specified.
+.TP
+.B \-N
+Read the name from this file.
+.RE
+.TP
+.BI "attr_remove [\-p|\-r|\-u|\-s|\-Z] [\-n] [\-N " namefile "|" name "] "
Remove the specified extended attribute from the current file.
.RS 1.0i
.TP 0.4i
@@ -204,6 +232,10 @@ Only one namespace option can be specified.
Sets the attribute in the secure namespace.
Only one namespace option can be specified.
.TP
+.B \-Z
+Sets a filesystem property in the root namespace.
+Only one namespace option can be specified.
+.TP
.B \-N
Read the name from this file.
.TP
@@ -211,7 +243,7 @@ Read the name from this file.
Do not enable 'noattr2' mode on V4 filesystems.
.RE
.TP
-.BI "attr_set [\-p\-r|\-u|\-s] [\-n] [\-R|\-C] [\-v " valuelen "|\-V " valuefile "] [\-N " namefile "|" name "] "
+.BI "attr_set [\-p\-r|\-u|\-s|\-Z] [\-n] [\-R|\-C] [\-v " valuelen "|\-V " valuefile "] [\-N " namefile "|" name "] [" value "]"
Sets an extended attribute on the current file with the given name.
.RS 1.0i
.TP 0.4i
@@ -231,6 +263,10 @@ Only one namespace option can be specified.
Sets the attribute in the secure namespace.
Only one namespace option can be specified.
.TP
+.B \-Z
+Sets a filesystem property in the root namespace.
+Only one namespace option can be specified.
+.TP
.B \-N
Read the name from this file.
.TP
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH 3/7] xfs_db: improve getting and setting extended attributes
2024-08-06 18:20 ` [PATCH 3/7] xfs_db: improve getting and setting extended attributes Darrick J. Wong
@ 2024-08-07 16:10 ` Christoph Hellwig
2024-08-07 16:43 ` Darrick J. Wong
0 siblings, 1 reply; 28+ messages in thread
From: Christoph Hellwig @ 2024-08-07 16:10 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: cem, Christoph Hellwig, Dave Chinner, fstests, linux-xfs
On Tue, Aug 06, 2024 at 11:20:06AM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> Add an attr_get command to retrieve the value of an xattr from a file;
> and extend the attr_set command to allow passing of string values.
>
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>Reviewed-by: Christoph Hellwig <hch@lst.de>
Missing newline here again (not going to say this again if it shows
up on more patches..)
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 3/7] xfs_db: improve getting and setting extended attributes
2024-08-07 16:10 ` Christoph Hellwig
@ 2024-08-07 16:43 ` Darrick J. Wong
0 siblings, 0 replies; 28+ messages in thread
From: Darrick J. Wong @ 2024-08-07 16:43 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: cem, Dave Chinner, fstests, linux-xfs
On Wed, Aug 07, 2024 at 06:10:04PM +0200, Christoph Hellwig wrote:
> On Tue, Aug 06, 2024 at 11:20:06AM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <djwong@kernel.org>
> >
> > Add an attr_get command to retrieve the value of an xattr from a file;
> > and extend the attr_set command to allow passing of string values.
> >
> > Signed-off-by: Darrick J. Wong <djwong@kernel.org>Reviewed-by: Christoph Hellwig <hch@lst.de>
>
> Missing newline here again (not going to say this again if it shows
> up on more patches..)
Yeah, I don't know why that happened. Something went wrong in my text
processing scripts.
--D
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 4/7] libxfs: hoist listxattr from xfs_repair
2024-08-06 18:18 ` [PATCHSET v30.10 1/3] xfsprogs: " Darrick J. Wong
` (2 preceding siblings ...)
2024-08-06 18:20 ` [PATCH 3/7] xfs_db: improve getting and setting extended attributes Darrick J. Wong
@ 2024-08-06 18:20 ` Darrick J. Wong
2024-08-06 18:20 ` [PATCH 5/7] libxfs: pass a transaction context through listxattr Darrick J. Wong
` (2 subsequent siblings)
6 siblings, 0 replies; 28+ messages in thread
From: Darrick J. Wong @ 2024-08-06 18:20 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, Dave Chinner, hch, dchinner, fstests,
linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Hoist the listxattr code from xfs_repair so that we can use it in
xfs_db.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Acked-by: Dave Chinner <dchinner@redhat.com>
---
libxfs/Makefile | 2 ++
libxfs/listxattr.c | 2 +-
libxfs/listxattr.h | 6 +++---
repair/Makefile | 2 --
repair/pptr.c | 2 +-
5 files changed, 7 insertions(+), 7 deletions(-)
rename repair/listxattr.c => libxfs/listxattr.c (99%)
rename repair/listxattr.h => libxfs/listxattr.h (81%)
diff --git a/libxfs/Makefile b/libxfs/Makefile
index 4e8f9a135..2f2791cae 100644
--- a/libxfs/Makefile
+++ b/libxfs/Makefile
@@ -23,6 +23,7 @@ HFILES = \
defer_item.h \
libxfs_io.h \
libxfs_api_defs.h \
+ listxattr.h \
init.h \
libxfs_priv.h \
linux-err.h \
@@ -69,6 +70,7 @@ CFILES = buf_mem.c \
defer_item.c \
init.c \
kmem.c \
+ listxattr.c \
logitem.c \
rdwr.c \
topology.c \
diff --git a/repair/listxattr.c b/libxfs/listxattr.c
similarity index 99%
rename from repair/listxattr.c
rename to libxfs/listxattr.c
index 2af77b7b2..bedaca678 100644
--- a/repair/listxattr.c
+++ b/libxfs/listxattr.c
@@ -6,7 +6,7 @@
#include "libxfs.h"
#include "libxlog.h"
#include "libfrog/bitmap.h"
-#include "repair/listxattr.h"
+#include "listxattr.h"
/* Call a function for every entry in a shortform xattr structure. */
STATIC int
diff --git a/repair/listxattr.h b/libxfs/listxattr.h
similarity index 81%
rename from repair/listxattr.h
rename to libxfs/listxattr.h
index 2d26fce0f..cddd96af7 100644
--- a/repair/listxattr.h
+++ b/libxfs/listxattr.h
@@ -3,8 +3,8 @@
* Copyright (c) 2022-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
-#ifndef __REPAIR_LISTXATTR_H__
-#define __REPAIR_LISTXATTR_H__
+#ifndef __LIBXFS_LISTXATTR_H__
+#define __LIBXFS_LISTXATTR_H__
typedef int (*xattr_walk_fn)(struct xfs_inode *ip, unsigned int attr_flags,
const unsigned char *name, unsigned int namelen,
@@ -12,4 +12,4 @@ typedef int (*xattr_walk_fn)(struct xfs_inode *ip, unsigned int attr_flags,
int xattr_walk(struct xfs_inode *ip, xattr_walk_fn attr_fn, void *priv);
-#endif /* __REPAIR_LISTXATTR_H__ */
+#endif /* __LIBXFS_LISTXATTR_H__ */
diff --git a/repair/Makefile b/repair/Makefile
index e7445d53e..a36a95e35 100644
--- a/repair/Makefile
+++ b/repair/Makefile
@@ -24,7 +24,6 @@ HFILES = \
err_protos.h \
globals.h \
incore.h \
- listxattr.h \
pptr.h \
prefetch.h \
progress.h \
@@ -59,7 +58,6 @@ CFILES = \
incore_ext.c \
incore_ino.c \
init.c \
- listxattr.c \
phase1.c \
phase2.c \
phase3.c \
diff --git a/repair/pptr.c b/repair/pptr.c
index 8ec6a51d2..cc66e6372 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -11,7 +11,7 @@
#include "repair/globals.h"
#include "repair/err_protos.h"
#include "repair/slab.h"
-#include "repair/listxattr.h"
+#include "libxfs/listxattr.h"
#include "repair/threads.h"
#include "repair/incore.h"
#include "repair/pptr.h"
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH 5/7] libxfs: pass a transaction context through listxattr
2024-08-06 18:18 ` [PATCHSET v30.10 1/3] xfsprogs: " Darrick J. Wong
` (3 preceding siblings ...)
2024-08-06 18:20 ` [PATCH 4/7] libxfs: hoist listxattr from xfs_repair Darrick J. Wong
@ 2024-08-06 18:20 ` Darrick J. Wong
2024-08-06 18:20 ` [PATCH 6/7] xfs_db: add a command to list xattrs Darrick J. Wong
2024-08-06 18:21 ` [PATCH 7/7] xfs_property: add a new tool to administer fs properties Darrick J. Wong
6 siblings, 0 replies; 28+ messages in thread
From: Darrick J. Wong @ 2024-08-06 18:20 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, Dave Chinner, hch, dchinner, fstests,
linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Pass a transaction context so that a new caller can walk the attr names
and query the values all in one go without deadlocking on nested buffer
access.
While we're at it, make the existing xfs_repair callers try to use
empty transactions so that we don't deadlock on cycles in the xattr
structure.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Acked-by: Dave Chinner <dchinner@redhat.com>
---
libxfs/listxattr.c | 40 +++++++++++++++++++++++-----------------
libxfs/listxattr.h | 6 ++++--
repair/pptr.c | 7 ++++++-
3 files changed, 33 insertions(+), 20 deletions(-)
diff --git a/libxfs/listxattr.c b/libxfs/listxattr.c
index bedaca678..34205682f 100644
--- a/libxfs/listxattr.c
+++ b/libxfs/listxattr.c
@@ -11,6 +11,7 @@
/* Call a function for every entry in a shortform xattr structure. */
STATIC int
xattr_walk_sf(
+ struct xfs_trans *tp,
struct xfs_inode *ip,
xattr_walk_fn attr_fn,
void *priv)
@@ -22,7 +23,7 @@ xattr_walk_sf(
sfe = libxfs_attr_sf_firstentry(hdr);
for (i = 0; i < hdr->count; i++) {
- error = attr_fn(ip, sfe->flags, sfe->nameval, sfe->namelen,
+ error = attr_fn(tp, ip, sfe->flags, sfe->nameval, sfe->namelen,
&sfe->nameval[sfe->namelen], sfe->valuelen,
priv);
if (error)
@@ -37,6 +38,7 @@ xattr_walk_sf(
/* Call a function for every entry in this xattr leaf block. */
STATIC int
xattr_walk_leaf_entries(
+ struct xfs_trans *tp,
struct xfs_inode *ip,
xattr_walk_fn attr_fn,
struct xfs_buf *bp,
@@ -75,7 +77,7 @@ xattr_walk_leaf_entries(
valuelen = be32_to_cpu(name_rmt->valuelen);
}
- error = attr_fn(ip, entry->flags, name, namelen, value,
+ error = attr_fn(tp, ip, entry->flags, name, namelen, value,
valuelen, priv);
if (error)
return error;
@@ -91,6 +93,7 @@ xattr_walk_leaf_entries(
*/
STATIC int
xattr_walk_leaf(
+ struct xfs_trans *tp,
struct xfs_inode *ip,
xattr_walk_fn attr_fn,
void *priv)
@@ -98,18 +101,19 @@ xattr_walk_leaf(
struct xfs_buf *leaf_bp;
int error;
- error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino, 0, &leaf_bp);
+ error = -libxfs_attr3_leaf_read(tp, ip, ip->i_ino, 0, &leaf_bp);
if (error)
return error;
- error = xattr_walk_leaf_entries(ip, attr_fn, leaf_bp, priv);
- libxfs_trans_brelse(NULL, leaf_bp);
+ error = xattr_walk_leaf_entries(tp, ip, attr_fn, leaf_bp, priv);
+ libxfs_trans_brelse(tp, leaf_bp);
return error;
}
/* Find the leftmost leaf in the xattr dabtree. */
STATIC int
xattr_walk_find_leftmost_leaf(
+ struct xfs_trans *tp,
struct xfs_inode *ip,
struct bitmap *seen_blocks,
struct xfs_buf **leaf_bpp)
@@ -127,7 +131,7 @@ xattr_walk_find_leftmost_leaf(
for (;;) {
uint16_t magic;
- error = -libxfs_da3_node_read(NULL, ip, blkno, &bp,
+ error = -libxfs_da3_node_read(tp, ip, blkno, &bp,
XFS_ATTR_FORK);
if (error)
return error;
@@ -164,7 +168,7 @@ xattr_walk_find_leftmost_leaf(
/* Find the next level towards the leaves of the dabtree. */
btree = nodehdr.btree;
blkno = be32_to_cpu(btree->before);
- libxfs_trans_brelse(NULL, bp);
+ libxfs_trans_brelse(tp, bp);
/* Make sure we haven't seen this new block already. */
if (bitmap_test(seen_blocks, blkno, 1))
@@ -184,13 +188,14 @@ xattr_walk_find_leftmost_leaf(
return 0;
out_buf:
- libxfs_trans_brelse(NULL, bp);
+ libxfs_trans_brelse(tp, bp);
return error;
}
/* Call a function for every entry in a node-format xattr structure. */
STATIC int
xattr_walk_node(
+ struct xfs_trans *tp,
struct xfs_inode *ip,
xattr_walk_fn attr_fn,
void *priv)
@@ -204,12 +209,12 @@ xattr_walk_node(
bitmap_alloc(&seen_blocks);
- error = xattr_walk_find_leftmost_leaf(ip, seen_blocks, &leaf_bp);
+ error = xattr_walk_find_leftmost_leaf(tp, ip, seen_blocks, &leaf_bp);
if (error)
goto out_bitmap;
for (;;) {
- error = xattr_walk_leaf_entries(ip, attr_fn, leaf_bp,
+ error = xattr_walk_leaf_entries(tp, ip, attr_fn, leaf_bp,
priv);
if (error)
goto out_leaf;
@@ -220,13 +225,13 @@ xattr_walk_node(
if (leafhdr.forw == 0)
goto out_leaf;
- libxfs_trans_brelse(NULL, leaf_bp);
+ libxfs_trans_brelse(tp, leaf_bp);
/* Make sure we haven't seen this new leaf already. */
if (bitmap_test(seen_blocks, leafhdr.forw, 1))
goto out_bitmap;
- error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino,
+ error = -libxfs_attr3_leaf_read(tp, ip, ip->i_ino,
leafhdr.forw, &leaf_bp);
if (error)
goto out_bitmap;
@@ -238,7 +243,7 @@ xattr_walk_node(
}
out_leaf:
- libxfs_trans_brelse(NULL, leaf_bp);
+ libxfs_trans_brelse(tp, leaf_bp);
out_bitmap:
bitmap_free(&seen_blocks);
return error;
@@ -247,6 +252,7 @@ xattr_walk_node(
/* Call a function for every extended attribute in a file. */
int
xattr_walk(
+ struct xfs_trans *tp,
struct xfs_inode *ip,
xattr_walk_fn attr_fn,
void *priv)
@@ -257,15 +263,15 @@ xattr_walk(
return 0;
if (ip->i_af.if_format == XFS_DINODE_FMT_LOCAL)
- return xattr_walk_sf(ip, attr_fn, priv);
+ return xattr_walk_sf(tp, ip, attr_fn, priv);
/* attr functions require that the attr fork is loaded */
- error = -libxfs_iread_extents(NULL, ip, XFS_ATTR_FORK);
+ error = -libxfs_iread_extents(tp, ip, XFS_ATTR_FORK);
if (error)
return error;
if (libxfs_attr_is_leaf(ip))
- return xattr_walk_leaf(ip, attr_fn, priv);
+ return xattr_walk_leaf(tp, ip, attr_fn, priv);
- return xattr_walk_node(ip, attr_fn, priv);
+ return xattr_walk_node(tp, ip, attr_fn, priv);
}
diff --git a/libxfs/listxattr.h b/libxfs/listxattr.h
index cddd96af7..933e0f529 100644
--- a/libxfs/listxattr.h
+++ b/libxfs/listxattr.h
@@ -6,10 +6,12 @@
#ifndef __LIBXFS_LISTXATTR_H__
#define __LIBXFS_LISTXATTR_H__
-typedef int (*xattr_walk_fn)(struct xfs_inode *ip, unsigned int attr_flags,
+typedef int (*xattr_walk_fn)(struct xfs_trans *tp, struct xfs_inode *ip,
+ unsigned int attr_flags,
const unsigned char *name, unsigned int namelen,
const void *value, unsigned int valuelen, void *priv);
-int xattr_walk(struct xfs_inode *ip, xattr_walk_fn attr_fn, void *priv);
+int xattr_walk(struct xfs_trans *tp, struct xfs_inode *ip,
+ xattr_walk_fn attr_fn, void *priv);
#endif /* __LIBXFS_LISTXATTR_H__ */
diff --git a/repair/pptr.c b/repair/pptr.c
index cc66e6372..ee29e47a8 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -593,6 +593,7 @@ store_file_pptr_name(
/* Decide if this is a directory parent pointer and stash it if so. */
static int
examine_xattr(
+ struct xfs_trans *tp,
struct xfs_inode *ip,
unsigned int attr_flags,
const unsigned char *name,
@@ -1205,6 +1206,7 @@ check_file_parent_ptrs(
struct xfs_inode *ip,
struct file_scan *fscan)
{
+ struct xfs_trans *tp = NULL;
int error;
error = -init_slab(&fscan->file_pptr_recs, sizeof(struct file_pptr));
@@ -1215,7 +1217,10 @@ check_file_parent_ptrs(
fscan->have_garbage = false;
fscan->nr_file_pptrs = 0;
- error = xattr_walk(ip, examine_xattr, fscan);
+ libxfs_trans_alloc_empty(ip->i_mount, &tp);
+ error = xattr_walk(tp, ip, examine_xattr, fscan);
+ if (tp)
+ libxfs_trans_cancel(tp);
if (error && !no_modify)
do_error(_("ino %llu parent pointer scan failed: %s\n"),
(unsigned long long)ip->i_ino,
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH 6/7] xfs_db: add a command to list xattrs
2024-08-06 18:18 ` [PATCHSET v30.10 1/3] xfsprogs: " Darrick J. Wong
` (4 preceding siblings ...)
2024-08-06 18:20 ` [PATCH 5/7] libxfs: pass a transaction context through listxattr Darrick J. Wong
@ 2024-08-06 18:20 ` Darrick J. Wong
2024-08-06 18:21 ` [PATCH 7/7] xfs_property: add a new tool to administer fs properties Darrick J. Wong
6 siblings, 0 replies; 28+ messages in thread
From: Darrick J. Wong @ 2024-08-06 18:20 UTC (permalink / raw)
To: djwong, cem
Cc: Christoph Hellwig, Dave Chinner, hch, dchinner, fstests,
linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add a command to list extended attributes from xfs_db. We'll need this
later to manage the fs properties when unmounted.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Acked-by: Dave Chinner <dchinner@redhat.com>
---
db/attrset.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++
man/man8/xfs_db.8 | 28 +++++++
2 files changed, 229 insertions(+)
diff --git a/db/attrset.c b/db/attrset.c
index 9e53e63c9..e3ffb75aa 100644
--- a/db/attrset.c
+++ b/db/attrset.c
@@ -18,13 +18,21 @@
#include "malloc.h"
#include <sys/xattr.h>
#include "libfrog/fsproperties.h"
+#include "libxfs/listxattr.h"
+static int attr_list_f(int argc, char **argv);
static int attr_get_f(int argc, char **argv);
static int attr_set_f(int argc, char **argv);
static int attr_remove_f(int argc, char **argv);
+
+static void attrlist_help(void);
static void attrget_help(void);
static void attrset_help(void);
+static const cmdinfo_t attr_list_cmd =
+ { "attr_list", "alist", attr_list_f, 0, -1, 0,
+ N_("[-r|-s|-u|-p|-Z] [-v]"),
+ N_("list attributes on the current inode"), attrlist_help };
static const cmdinfo_t attr_get_cmd =
{ "attr_get", "aget", attr_get_f, 1, -1, 0,
N_("[-r|-s|-u|-p|-Z] name"),
@@ -38,6 +46,24 @@ static const cmdinfo_t attr_remove_cmd =
N_("[-r|-s|-u|-p|-Z] [-n] name"),
N_("remove the named attribute from the current inode"), attrset_help };
+static void
+attrlist_help(void)
+{
+ dbprintf(_(
+"\n"
+" The attr_list command provide interfaces for listing all extended attributes\n"
+" attached to an inode.\n"
+" There are 4 namespace flags:\n"
+" -r -- 'root'\n"
+" -u -- 'user' (default)\n"
+" -s -- 'secure'\n"
+" -p -- 'parent'\n"
+" -Z -- fs property\n"
+"\n"
+" -v -- print the value of the attributes\n"
+"\n"));
+}
+
static void
attrget_help(void)
{
@@ -87,6 +113,7 @@ attrset_init(void)
if (!expert_mode)
return;
+ add_command(&attr_list_cmd);
add_command(&attr_get_cmd);
add_command(&attr_set_cmd);
add_command(&attr_remove_cmd);
@@ -650,3 +677,177 @@ attr_get_f(
free((void *)args.name);
return 0;
}
+
+struct attrlist_ctx {
+ unsigned int attr_filter;
+ bool print_values;
+ bool fsprop;
+};
+
+static int
+attrlist_print(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ unsigned int attr_flags,
+ const unsigned char *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen,
+ void *priv)
+{
+ struct attrlist_ctx *ctx = priv;
+ struct xfs_da_args args = {
+ .geo = mp->m_attr_geo,
+ .whichfork = XFS_ATTR_FORK,
+ .op_flags = XFS_DA_OP_OKNOENT,
+ .dp = ip,
+ .owner = ip->i_ino,
+ .trans = tp,
+ .attr_filter = attr_flags & XFS_ATTR_NSP_ONDISK_MASK,
+ .name = name,
+ .namelen = namelen,
+ };
+ char namebuf[MAXNAMELEN + 1];
+ const char *print_name = namebuf;
+ int error;
+
+ if ((attr_flags & XFS_ATTR_NSP_ONDISK_MASK) != ctx->attr_filter)
+ return 0;
+
+ /* Make sure the name is null terminated. */
+ memcpy(namebuf, name, namelen);
+ namebuf[MAXNAMELEN] = 0;
+
+ if (ctx->fsprop) {
+ const char *p = attr_name_to_fsprop_name(namebuf);
+
+ if (!p)
+ return 0;
+
+ namelen -= (p - namebuf);
+ print_name = p;
+ }
+
+ if (!ctx->print_values) {
+ printf("%.*s\n", namelen, print_name);
+ return 0;
+ }
+
+ if (value) {
+ printf("%.*s=%.*s\n", namelen, print_name, valuelen,
+ (char *)value);
+ return 0;
+ }
+
+ libxfs_attr_sethash(&args);
+
+ /*
+ * Look up attr value with a maximally long length and a null buffer
+ * to return the value and the correct length.
+ */
+ args.valuelen = XATTR_SIZE_MAX;
+ error = -libxfs_attr_get(&args);
+ if (error) {
+ dbprintf(_("failed to get attr %s on inode %llu: %s\n"),
+ args.name, (unsigned long long)iocur_top->ino,
+ strerror(error));
+ return error;
+ }
+
+ printf("%.*s=%.*s\n", namelen, print_name, args.valuelen,
+ (char *)args.value);
+ kfree(args.value);
+
+ return 0;
+}
+
+static int
+attr_list_f(
+ int argc,
+ char **argv)
+{
+ struct attrlist_ctx ctx = { };
+ struct xfs_trans *tp;
+ struct xfs_inode *ip;
+ int c;
+ int error;
+
+ if (cur_typ == NULL) {
+ dbprintf(_("no current type\n"));
+ return 0;
+ }
+ if (cur_typ->typnm != TYP_INODE) {
+ dbprintf(_("current type is not inode\n"));
+ return 0;
+ }
+
+ while ((c = getopt(argc, argv, "ruspvZ")) != EOF) {
+ switch (c) {
+ /* namespaces */
+ case 'Z':
+ ctx.fsprop = true;
+ fallthrough;
+ case 'r':
+ ctx.attr_filter &= ~LIBXFS_ATTR_NS;
+ ctx.attr_filter |= LIBXFS_ATTR_ROOT;
+ break;
+ case 'u':
+ ctx.attr_filter &= ~LIBXFS_ATTR_NS;
+ break;
+ case 's':
+ ctx.attr_filter &= ~LIBXFS_ATTR_NS;
+ ctx.attr_filter |= LIBXFS_ATTR_SECURE;
+ break;
+ case 'p':
+ ctx.attr_filter &= ~LIBXFS_ATTR_NS;
+ ctx.attr_filter |= XFS_ATTR_PARENT;
+ break;
+
+ case 'v':
+ ctx.print_values = true;
+ break;
+ default:
+ dbprintf(_("bad option for attr_list command\n"));
+ return 0;
+ }
+ }
+
+ if (ctx.fsprop &&
+ (ctx.attr_filter & LIBXFS_ATTR_NS) != LIBXFS_ATTR_ROOT) {
+ dbprintf(_("fs properties must be ATTR_ROOT\n"));
+ return false;
+ }
+
+ if (optind != argc) {
+ dbprintf(_("too many options for attr_list (no name needed)\n"));
+ return 0;
+ }
+
+ error = -libxfs_trans_alloc_empty(mp, &tp);
+ if (error) {
+ dbprintf(_("failed to allocate empty transaction\n"));
+ return 0;
+ }
+
+ error = -libxfs_iget(mp, NULL, iocur_top->ino, 0, &ip);
+ if (error) {
+ dbprintf(_("failed to iget inode %llu: %s\n"),
+ (unsigned long long)iocur_top->ino,
+ strerror(error));
+ goto out_trans;
+ }
+
+ error = xattr_walk(tp, ip, attrlist_print, &ctx);
+ if (error) {
+ dbprintf(_("walking inode %llu xattrs: %s\n"),
+ (unsigned long long)iocur_top->ino,
+ strerror(error));
+ goto out_inode;
+ }
+
+out_inode:
+ libxfs_irele(ip);
+out_trans:
+ libxfs_trans_cancel(tp);
+ return 0;
+}
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index f0865b2df..291ec1c58 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -212,6 +212,34 @@ Only one namespace option can be specified.
Read the name from this file.
.RE
.TP
+.BI "attr_list [\-p|\-r|\-u|\-s|\-Z] [\-v] "
+Lists the extended attributes of the current file.
+.RS 1.0i
+.TP 0.4i
+.B \-p
+Sets the attribute in the parent namespace.
+Only one namespace option can be specified.
+.TP
+.B \-r
+Sets the attribute in the root namespace.
+Only one namespace option can be specified.
+.TP
+.B \-u
+Sets the attribute in the user namespace.
+Only one namespace option can be specified.
+.TP
+.B \-s
+Sets the attribute in the secure namespace.
+Only one namespace option can be specified.
+.TP
+.B \-Z
+Sets a filesystem property in the root namespace.
+Only one namespace option can be specified.
+.TP
+.B \-v
+Print the extended attribute values too.
+.RE
+.TP
.BI "attr_remove [\-p|\-r|\-u|\-s|\-Z] [\-n] [\-N " namefile "|" name "] "
Remove the specified extended attribute from the current file.
.RS 1.0i
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH 7/7] xfs_property: add a new tool to administer fs properties
2024-08-06 18:18 ` [PATCHSET v30.10 1/3] xfsprogs: " Darrick J. Wong
` (5 preceding siblings ...)
2024-08-06 18:20 ` [PATCH 6/7] xfs_db: add a command to list xattrs Darrick J. Wong
@ 2024-08-06 18:21 ` Darrick J. Wong
2024-08-07 16:10 ` Christoph Hellwig
6 siblings, 1 reply; 28+ messages in thread
From: Darrick J. Wong @ 2024-08-06 18:21 UTC (permalink / raw)
To: djwong, cem; +Cc: Dave Chinner, hch, dchinner, fstests, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create a tool to list, get, set, and remove filesystem properties.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>Acked-by: Dave Chinner <dchinner@redhat.com>
---
io/Makefile | 3 +-
io/xfs_property | 77 +++++++++++++++++++++++++++++++++++++++++++++++
man/man8/xfs_property.8 | 61 +++++++++++++++++++++++++++++++++++++
3 files changed, 140 insertions(+), 1 deletion(-)
create mode 100755 io/xfs_property
create mode 100644 man/man8/xfs_property.8
diff --git a/io/Makefile b/io/Makefile
index 0bdd05b57..c33d57f5e 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -6,7 +6,7 @@ TOPDIR = ..
include $(TOPDIR)/include/builddefs
LTCOMMAND = xfs_io
-LSRCFILES = xfs_bmap.sh xfs_freeze.sh xfs_mkfile.sh
+LSRCFILES = xfs_bmap.sh xfs_freeze.sh xfs_mkfile.sh xfs_property
HFILES = init.h io.h
CFILES = \
attr.c \
@@ -92,6 +92,7 @@ install: default
$(LTINSTALL) -m 755 xfs_bmap.sh $(PKG_SBIN_DIR)/xfs_bmap
$(LTINSTALL) -m 755 xfs_freeze.sh $(PKG_SBIN_DIR)/xfs_freeze
$(LTINSTALL) -m 755 xfs_mkfile.sh $(PKG_SBIN_DIR)/xfs_mkfile
+ $(LTINSTALL) -m 755 xfs_property $(PKG_SBIN_DIR)/xfs_property
install-dev:
-include .dep
diff --git a/io/xfs_property b/io/xfs_property
new file mode 100755
index 000000000..6f630312a
--- /dev/null
+++ b/io/xfs_property
@@ -0,0 +1,77 @@
+#!/bin/bash -f
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+# Author: Darrick J. Wong <djwong@kernel.org>
+#
+
+OPTS=""
+USAGE="Usage: xfs_property [-V] [mountpoint|device|file] [list [-v]|get name...|set name=value...|remove name...]"
+
+# Try to find a loop device associated with a file. We only want to return
+# one loopdev (multiple loop devices can attach to a single file) so we grab
+# the last line and return it if it's actually a block device.
+try_find_loop_dev_for_file() {
+ local x="$(losetup -O NAME -j "$1" 2> /dev/null | tail -n 1)"
+ test -b "${x}" && echo "${x}"
+}
+
+while getopts "V" c
+do
+ case $c in
+ V) xfs_io -p xfs_info -V
+ status=$?
+ exit ${status}
+ ;;
+ *) echo "${USAGE}" 1>&2
+ exit 2
+ ;;
+ esac
+done
+set -- extra "$@"
+shift $OPTIND
+
+if [ $# -lt 2 ]; then
+ echo "${USAGE}" 1>&2
+ exit 2
+fi
+
+target="$1"
+shift
+subcommand="$1"
+shift
+
+db_args=()
+io_args=()
+
+case "$subcommand" in
+"list")
+ vparam=
+ if [ $# -eq 1 ] && [ "$1" = "-v" ]; then
+ vparam=" -v"
+ fi
+ db_args+=('-c' "attr_list -Z${vparam}")
+ io_args+=('-c' "listfsprops${vparam}")
+ ;;
+"get"|"remove"|"set")
+ for arg in "$@"; do
+ db_args+=('-c' "attr_${subcommand} -Z ${arg/=/ }")
+ io_args+=('-c' "${subcommand}fsprops ${arg}")
+ done
+ ;;
+*)
+ echo "${USAGE}" 1>&2
+ exit 2
+esac
+
+# See if we can map the arg to a loop device
+loopdev="$(try_find_loop_dev_for_file "${target}")"
+test -n "${loopdev}" && target="${loopdev}"
+
+# If we find a mountpoint for the device, do a live query; otherwise try
+# reading the fs with xfs_db.
+if mountpt="$(findmnt -t xfs -f -n -o TARGET "${target}" 2> /dev/null)"; then
+ exec xfs_io -p xfs_property "${io_args[@]}" "${mountpt}"
+else
+ exec xfs_db -p xfs_property -x -c 'path /' "${db_args[@]}" "${target}"
+fi
diff --git a/man/man8/xfs_property.8 b/man/man8/xfs_property.8
new file mode 100644
index 000000000..19c1c0e37
--- /dev/null
+++ b/man/man8/xfs_property.8
@@ -0,0 +1,61 @@
+.TH xfs_property 8
+.SH NAME
+xfs_property \- examine and edit properties about an XFS filesystem
+.SH SYNOPSIS
+.B xfs_property
+.I target
+.B get
+.IR name ...
+.br
+.B xfs_property
+.I target
+.B list [ \-v ]
+.br
+.B xfs_property
+.I target
+.B set
+.IR name=value ...
+.br
+.B xfs_property
+.I target
+.B remove
+.IR name ...
+.br
+.B xfs_property \-V
+.SH DESCRIPTION
+.B xfs_property
+retrieves, lists, sets, or removes properties of an XFS filesystem.
+Filesystem properties are root-controlled attributes set on the root directory
+of the filesystem to enable the system administrator to coordinate with
+userspace programs.
+
+.I target
+is one of: the root directory of a mounted filesystem; a block device containing
+an XFS filesystem; or a regular file containing an XFS filesystem.
+
+.SH OPTIONS
+.TP
+.B \-V
+Print the version number and exits.
+
+.SH COMMANDS
+.TP
+.B get
+.IR name ...
+Prints the values of the given filesystem properties.
+.TP
+.B list
+Lists the names of all filesystem properties.
+If the
+.B -v
+flag is specified, prints the values as well.
+.TP
+.B set
+.IR name = value ...
+Sets the given filesystem properties to the specified values and prints what
+was set.
+.TP
+.B
+remove
+.IR name ...
+Unsets the given filesystem properties.
^ permalink raw reply related [flat|nested] 28+ messages in thread