Sometimes, it's not true that the root directory is always the first result from calling bulkstat with lastino == 0 assumed by xfsdump. Recently XFS_BULK_IREQ_SPECIAL_ROOT was introduced last year, yet that doesn't exist in old kernels. Alternatively, we can also use bulkstat to walk through all dirs and find the exact dir whose ino # of ".." is itself by getdents, and that should be considered as the root dir. Fixes: 25195ebf107d ("xfsdump: handle bind mount targets") Cc: Eric Sandeen <sandeen@xxxxxxxxxx> Signed-off-by: Gao Xiang <hsiangkao@xxxxxxxxxx> --- preliminary test with the original testcase is done... dump/content.c | 168 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 140 insertions(+), 28 deletions(-) diff --git a/dump/content.c b/dump/content.c index 30232d4..653d4eb 100644 --- a/dump/content.c +++ b/dump/content.c @@ -511,6 +511,132 @@ static bool_t create_inv_session( ix_t subtreecnt, size_t strmix); +struct fixrootino_context { + xfs_ino_t rootino; + struct dirent *gdp; + size_t gdsz; +}; + +static bool_t +scan_rootinode(int fd, + xfs_ino_t ino, + struct dirent *gdp, + size_t gdsz) +{ + while (1) { + struct dirent *p; + int nread; + + nread = getdents_wrap(fd, (char *)gdp, gdsz); + /* + * negative count indicates something very bad happened; + * try to gracefully end this dir. + */ + if (nread < 0) { + mlog(MLOG_NORMAL | MLOG_WARNING, +_("unable to read dirents for directory ino %llu: %s\n"), + ino, strerror(errno)); + /* !!! curtis looked at this, and pointed out that + * we could take some recovery action here. if the + * errno is appropriate, lseek64 to the value of + * doff field of the last dirent successfully + * obtained, and contiue the loop. + */ + nread = 0; /* pretend we are done */ + } + + /* no more directory entries: break; */ + if (!nread) + break; + + for (p = gdp; nread > 0; + nread -= (int)p->d_reclen, + assert(nread >= 0), + p = (struct dirent *)((char *)p + p->d_reclen)) { + if (!strcmp(p->d_name, "..") && p->d_ino == ino) { + mlog(MLOG_NITTY, "FOUND: name %s d_ino %llu\n", + p->d_name, ino); + return BOOL_TRUE; + } + } + } + return BOOL_FALSE; +} + +/* ARGSUSED */ +static int +cb_find_root_inode(void *arg1, + jdm_fshandle_t *fshandlep, + int fsfd, + struct xfs_bstat *statp) +{ + int fd; + struct fixrootino_context *ctx = arg1; + + /* open the directory named by statp*/ + fd = jdm_open(fshandlep, statp, O_RDONLY); + if (fd < 0) { + mlog(MLOG_NORMAL | MLOG_WARNING, _( + "unable to open directory: ino %llu: %s\n"), + statp->bs_ino, strerror(errno)); + return RV_OK; /* continue anyway */ + } + + if (scan_rootinode(fd, statp->bs_ino, ctx->gdp, ctx->gdsz)) { + ctx->rootino = statp->bs_ino; + return RV_NOMORE; + } + return RV_OK; +} + +static bool_t +fix_root_inode( + stat64_t *rootstat, + struct xfs_bstat *sc_rootxfsstatp) +{ + struct xfs_bstat *bstatbufp; + int rval; + struct fixrootino_context ctx; + rv_t rv = RV_OK; + + ctx.gdsz = sizeof(struct dirent) + NAME_MAX + 1; + if (ctx.gdsz < GETDENTSBUF_SZ_MIN) + ctx.gdsz = GETDENTSBUF_SZ_MIN; + + ctx.gdp = (struct dirent *)calloc(1, ctx.gdsz); + assert(ctx.gdp); + + /* already a root dir */ + if (scan_rootinode(sc_fsfd, rootstat->st_ino, ctx.gdp, ctx.gdsz)) + goto out; + + /* allocate a buffer for use by bigstat_iter */ + bstatbufp = (struct xfs_bstat *)calloc(BSTATBUFLEN, + sizeof(struct xfs_bstat)); + assert(bstatbufp); + + rval = bigstat_iter(sc_fshandlep, sc_fsfd, BIGSTAT_ITER_DIR, + (xfs_ino_t)0, cb_find_root_inode, + &ctx, NULL, NULL, (int *)&rv, preemptchk, + bstatbufp, BSTATBUFLEN); + if (rval) + return BOOL_FALSE; + + if (rv != RV_NOMORE && rv != RV_OK) + return BOOL_FALSE; + + mlog(MLOG_NORMAL | MLOG_NOTE, + "fix up rootino %lld, bind mount?\n", ctx.rootino); + rootstat->st_ino = ctx.rootino; +out: + if (bigstat_one(sc_fsfd, rootstat->st_ino, sc_rootxfsstatp) < 0) { + mlog( MLOG_ERROR, + _("failed to get bulkstat information for root inode\n")); + return BOOL_FALSE; + } + return BOOL_TRUE; +} + bool_t content_init(int argc, char *argv[], @@ -1381,6 +1507,18 @@ baseuuidbypass: return BOOL_FALSE; } + /* alloc a file system handle, to be used with the jdm_open() + * functions. + */ + sc_fshandlep = jdm_getfshandle(mntpnt); + if (!sc_fshandlep) { + mlog(MLOG_NORMAL, _( + "unable to construct a file system handle for %s: %s\n"), + mntpnt, + strerror(errno)); + return BOOL_FALSE; + } + /* figure out the ino for the root directory of the fs * and get its struct xfs_bstat for inomap_build(). This could * be a bind mount; don't ask for the mount point inode, @@ -1388,9 +1526,6 @@ baseuuidbypass: */ { stat64_t rootstat; - xfs_ino_t lastino = 0; - int ocount = 0; - struct xfs_fsop_bulkreq bulkreq; /* Get the inode of the mount point */ rval = fstat64(sc_fsfd, &rootstat); @@ -1404,33 +1539,10 @@ baseuuidbypass: (struct xfs_bstat *)calloc(1, sizeof(struct xfs_bstat)); assert(sc_rootxfsstatp); - /* Get the first valid (i.e. root) inode in this fs */ - bulkreq.lastip = (__u64 *)&lastino; - bulkreq.icount = 1; - bulkreq.ubuffer = sc_rootxfsstatp; - bulkreq.ocount = &ocount; - if (ioctl(sc_fsfd, XFS_IOC_FSBULKSTAT, &bulkreq) < 0) { - mlog(MLOG_ERROR, - _("failed to get bulkstat information for root inode\n")); + if (!fix_root_inode(&rootstat, sc_rootxfsstatp)) { + mlog(MLOG_ERROR, _("failed to fix root inode\n")); return BOOL_FALSE; } - - if (sc_rootxfsstatp->bs_ino != rootstat.st_ino) - mlog (MLOG_NORMAL | MLOG_NOTE, - _("root ino %lld differs from mount dir ino %lld, bind mount?\n"), - sc_rootxfsstatp->bs_ino, rootstat.st_ino); - } - - /* alloc a file system handle, to be used with the jdm_open() - * functions. - */ - sc_fshandlep = jdm_getfshandle(mntpnt); - if (!sc_fshandlep) { - mlog(MLOG_NORMAL, _( - "unable to construct a file system handle for %s: %s\n"), - mntpnt, - strerror(errno)); - return BOOL_FALSE; } if (preemptchk(PREEMPT_FULL)) { -- 2.18.1