]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_db: add a parents command to list the parents of a file
authorDarrick J. Wong <djwong@kernel.org>
Mon, 29 Jul 2024 23:23:23 +0000 (16:23 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 30 Jul 2024 00:01:12 +0000 (17:01 -0700)
Create a command to dump the parents of a file.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
db/namei.c
man/man8/xfs_db.8

index 41ccaa04b659fd617c5223b81144f7b5eb84e692..46b4cacb507e54bc50d70e83ca861c23a2f0444b 100644 (file)
@@ -596,6 +596,326 @@ static struct cmdinfo ls_cmd = {
        .help           = ls_help,
 };
 
+static void
+pptr_emit(
+       struct xfs_inode        *ip,
+       unsigned int            attr_flags,
+       const uint8_t           *name,
+       unsigned int            namelen,
+       const void              *value,
+       unsigned int            valuelen)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_ino_t               parent_ino;
+       uint32_t                parent_gen;
+       int                     error;
+
+       if (!(attr_flags & XFS_ATTR_PARENT))
+               return;
+
+       error = -libxfs_parent_from_attr(mp, attr_flags, name, namelen, value,
+                       valuelen, &parent_ino, &parent_gen);
+       if (error)
+               return;
+
+       dbprintf("%18llu:0x%08x %3d %.*s\n", parent_ino, parent_gen, namelen,
+                       namelen, name);
+}
+
+static int
+list_sf_pptrs(
+       struct xfs_inode                *ip)
+{
+       struct xfs_attr_sf_hdr          *hdr = ip->i_af.if_data;
+       struct xfs_attr_sf_entry        *sfe;
+       unsigned int                    i;
+
+       sfe = libxfs_attr_sf_firstentry(hdr);
+       for (i = 0; i < hdr->count; i++) {
+               pptr_emit(ip, sfe->flags, sfe->nameval, sfe->namelen,
+                               sfe->nameval + sfe->valuelen, sfe->valuelen);
+
+               sfe = xfs_attr_sf_nextentry(sfe);
+       }
+
+       return 0;
+}
+
+static void
+list_leaf_pptr_entries(
+       struct xfs_inode                *ip,
+       struct xfs_buf                  *bp)
+{
+       struct xfs_attr3_icleaf_hdr     ichdr;
+       struct xfs_mount                *mp = ip->i_mount;
+       struct xfs_attr_leafblock       *leaf = bp->b_addr;
+       struct xfs_attr_leaf_entry      *entry;
+       unsigned int                    i;
+
+       libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf);
+       entry = xfs_attr3_leaf_entryp(leaf);
+
+       for (i = 0; i < ichdr.count; entry++, i++) {
+               struct xfs_attr_leaf_name_local *name_loc;
+
+               /*
+                * Parent pointers cannot be remote values; don't bother
+                * decoding this xattr name.
+                */
+               if (!(entry->flags & XFS_ATTR_LOCAL))
+                       continue;
+
+               name_loc = xfs_attr3_leaf_name_local(leaf, i);
+               pptr_emit(ip, entry->flags, name_loc->nameval,
+                               name_loc->namelen,
+                               name_loc->nameval + name_loc->namelen,
+                               be16_to_cpu(name_loc->valuelen));
+       }
+}
+
+static int
+list_leaf_pptrs(
+       struct xfs_inode                *ip)
+{
+       struct xfs_buf                  *leaf_bp;
+       int                             error;
+
+       error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino, 0, &leaf_bp);
+       if (error)
+               return error;
+
+       list_leaf_pptr_entries(ip, leaf_bp);
+       libxfs_trans_brelse(NULL, leaf_bp);
+       return 0;
+}
+
+static int
+find_leftmost_attr_leaf(
+       struct xfs_inode                *ip,
+       struct xfs_buf                  **leaf_bpp)
+{
+       struct xfs_da3_icnode_hdr       nodehdr;
+       struct xfs_mount                *mp = ip->i_mount;
+       struct xfs_da_intnode           *node;
+       struct xfs_da_node_entry        *btree;
+       struct xfs_buf                  *bp;
+       xfs_dablk_t                     blkno = 0;
+       unsigned int                    expected_level = 0;
+       int                             error;
+
+       for (;;) {
+               uint16_t                magic;
+
+               error = -libxfs_da3_node_read(NULL, ip, blkno, &bp,
+                               XFS_ATTR_FORK);
+               if (error)
+                       return error;
+
+               node = bp->b_addr;
+               magic = be16_to_cpu(node->hdr.info.magic);
+               if (magic == XFS_ATTR_LEAF_MAGIC ||
+                   magic == XFS_ATTR3_LEAF_MAGIC)
+                       break;
+
+               error = EFSCORRUPTED;
+               if (magic != XFS_DA_NODE_MAGIC &&
+                   magic != XFS_DA3_NODE_MAGIC)
+                       goto out_buf;
+
+               libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node);
+
+               if (nodehdr.count == 0 || nodehdr.level >= XFS_DA_NODE_MAXDEPTH)
+                       goto out_buf;
+
+               /* Check the level from the root node. */
+               if (blkno == 0)
+                       expected_level = nodehdr.level - 1;
+               else if (expected_level != nodehdr.level)
+                       goto out_buf;
+               else
+                       expected_level--;
+
+               /* Find the next level towards the leaves of the dabtree. */
+               btree = nodehdr.btree;
+               blkno = be32_to_cpu(btree->before);
+               libxfs_trans_brelse(NULL, bp);
+       }
+
+       error = EFSCORRUPTED;
+       if (expected_level != 0)
+               goto out_buf;
+
+       *leaf_bpp = bp;
+       return 0;
+
+out_buf:
+       libxfs_trans_brelse(NULL, bp);
+       return error;
+}
+
+static int
+list_node_pptrs(
+       struct xfs_inode                *ip)
+{
+       struct xfs_attr3_icleaf_hdr     leafhdr;
+       struct xfs_mount                *mp = ip->i_mount;
+       struct xfs_attr_leafblock       *leaf;
+       struct xfs_buf                  *leaf_bp;
+       int                             error;
+
+       error = find_leftmost_attr_leaf(ip, &leaf_bp);
+       if (error)
+               return error;
+
+       for (;;) {
+               list_leaf_pptr_entries(ip, leaf_bp);
+
+               /* Find the right sibling of this leaf block. */
+               leaf = leaf_bp->b_addr;
+               libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
+               if (leafhdr.forw == 0)
+                       goto out_leaf;
+
+               libxfs_trans_brelse(NULL, leaf_bp);
+
+               error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino,
+                               leafhdr.forw, &leaf_bp);
+               if (error)
+                       return error;
+       }
+
+out_leaf:
+       libxfs_trans_brelse(NULL, leaf_bp);
+       return error;
+}
+
+static int
+list_pptrs(
+       struct xfs_inode        *ip)
+{
+       int                     error;
+
+       if (!libxfs_inode_hasattr(ip))
+               return 0;
+
+       if (ip->i_af.if_format == XFS_DINODE_FMT_LOCAL)
+               return list_sf_pptrs(ip);
+
+       /* attr functions require that the attr fork is loaded */
+       error = -libxfs_iread_extents(NULL, ip, XFS_ATTR_FORK);
+       if (error)
+               return error;
+
+       if (libxfs_attr_is_leaf(ip))
+               return list_leaf_pptrs(ip);
+
+       return list_node_pptrs(ip);
+}
+
+/* If the io cursor points to a file, list its parents. */
+static int
+parent_cur(
+       char                    *tag)
+{
+       struct xfs_inode        *ip;
+       int                     error = 0;
+
+       if (!xfs_has_parent(mp))
+               return 0;
+
+       if (iocur_top->typ != &typtab[TYP_INODE])
+               return ENOTDIR;
+
+       error = -libxfs_iget(mp, NULL, iocur_top->ino, 0, &ip);
+       if (error)
+               return error;
+
+       /* List the parents of a file. */
+       if (tag)
+               dbprintf(_("%s:\n"), tag);
+
+       error = list_pptrs(ip);
+       if (error)
+               goto rele;
+
+rele:
+       libxfs_irele(ip);
+       return error;
+}
+
+static void
+parent_help(void)
+{
+       dbprintf(_(
+"\n"
+" List the parents of the currently selected file.\n"
+"\n"
+" Parent pointers will be listed in the format:\n"
+" inode_number:inode_gen       ondisk_namehash:namehash        name_length     name\n"
+       ));
+}
+
+static int
+parent_f(
+       int                     argc,
+       char                    **argv)
+{
+       int                     c;
+       int                     error = 0;
+
+       while ((c = getopt(argc, argv, "")) != -1) {
+               switch (c) {
+               default:
+                       ls_help();
+                       return 0;
+               }
+       }
+
+       if (optind == argc) {
+               error = parent_cur(NULL);
+               if (error) {
+                       dbprintf("%s\n", strerror(error));
+                       exitcode = 1;
+               }
+
+               return 0;
+       }
+
+       for (c = optind; c < argc; c++) {
+               push_cur();
+
+               error = path_walk(argv[c]);
+               if (error)
+                       goto err_cur;
+
+               error = parent_cur(argv[c]);
+               if (error)
+                       goto err_cur;
+
+               pop_cur();
+       }
+
+       return 0;
+err_cur:
+       pop_cur();
+       if (error) {
+               dbprintf("%s: %s\n", argv[c], strerror(error));
+               exitcode = 1;
+       }
+       return 0;
+}
+
+static struct cmdinfo parent_cmd = {
+       .name           = "parent",
+       .altname        = "pptr",
+       .cfunc          = parent_f,
+       .argmin         = 0,
+       .argmax         = -1,
+       .canpush        = 0,
+       .args           = "[paths...]",
+       .help           = parent_help,
+};
+
 void
 namei_init(void)
 {
@@ -604,4 +924,7 @@ namei_init(void)
 
        ls_cmd.oneline = _("list directory contents");
        add_command(&ls_cmd);
+
+       parent_cmd.oneline = _("list parent pointers");
+       add_command(&parent_cmd);
 }
index a7f6d55ed8bed6ce3caa7e0c4b5875cb2e948299..937b17e79a35eeb428973d6377549ad26eeab771 100644 (file)
@@ -943,6 +943,15 @@ See the
 .B print
 command.
 .TP
+.BI "parent [" paths "]..."
+List the parents of a file.
+If a path resolves to a file, the parents of that file will be listed.
+If no paths are supplied and the IO cursor points at an inode, the parents of
+that file will be listed.
+
+The output format is:
+inode number, inode generation, ondisk namehash, namehash, name length, name.
+.TP
 .BI "path " dir_path
 Walk the directory tree to an inode using the supplied path.
 Absolute and relative paths are supported.