From: Darrick J. Wong <djwong@xxxxxxxxxx> 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@xxxxxxxxxx> Reviewed-by: Christoph Hellwig <hch@xxxxxx> Acked-by: Dave Chinner <dchinner@xxxxxxxxxx> --- 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