@@ -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)
{
@@ -88,6 +114,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;
+}
@@ -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