xfs_repair aborts execution due to an ASSERT at line 1419 from dir2.c

Solution In Progress - Updated -

Environment

  • Red Hat Enterprise Linux 7
  • XFS filesystem
  • xfsprogs <= 4.5.0-15.el7

Issue

xfs_repair aborts during execution in process_dir2.

xfs_repair: dir2.c:1419: process_dir2: Assertion `(ino != mp->m_sb.sb_rootino && ino != *parent) || (ino == mp->m_sb.sb_rootino && (ino == *parent || need_root_dotdot == 1))' failed.
Aborted (core dumped)

Resolution

An upstream patch for xfsprogs package has been suggested:

Author: Marco Benatto <mbenatto@redhat.com>
Date:   Thu Jun 14 19:01:29 2018 -0300

    xfs_repair: Fix root inode's parent when it's bogus for sf directory

    Currently when root inode is in short-form and its parent ino
    has an invalid value, process_sf_dir2() ends up not fixing it,
    because if verify_inum() fails we never get to the next case which
    would fix the root inode's parent pointer.

    This behavior triggers the following assert on process_dir2():

       ASSERT((ino != mp->m_sb.sb_rootino && ino != *parent) ||
            (ino == mp->m_sb.sb_rootino &&
            (ino == *parent || need_root_dotdot == 1)));

    This patch fixes this behavior by making sure we always properly
    handle rootino parent pointer in process_sf_dir2()

    Signed-off-by: Marco Benatto <mbenatto@redhat.com>

Root Cause

  • Currently, if the root inode is in short-form directory form and its parent pointer has an invalid inode number xfs_repair doesn't fix it.
    This happens due to a bug in process_sf_dir2() which handles directory parent's invalid inode numbers without taking in account the root inode directory.

  • If xfs_repair detects root inode parent has an invalid inode number (line 499 on code snippet below) inprocess_sf_dir2() function, the second else if is never reached leaving root inode's parent zeroed.

 490 
 491     /*
 492      * check parent (..) entry
 493      */
 494     *parent = M_DIROPS(mp)->sf_get_parent_ino(sfp);
 495 
 496     /*
 497      * if parent entry is bogus, null it out.  we'll fix it later .
 498      */
 499     if (verify_inum(mp, *parent))  { 
 500 
 501         do_warn(
 502 _("bogus .. inode number (%" PRIu64 ") in directory inode %" PRIu64 ", "),
 503                 *parent, ino);
 504         *parent = NULLFSINO;
 505         if (!no_modify)  {
 506             do_warn(_("clearing inode number\n"));
 507 
 508             M_DIROPS(mp)->sf_put_parent_ino(sfp, zero);
 509             *dino_dirty = 1;
 510             *repair = 1;
 511         } else  {
 512             do_warn(_("would clear inode number\n"));
 513         }
 514     } else if (ino == mp->m_sb.sb_rootino && ino != *parent) {
 515         /*
 516          * root directories must have .. == .
 517          */
 518         if (!no_modify)  {
 519             do_warn(
 520 _("corrected root directory %" PRIu64 " .. entry, was %" PRIu64 ", now %" PRIu64 "\n"),
 521                 ino, *parent, ino);
 522             *parent = ino;
 523             M_DIROPS(mp)->sf_put_parent_ino(sfp, ino);
 524             *dino_dirty = 1;
 525             *repair = 1;
 526         } else  {
 527             do_warn(
 528 _("would have corrected root directory %" PRIu64 " .. entry from %" PRIu64" to %" PRIu64 "\n"),
 529                 ino, *parent, ino);
 530         }
 531     } else if (ino == *parent && ino != mp->m_sb.sb_rootino)  {
 532         /*
 533          * likewise, non-root directories can't have .. pointing
 534          * to .
 535          */
 536         *parent = NULLFSINO;
 537         do_warn(
 538 _("bad .. entry in directory inode %" PRIu64 ", points to self, "),
 539             ino);
 540         if (!no_modify)  {
 541             do_warn(_("clearing inode number\n"));
 542 
 543             M_DIROPS(mp)->sf_put_parent_ino(sfp, zero);
 544             *dino_dirty = 1;
 545             *repair = 1;
 546         } else  {
 547             do_warn(_("would clear inode number\n"));
 548         }
 549     }

  • As effect the following ASSERT() is triggered right after, still in repair's Phase 3:
 ASSERT((ino != mp->m_sb.sb_rootino && ino != *parent) ||
     (ino == mp->m_sb.sb_rootino &&
         (ino == *parent || need_root_dotdot == 1)));
  • The RHBZ#1590334 Bugzilla ticket has been raised for this issue.

Diagnostic Steps

1) Use xfs_db tool to open the unmounted device:

# xfs_db /dev/loop0
xfs_db> 

2) Looking into superblock 0, retrieve which is the root inode number:

xfs_db> sb 0
xfs_db> p rootino
rootino = 128

3) Inspect the root inode looking to determine which directory format it's using:

xfs_db> inode 128
xfs_db> p core.format
core.format = 1 (local)
  • If core.format is equal to 1 it means XFS is using short-form directory format to store this directory information.

4) Look to the parent value on short-form header:

xfs_db> p u.sfdir2.hdr.parent.i4
u.sfdir2.hdr.parent.i4 = 0
  • On XFS filesystem the root inode's parent should point to root inode itself, in this case, it should point to ino 128 but it's pointing to ino 0 which is an invalid inode number.

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.

Close

Welcome! Check out the Getting Started with Red Hat page for quick tours and guides for common tasks.