@@ -20,6 +20,7 @@ CFILES = \
fiemap.c \
file.c \
freeze.c \
+ fsproperties.c \
fsuuid.c \
fsync.c \
getrusage.c \
new file mode 100644
@@ -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);
+}
@@ -89,6 +89,7 @@ init_commands(void)
utimes_init();
crc32cselftest_init();
exchangerange_init();
+ fsprops_init();
}
/*
@@ -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);
@@ -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