@@ -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)
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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__ */
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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__ */