Re: [RFC PATCH] xfsdump: fix handling bind mount targets

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Nov 02, 2020 at 06:01:20PM +0800, Gao Xiang wrote:
> 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.

Is this patch supposed to make xfsrestore start using that?

> 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) {

Doesn't this fail on bind mounted directories?  The VFS remaps '..' on a
bind mount to the directory on the parent mount, which means...

# ls -di /
128 drwxr-xr-x 20 root root 317 May 30 10:05 //
# ls -ila /home/
total 16
      128 drwxr-xr-x  9 root   root     90 Apr 28  2018 ./
      128 drwxr-xr-x 20 root   root    317 May 30 10:05 ../

that reading '..' won't get you to the root of /home, it'll get you to
the root of the root filesystem.

--D

> +				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
> 



[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux