]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
xfs_db: add a command to list xattrs
authorDarrick J. Wong <djwong@kernel.org>
Fri, 26 Jul 2024 21:32:56 +0000 (14:32 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 31 Jul 2024 01:45:40 +0000 (18:45 -0700)
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>
db/attrset.c
man/man8/xfs_db.8

index baf346fa8e237430bd59ddebc2f3dcb0165399f6..a33542fe2be043348f7349d0ae322a0e364e7238 100644 (file)
 #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);
@@ -649,3 +676,177 @@ out:
                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;
+}
index f0865b2df4ecab3a85ddf9113a8de99292782361..291ec1c5827bfde9f7e871d94136dce1b6efcb2d 100644 (file)
@@ -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