diff mbox

[21/21] xfsprogs: implement the upper half of parent pointers

Message ID 1525754479-12177-22-git-send-email-allison.henderson@oracle.com (mailing list archive)
State Superseded
Headers show

Commit Message

Allison Henderson May 8, 2018, 4:41 a.m. UTC
From: "Darrick J. Wong" <darrick.wong@oracle.com>

Add ioctl definitions to libxfs, build the necessary helpers into
libfrog and libhandle to iterate parents (and parent paths), then wire
up xfs_scrub to be able to query parent pointers from userspace.  The
goal of this patch is to exercise userspace, and is nowhere near a
complete solution.  A basic xfs_io parent command implementation
replaces ... whatever that is that's there now.

Totally missing: actual support in libxfs for working with parent ptrs
straight off the disk (mkfs, xfs_db, xfs_repair).

[achender: Minor syntax adjustments to sew solution in actual support
	   in libxfs for working with parent ptrs]

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 include/handle.h   |   2 +
 include/parent.h   |  18 ++
 include/path.h     |  19 +++
 io/parent.c        | 471 ++++++++++++++---------------------------------------
 libfrog/paths.c    | 136 ++++++++++++++++
 libhandle/Makefile |   2 +-
 libhandle/handle.c |   7 +-
 libhandle/parent.c | 325 ++++++++++++++++++++++++++++++++++++
 libxfs/xfs_fs.h    |   8 +
 scrub/inodes.c     |  26 +++
 scrub/inodes.h     |   2 +
 scrub/phase5.c     |   9 +-
 12 files changed, 661 insertions(+), 364 deletions(-)

Comments

Darrick J. Wong May 8, 2018, 5:45 p.m. UTC | #1
On Mon, May 07, 2018 at 09:41:19PM -0700, Allison Henderson wrote:
> From: "Darrick J. Wong" <darrick.wong@oracle.com>
> 
> Add ioctl definitions to libxfs, build the necessary helpers into
> libfrog and libhandle to iterate parents (and parent paths), then wire
> up xfs_scrub to be able to query parent pointers from userspace.  The
> goal of this patch is to exercise userspace, and is nowhere near a
> complete solution.  A basic xfs_io parent command implementation
> replaces ... whatever that is that's there now.
> 
> Totally missing: actual support in libxfs for working with parent ptrs
> straight off the disk (mkfs, xfs_db, xfs_repair).
> 
> [achender: Minor syntax adjustments to sew solution in actual support
> 	   in libxfs for working with parent ptrs]
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  include/handle.h   |   2 +
>  include/parent.h   |  18 ++
>  include/path.h     |  19 +++
>  io/parent.c        | 471 ++++++++++++++---------------------------------------
>  libfrog/paths.c    | 136 ++++++++++++++++
>  libhandle/Makefile |   2 +-
>  libhandle/handle.c |   7 +-
>  libhandle/parent.c | 325 ++++++++++++++++++++++++++++++++++++
>  libxfs/xfs_fs.h    |   8 +
>  scrub/inodes.c     |  26 +++
>  scrub/inodes.h     |   2 +
>  scrub/phase5.c     |   9 +-
>  12 files changed, 661 insertions(+), 364 deletions(-)
> 
> diff --git a/include/handle.h b/include/handle.h
> index 49f1441..00aa43d 100644
> --- a/include/handle.h
> +++ b/include/handle.h
> @@ -52,6 +52,8 @@ extern int  fssetdm_by_handle (void *__hanp, size_t __hlen,
>  
>  void fshandle_destroy(void);
>  
> +int handle_to_fsfd(void *hanp, char **path);
> +
>  #ifdef __cplusplus
>  }
>  #endif
> diff --git a/include/parent.h b/include/parent.h
> index 85cef85..33f8d85 100644
> --- a/include/parent.h
> +++ b/include/parent.h
> @@ -28,4 +28,22 @@ typedef struct parent_cursor {
>  	__u32	opaque[4];      /* an opaque cookie */
>  } parent_cursor_t;
>  
> +struct path_list;
> +
> +typedef int (*walk_pptr_fn)(struct xfs_pptr_info *pi, struct xfs_parent_ptr *pptr,
> +		void *arg);
> +typedef int (*walk_ppath_fn)(const char *mntpt, struct path_list *path,
> +		void *arg);
> +
> +#define WALK_PPTRS_ABORT	1
> +int fd_walk_pptrs(int fd, walk_pptr_fn fn, void *arg);
> +int handle_walk_pptrs(void *hanp, size_t hanlen, walk_pptr_fn fn, void *arg);
> +
> +#define WALK_PPATHS_ABORT	1
> +int fd_walk_ppaths(int fd, walk_ppath_fn fn, void *arg);
> +int handle_walk_ppaths(void *hanp, size_t hanlen, walk_ppath_fn fn, void *arg);
> +
> +int fd_to_path(int fd, char *path, size_t pathlen);
> +int handle_to_path(void *hanp, size_t hlen, char *path, size_t pathlen);
> +
>  #endif
> diff --git a/include/path.h b/include/path.h
> index 88dc44b..cbe4e19 100644
> --- a/include/path.h
> +++ b/include/path.h
> @@ -70,4 +70,23 @@ typedef struct fs_cursor {
>  extern void fs_cursor_initialise(char *__dir, uint __flags, fs_cursor_t *__cp);
>  extern fs_path_t *fs_cursor_next_entry(fs_cursor_t *__cp);
>  
> +/* Path information. */
> +
> +struct path_list;
> +struct path_component;
> +
> +struct path_component *path_component_init(const char *name);
> +void path_component_free(struct path_component *pc);
> +int path_component_change(struct path_component *pc, void *name,
> +		size_t namelen);
> +
> +struct path_list *path_list_init(void);
> +void path_list_free(struct path_list *path);
> +void path_list_add_parent_component(struct path_list *path,
> +		struct path_component *pc);
> +void path_list_add_component(struct path_list *path, struct path_component *pc);
> +void path_list_del_component(struct path_list *path, struct path_component *pc);
> +
> +ssize_t path_list_to_string(struct path_list *path, char *buf, size_t buflen);
> +
>  #endif	/* __PATH_H__ */
> diff --git a/io/parent.c b/io/parent.c
> index 55b8b49..ad51fe6 100644
> --- a/io/parent.c
> +++ b/io/parent.c
> @@ -21,366 +21,105 @@
>  #include "path.h"
>  #include "parent.h"
>  #include "handle.h"
> -#include "jdm.h"
>  #include "init.h"
>  #include "io.h"
>  
> -#define PARENTBUF_SZ		16384
> -#define BSTATBUF_SZ		16384
> -
>  static cmdinfo_t parent_cmd;
> -static int verbose_flag;
> -static int err_status;
> -static __u64 inodes_checked;
>  static char *mntpt;
>  
> -/*
> - * check out a parent entry to see if the values seem valid
> - */
> -static void
> -check_parent_entry(xfs_bstat_t *bstatp, parent_t *parent)
> -{
> -	int sts;
> -	char fullpath[PATH_MAX];
> -	struct stat statbuf;
> -	char *str;
> -
> -	snprintf(fullpath, parent->p_reclen, _("%s%s"), mntpt,
> -				((char*)parent)+sizeof(struct parent));
> -
> -	sts = lstat(fullpath, &statbuf);
> -	if (sts != 0) {
> -		fprintf(stderr,
> -			_("inode-path for inode: %llu is incorrect - path \"%s\" non-existent\n"),
> -			(unsigned long long) bstatp->bs_ino, fullpath);
> -		if (verbose_flag) {
> -			fprintf(stderr,
> -				_("path \"%s\" does not stat for inode: %llu; err = %s\n"),
> -				fullpath,
> -			       (unsigned long long) bstatp->bs_ino,
> -				strerror(errno));
> -		}
> -		err_status++;
> -		return;
> -	} else {
> -		if (verbose_flag > 1) {
> -			printf(_("path \"%s\" found\n"), fullpath);
> -		}
> -	}
> -
> -	if (statbuf.st_ino != bstatp->bs_ino) {
> -		fprintf(stderr,
> -			_("inode-path for inode: %llu is incorrect - wrong inode#\n"),
> -		       (unsigned long long) bstatp->bs_ino);
> -		if (verbose_flag) {
> -			fprintf(stderr,
> -				_("ino mismatch for path \"%s\" %llu vs %llu\n"),
> -				fullpath,
> -				(unsigned long long)statbuf.st_ino,
> -				(unsigned long long)bstatp->bs_ino);
> -		}
> -		err_status++;
> -		return;
> -	} else if (verbose_flag > 1) {
> -		printf(_("inode number match: %llu\n"),
> -			(unsigned long long)statbuf.st_ino);
> -	}
> -
> -	/* get parent path */
> -	str = strrchr(fullpath, '/');
> -	*str = '\0';
> -	sts = stat(fullpath, &statbuf);
> -	if (sts != 0) {
> -		fprintf(stderr,
> -			_("parent path \"%s\" does not stat: %s\n"),
> -			fullpath,
> -			strerror(errno));
> -		err_status++;
> -		return;
> -	} else {
> -		if (parent->p_ino != statbuf.st_ino) {
> -			fprintf(stderr,
> -				_("inode-path for inode: %llu is incorrect - wrong parent inode#\n"),
> -			       (unsigned long long) bstatp->bs_ino);
> -			if (verbose_flag) {
> -				fprintf(stderr,
> -					_("ino mismatch for path \"%s\" %llu vs %llu\n"),
> -					fullpath,
> -					(unsigned long long)parent->p_ino,
> -					(unsigned long long)statbuf.st_ino);
> -			}
> -			err_status++;
> -			return;
> -		} else {
> -			if (verbose_flag > 1) {
> -			       printf(_("parent ino match for %llu\n"),
> -				       (unsigned long long) parent->p_ino);
> -			}
> -		}
> -	}
> -}
> -
> -static void
> -check_parents(parent_t *parentbuf, size_t *parentbuf_size,
> -	     jdm_fshandle_t *fshandlep, xfs_bstat_t *statp)
> -{
> -	int error, i;
> -	__u32 count;
> -	parent_t *entryp;
> -
> -	do {
> -		error = jdm_parentpaths(fshandlep, statp, parentbuf, *parentbuf_size, &count);
> -
> -		if (error == ERANGE) {
> -			*parentbuf_size *= 2;
> -			parentbuf = (parent_t *)realloc(parentbuf, *parentbuf_size);
> -		} else if (error) {
> -			fprintf(stderr, _("parentpaths failed for ino %llu: %s\n"),
> -			       (unsigned long long) statp->bs_ino,
> -				strerror(errno));
> -			err_status++;
> -			break;
> -		}
> -	} while (error == ERANGE);
> -
> -
> -	if (count == 0) {
> -		/* no links for inode - something wrong here */
> -	       fprintf(stderr, _("inode-path for inode: %llu is missing\n"),
> -			       (unsigned long long) statp->bs_ino);
> -		err_status++;
> -	}
> -
> -	entryp = parentbuf;
> -	for (i = 0; i < count; i++) {
> -		check_parent_entry(statp, entryp);
> -		entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
> -	}
> -}
> -
>  static int
> -do_bulkstat(parent_t *parentbuf, size_t *parentbuf_size, xfs_bstat_t *bstatbuf,
> -	    int fsfd, jdm_fshandle_t *fshandlep)
> +pptr_print(
> +	struct xfs_pptr_info	*pi,
> +	struct xfs_parent_ptr	*pptr,
> +	void			*arg)
>  {
> -	__s32 buflenout;
> -	__u64 lastino = 0;
> -	xfs_bstat_t *p;
> -	xfs_bstat_t *endp;
> -	xfs_fsop_bulkreq_t bulkreq;
> -	struct stat mntstat;
> +	char			buf[XFS_PPTR_MAXNAMELEN + 1];
>  
> -	if (stat(mntpt, &mntstat)) {
> -		fprintf(stderr, _("can't stat mount point \"%s\": %s\n"),
> -			mntpt, strerror(errno));
> -		return 1;
> +	if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT) {
> +		printf(_("Root directory.\n"));
> +		return 0;
>  	}
>  
> -	bulkreq.lastip  = &lastino;
> -	bulkreq.icount  = BSTATBUF_SZ;
> -	bulkreq.ubuffer = (void *)bstatbuf;
> -	bulkreq.ocount  = &buflenout;
> -
> -	while (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT, &bulkreq) == 0) {
> -		if (*(bulkreq.ocount) == 0) {
> -			return 0;
> -		}
> -		for (p = bstatbuf, endp = bstatbuf + *bulkreq.ocount; p < endp; p++) {
> -
> -			/* inode being modified, get synced data with iget */
> -			if ( (!p->bs_nlink || !p->bs_mode) && p->bs_ino != 0 ) {
> -
> -				if (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) < 0) {
> -				    fprintf(stderr,
> -					  _("failed to get bulkstat information for inode %llu\n"),
> -					 (unsigned long long) p->bs_ino);
> -				    continue;
> -				}
> -				if (!p->bs_nlink || !p->bs_mode || !p->bs_ino) {
> -				    fprintf(stderr,
> -					  _("failed to get valid bulkstat information for inode %llu\n"),
> -					 (unsigned long long) p->bs_ino);
> -				    continue;
> -				}
> -			}
> -
> -			/* skip root */
> -			if (p->bs_ino == mntstat.st_ino) {
> -				continue;
> -			}
> -
> -			if (verbose_flag > 1) {
> -			       printf(_("checking inode %llu\n"),
> -				       (unsigned long long) p->bs_ino);
> -			}
> -
> -			/* print dotted progress */
> -			if ((inodes_checked % 100) == 0 && verbose_flag == 1) {
> -				printf("."); fflush(stdout);
> -			}
> -			inodes_checked++;
> -
> -			check_parents(parentbuf, parentbuf_size, fshandlep, p);
> -		}
> -
> -	}/*while*/
> -
> -	fprintf(stderr, _("syssgi bulkstat failed: %s\n"), strerror(errno));
> -	return 1;
> +	memcpy(buf, pptr->xpp_name, pptr->xpp_namelen);
> +	buf[pptr->xpp_namelen] = 0;
> +	printf(_("p_ino    = %llu\n"), (unsigned long long)pptr->xpp_ino);
> +	printf(_("p_gen    = %u\n"), (unsigned int)pptr->xpp_gen);
> +	printf(_("p_reclen = %u\n"), (unsigned int)pptr->xpp_namelen);
> +	printf(_("p_name   = \"%s\"\n\n"), buf);
> +	return 0;
>  }
>  
> -static int
> -parent_check(void)
> +int
> +print_parents(
> +	struct xfs_handle	*handle)
>  {
> -	int fsfd;
> -	jdm_fshandle_t *fshandlep;
> -	parent_t *parentbuf;
> -	size_t parentbuf_size = PARENTBUF_SZ;
> -	xfs_bstat_t *bstatbuf;
> -
> -	err_status = 0;
> -	inodes_checked = 0;
> -
> -	sync();
> -
> -        fsfd = file->fd;
> -
> -	fshandlep = jdm_getfshandle(mntpt);
> -	if (fshandlep == NULL) {
> -		fprintf(stderr, _("unable to open \"%s\" for jdm: %s\n"),
> -		      mntpt,
> -		      strerror(errno));
> -		return 1;
> -	}
> -
> -	/* allocate buffers */
> -        bstatbuf = (xfs_bstat_t *)calloc(BSTATBUF_SZ, sizeof(xfs_bstat_t));
> -	parentbuf = (parent_t *)malloc(parentbuf_size);
> -	if (!bstatbuf || !parentbuf) {
> -		fprintf(stderr, _("unable to allocate buffers: %s\n"),
> -			strerror(errno));
> -		err_status = 1;
> -		goto out;
> -	}
> +	int			ret;
>  
> -	if (do_bulkstat(parentbuf, &parentbuf_size, bstatbuf, fsfd, fshandlep) != 0)
> -		err_status++;
> -
> -	if (err_status > 0)
> -		fprintf(stderr, _("num errors: %d\n"), err_status);
> +	if (handle)
> +		ret = handle_walk_pptrs(handle, sizeof(*handle), pptr_print,
> +				NULL);
>  	else
> -		printf(_("succeeded checking %llu inodes\n"),
> -			(unsigned long long) inodes_checked);
> -
> -out:
> -	free(bstatbuf);
> -	free(parentbuf);
> -	free(fshandlep);
> -	return err_status;
> -}
> +		ret = fd_walk_pptrs(file->fd, pptr_print, NULL);
> +	if (ret)
> +		perror(file->name);
>  
> -static void
> -print_parent_entry(parent_t *parent, int fullpath)
> -{
> -       printf(_("p_ino    = %llu\n"),  (unsigned long long) parent->p_ino);
> -	printf(_("p_gen    = %u\n"),	parent->p_gen);
> -	printf(_("p_reclen = %u\n"),	parent->p_reclen);
> -	if (fullpath)
> -		printf(_("p_name   = \"%s%s\"\n"), mntpt,
> -					((char*)parent)+sizeof(struct parent));
> -	else
> -		printf(_("p_name   = \"%s\"\n"),
> -					((char*)parent)+sizeof(struct parent));
> +	return 0;
>  }
>  
>  static int
> -parent_list(int fullpath)
> -{
> -	void *handlep = NULL;
> -	size_t handlen;
> -	int error, i;
> -	int retval = 1;
> -	__u32 count;
> -	parent_t *entryp;
> -	parent_t *parentbuf = NULL;
> -	char *path = file->name;
> -	int pb_size = PARENTBUF_SZ;
> -
> -	/* XXXX for linux libhandle version - to set libhandle fsfd cache */
> -	{
> -		void *fshandle;
> -		size_t fshlen;
> -
> -		if (path_to_fshandle(mntpt, &fshandle, &fshlen) != 0) {
> -			fprintf(stderr, _("%s: failed path_to_fshandle \"%s\": %s\n"),
> -				progname, path, strerror(errno));
> -			goto error;
> -		}
> -		free_handle(fshandle, fshlen);
> -	}
> -
> -	if (path_to_handle(path, &handlep, &handlen) != 0) {
> -		fprintf(stderr, _("%s: path_to_handle failed for \"%s\"\n"), progname, path);
> -		goto error;
> -	}
> -
> -	do {
> -		parentbuf = (parent_t *)realloc(parentbuf, pb_size);
> -		if (!parentbuf) {
> -			fprintf(stderr, _("%s: unable to allocate parent buffer: %s\n"),
> -				progname, strerror(errno));
> -			goto error;
> -		}
> +path_print(
> +	const char		*mntpt,
> +	struct path_list	*path,
> +	void			*arg) {
>  
> -		if (fullpath) {
> -			error = parentpaths_by_handle(handlep,
> -						       handlen,
> -						       parentbuf,
> -						       pb_size,
> -						       &count);
> -		} else {
> -			error = parents_by_handle(handlep,
> -						   handlen,
> -						   parentbuf,
> -						   pb_size,
> -						   &count);
> -		}
> -		if (error == ERANGE) {
> -			pb_size *= 2;
> -		} else if (error) {
> -			fprintf(stderr, _("%s: %s call failed for \"%s\": %s\n"),
> -				progname, fullpath ? "parentpaths" : "parents",
> -				path, strerror(errno));
> -			goto error;
> -		}
> -	} while (error == ERANGE);
> +	char			buf[PATH_MAX];
> +	size_t			len = PATH_MAX;
> +	int			ret;
>  
> -	if (count == 0) {
> -		/* no links for inode - something wrong here */
> -		fprintf(stderr, _("%s: inode-path is missing\n"), progname);
> -		goto error;
> +	ret = snprintf(buf, len, "%s", mntpt);
> +	if (ret != strlen(mntpt)) {
> +		errno = ENOMEM;
> +		return -1;
>  	}
>  
> -	entryp = parentbuf;
> -	for (i = 0; i < count; i++) {
> -		print_parent_entry(entryp, fullpath);
> -		entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
> -	}
> +	ret = path_list_to_string(path, buf + ret, len - ret);
> +	if (ret < 0)
> +		return ret;
> +	return 0;
> +}
>  
> -	retval = 0;
> -error:
> -	free(handlep);
> -	free(parentbuf);
> -	return retval;
> +int
> +print_paths(
> +	struct xfs_handle	*handle)
> +{
> +	int			ret;
> +
> +	if (handle)
> +		ret = handle_walk_ppaths(handle, sizeof(*handle), path_print,
> +				NULL);
> + 	else
> +		ret = fd_walk_ppaths(file->fd, path_print, NULL);
> +	if (ret)
> +		perror(file->name);
> +	return 0;
>  }
>  
>  int
> -parent_f(int argc, char **argv)
> +parent_f(
> +	int			argc,
> +	char			**argv)
>  {
> -	int c;
> -	int listpath_flag = 0;
> -	int check_flag = 0;
> -	fs_path_t *fs;
> -	static int tab_init;
> +	struct xfs_handle	handle;
> +	void			*hanp = NULL;
> +	size_t			hlen;
> +	struct fs_path		*fs;
> +	char			*p;
> +	uint64_t		ino = 0;
> +	uint32_t		gen = 0;
> +	int			c;
> +	int			listpath_flag = 0;
> +	int			ret;
> +	static int		tab_init;
>  
>  	if (!tab_init) {
>  		tab_init = 1;
> @@ -394,46 +133,72 @@ parent_f(int argc, char **argv)
>  	}
>  	mntpt = fs->fs_dir;
>  
> -	verbose_flag = 0;
> -
> -	while ((c = getopt(argc, argv, "cpv")) != EOF) {
> +	while ((c = getopt(argc, argv, "p")) != EOF) {
>  		switch (c) {
> -		case 'c':
> -			check_flag = 1;
> -			break;
>  		case 'p':
>  			listpath_flag = 1;
>  			break;
> -		case 'v':
> -			verbose_flag++;
> -			break;
>  		default:
>  			return command_usage(&parent_cmd);
>  		}
>  	}
>  
> -	if (!check_flag && !listpath_flag) /* default case */
> -		exitcode = parent_list(listpath_flag);
> -	else {
> -		if (listpath_flag)
> -			exitcode = parent_list(listpath_flag);
> -		if (check_flag)
> -			exitcode = parent_check();
> +	/*
> +	 * Always initialize the fshandle table because we need it for
> +	 * the ppaths functions to work.
> +	 */
> +	ret = path_to_fshandle((char *)mntpt, &hanp, &hlen);
> +	if (ret) {
> +		perror(mntpt);
> +		return 0;
> + 	}
> + 
> +	if (optind + 2 == argc) {
> +		ino = strtoull(argv[optind], &p, 0);
> +		if (*p != '\0' || ino == 0) {
> +			fprintf(stderr,
> +				_("Bad inode number '%s'.\n"),
> +				argv[optind]);
> +			return 0;
> +		}
> +		gen = strtoul(argv[optind + 1], &p, 0);
> +		if (*p != '\0') {
> +			fprintf(stderr,
> +				_("Bad generation number '%s'.\n"),
> +				argv[optind + 1]);
> +			return 0;
> +		}
> +
> +		memcpy(&handle, hanp, sizeof(handle));
> +		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
> +				sizeof(handle.ha_fid.fid_len);
> +		handle.ha_fid.fid_pad = 0;
> +		handle.ha_fid.fid_ino = ino;
> +		handle.ha_fid.fid_gen = gen;
> +
>  	}
>  
> +	if (listpath_flag)
> +		exitcode = print_paths(ino ? &handle : NULL);
> +	else
> +		exitcode = print_parents(ino ? &handle : NULL);
> +
> +	if (hanp)
> +		free_handle(hanp, hlen);
> +
>  	return 0;
>  }
>  
>  static void
>  parent_help(void)
>  {
> -	printf(_(
> +printf(_(
>  "\n"
>  " list the current file's parents and their filenames\n"
>  "\n"
> -" -c -- check the current file's file system for parent consistency\n"
> -" -p -- list the current file's parents and their full paths\n"
> -" -v -- verbose mode\n"
> +" -p -- list the current file's paths up to the root\n"
> +"\n"
> +"If ino and gen are supplied, use them instead.\n"
>  "\n"));
>  }
>  
> @@ -444,9 +209,9 @@ parent_init(void)
>  	parent_cmd.cfunc = parent_f;
>  	parent_cmd.argmin = 0;
>  	parent_cmd.argmax = -1;
> -	parent_cmd.args = _("[-cpv]");
> +	parent_cmd.args = _("[-p] [ino gen]");
>  	parent_cmd.flags = CMD_NOMAP_OK;
> -	parent_cmd.oneline = _("print or check parent inodes");
> +	parent_cmd.oneline = _("print parent inodes");
>  	parent_cmd.help = parent_help;
>  
>  	if (expert)
> diff --git a/libfrog/paths.c b/libfrog/paths.c
> index c7895e9..9fb0140 100644
> --- a/libfrog/paths.c
> +++ b/libfrog/paths.c
> @@ -27,6 +27,7 @@
>  #include "path.h"
>  #include "input.h"
>  #include "project.h"
> +#include "list.h"
>  #include <limits.h>
>  
>  extern char *progname;
> @@ -632,3 +633,138 @@ fs_table_insert_project_path(
>  		exit(1);
>  	}
>  }
> +
> +
> +/* Structured path components. */
> +
> +struct path_list {
> +	struct list_head	p_head;
> +};
> +
> +struct path_component {
> +	struct list_head	pc_list;
> +	char			*pc_fname;
> +};
> +
> +/* Initialize a path component with a given name. */
> +struct path_component *
> +path_component_init(
> +	const char		*name)
> +{
> +	struct path_component	*pc;
> +
> +	pc = malloc(sizeof(struct path_component));
> +	if (!pc)
> +		return NULL;
> +	INIT_LIST_HEAD(&pc->pc_list);
> +	pc->pc_fname = strdup(name);
> +	if (!pc->pc_fname) {
> +		free(pc);
> +		return NULL;
> +	}
> +	return pc;
> +}
> +
> +/* Free a path component. */
> +void
> +path_component_free(
> +	struct path_component	*pc)
> +{
> +	free(pc->pc_fname);
> +	free(pc);
> +}
> +
> +/* Change a path component's filename. */
> +int
> +path_component_change(
> +	struct path_component	*pc,
> +	void			*name,
> +	size_t			namelen)
> +{
> +	void			*p;
> +
> +	p = realloc(pc->pc_fname, namelen + 1);
> +	if (!p)
> +		return -1;
> +	pc->pc_fname = p;
> +	memcpy(pc->pc_fname, name, namelen);
> +	pc->pc_fname[namelen] = 0;
> +	return 0;
> +}
> +
> +/* Initialize a pathname. */
> +struct path_list *
> +path_list_init(void)
> +{
> +	struct path_list	*path;
> +
> +	path = malloc(sizeof(struct path_list));
> +	if (!path)
> +		return NULL;
> +	INIT_LIST_HEAD(&path->p_head);
> +	return path;
> +}
> +
> +/* Empty out a pathname. */
> +void
> +path_list_free(
> +	struct path_list	*path)
> +{
> +	struct path_component	*pos;
> +	struct path_component	*n;
> +
> +	list_for_each_entry_safe(pos, n, &path->p_head, pc_list) {
> +		path_list_del_component(path, pos);
> +		path_component_free(pos);
> +	}
> +	free(path);
> +}
> +
> +/* Add a parent component to a pathname. */
> +void
> +path_list_add_parent_component(
> +	struct path_list	*path,
> +	struct path_component	*pc)
> +{
> +	list_add(&pc->pc_list, &path->p_head);
> +}
> +
> +/* Add a component to a pathname. */
> +void
> +path_list_add_component(
> +	struct path_list	*path,
> +	struct path_component	*pc)
> +{
> +	list_add_tail(&pc->pc_list, &path->p_head);
> +}
> +
> +/* Remove a component from a pathname. */
> +void
> +path_list_del_component(
> +	struct path_list	*path,
> +	struct path_component	*pc)
> +{
> +	list_del_init(&pc->pc_list);
> +}
> +
> +/* Convert a pathname into a string. */
> +ssize_t
> +path_list_to_string(
> +	struct path_list	*path,
> +	char			*buf,
> +	size_t			buflen)
> +{
> +	struct path_component	*pos;
> +	ssize_t			bytes = 0;
> +	int			ret;
> +
> +	list_for_each_entry(pos, &path->p_head, pc_list) {
> +		ret = snprintf(buf, buflen, "/%s", pos->pc_fname);
> +		if (ret != 1 + strlen(pos->pc_fname))
> +			return -1;
> +		bytes += ret;
> +		buf += ret;
> +		buflen -= ret;
> +	}
> +	return bytes;
> +}
> diff --git a/libhandle/Makefile b/libhandle/Makefile
> index fe1a2af..d3cea41 100644
> --- a/libhandle/Makefile
> +++ b/libhandle/Makefile
> @@ -16,7 +16,7 @@ else
>  LTLDFLAGS += -Wl,--version-script,libhandle.sym
>  endif
>  
> -CFILES = handle.c jdm.c
> +CFILES = handle.c jdm.c parent.c
>  LSRCFILES = libhandle.sym
>  
>  default: ltdepend $(LTLIBRARY)
> diff --git a/libhandle/handle.c b/libhandle/handle.c
> index 878d14d..a70fa32 100644
> --- a/libhandle/handle.c
> +++ b/libhandle/handle.c
> @@ -41,7 +41,6 @@ typedef union {
>  } comarg_t;
>  
>  static int obj_to_handle(char *, int, unsigned int, comarg_t, void**, size_t*);
> -static int handle_to_fsfd(void *, char **);
>  static char *path_to_fspath(char *path);
>  
>  
> @@ -214,8 +213,10 @@ handle_to_fshandle(
>  	return 0;
>  }
>  
> -static int
> -handle_to_fsfd(void *hanp, char **path)
> +int
> +handle_to_fsfd(
> +	void		*hanp,
> +	char		**path)
>  {
>  	struct fdhash	*fdhp;
>  
> diff --git a/libhandle/parent.c b/libhandle/parent.c
> new file mode 100644
> index 0000000..f6be3bd
> --- /dev/null
> +++ b/libhandle/parent.c
> @@ -0,0 +1,325 @@
> +/*
> + * Copyright (C) 2017 Oracle.  All Rights Reserved.
> + *
> + * Author: Darrick J. Wong <darrick.wong@oracle.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation,
> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
> + */
> +#include "platform_defs.h"
> +#include "xfs.h"
> +#include "xfs_arch.h"
> +#include "list.h"
> +#include "path.h"
> +#include "handle.h"
> +#include "parent.h"
> +
> +/* Allocate a buffer large enough for some parent pointer records. */
> +static inline struct xfs_pptr_info *
> +xfs_pptr_alloc(
> +      size_t                  nr_ptrs)
> +{
> +      struct xfs_pptr_info    *pi;
> +
> +      pi = malloc(XFS_PPTR_INFO_SIZEOF(nr_ptrs));
> +      if (!pi)
> +              return NULL;
> +      memset(pi, 0, sizeof(struct xfs_pptr_info));
> +      pi->pi_ptrs_size = nr_ptrs;
> +      return pi;
> +}
> +
> +/* Walk all parents of the given file handle. */
> +static int
> +handle_walk_parents(
> +	int			fd,
> +	struct xfs_handle	*handle,
> +	walk_pptr_fn		fn,
> +	void			*arg)
> +{
> +	struct xfs_pptr_info	*pi;
> +	struct xfs_parent_ptr	*p;
> +	unsigned int		i;
> +	ssize_t			ret = -1;
> +
> +	pi = xfs_pptr_alloc(4);
> +	if (!pi)
> +		return -1;
> +
> +	if (handle) {
> +		memcpy(&pi->pi_handle, handle, sizeof(struct xfs_handle));
> +		pi->pi_flags = XFS_PPTR_IFLAG_HANDLE;
> +	}
> +
> +	ret = ioctl(fd, XFS_IOC_GETPPOINTER, pi);
> +	while (!ret) {
> +		if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT) {
> +			ret = fn(pi, NULL, arg);
> +			break;
> +		}
> +		if (pi->pi_ptrs_used == 0)
> +			break;
> +		for (i = 0; i < pi->pi_ptrs_used; i++) {
> +			p = XFS_PPINFO_TO_PP(pi, i);
> +			ret = fn(pi, p, arg);
> +			if (ret)
> +				goto out_pi;
> +		}
> +		ret = ioctl(fd, XFS_IOC_GETPPOINTER, pi);
> +	}
> +
> +out_pi:
> +	free(pi);
> +	return ret;
> +}
> +
> +/* Walk all parent pointers of this handle. */
> +int
> +handle_walk_pptrs(
> +	void			*hanp,
> +	size_t			hlen,
> +	walk_pptr_fn		fn,
> +	void			*arg)
> +{
> +	char			*mntpt;
> +	int			fd;
> +
> +	if (hlen != sizeof(struct xfs_handle)) {
> +		errno = EINVAL;
> +		return -1;
> +	}
> +
> +	fd = handle_to_fsfd(hanp, &mntpt);
> +	if (fd < 0)
> +		return -1;
> +
> +	return handle_walk_parents(fd, hanp, fn, arg);
> +}
> +
> +/* Walk all parent pointers of this fd. */
> +int
> +fd_walk_pptrs(
> +	int			fd,
> +	walk_pptr_fn		fn,
> +	void			*arg)
> +{
> +	return handle_walk_parents(fd, NULL, fn, arg);
> +}
> +
> +struct walk_ppaths_info {
> +	walk_ppath_fn			fn;
> +	void				*arg;
> +	char				*mntpt;
> +	struct path_list		*path;
> +	int				fd;
> +};
> +
> +struct walk_ppath_level_info {
> +	struct xfs_handle		newhandle;
> +	struct path_component		*pc;
> +	struct walk_ppaths_info		*wpi;
> +};
> +
> +static int handle_walk_parent_paths(struct walk_ppaths_info *wpi,
> +		struct xfs_handle *handle);
> +
> +static int
> +handle_walk_parent_path_ptr(
> +	struct xfs_pptr_info		*pi,
> +	struct xfs_parent_ptr		*p,
> +	void				*arg)
> +{
> +	struct walk_ppath_level_info	*wpli = arg;
> +	struct walk_ppaths_info		*wpi = wpli->wpi;
> +	unsigned int			i;
> +	int				ret = 0;
> +
> +	if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT)
> +		return wpi->fn(wpi->mntpt, wpi->path, wpi->arg);
> +
> +	for (i = 0; i < pi->pi_ptrs_used; i++) {
> +		p = XFS_PPINFO_TO_PP(pi, i);
> +		ret = path_component_change(wpli->pc, p->xpp_name,
> +				p->xpp_namelen);
> +		if (ret)
> +			break;
> +		wpli->newhandle.ha_fid.fid_ino = p->xpp_ino;
> +		wpli->newhandle.ha_fid.fid_gen = p->xpp_gen;
> +		path_list_add_parent_component(wpi->path, wpli->pc);
> +		ret = handle_walk_parent_paths(wpi, &wpli->newhandle);
> +		path_list_del_component(wpi->path, wpli->pc);
> +		if (ret)
> +			break;
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * Recursively walk all parents of the given file handle; if we hit the
> + * fs root then we call the associated function with the constructed path.
> + */
> +static int
> +handle_walk_parent_paths(
> +	struct walk_ppaths_info		*wpi,
> +	struct xfs_handle		*handle)
> +{
> +	struct walk_ppath_level_info	*wpli;
> +	int				ret;
> +
> +	wpli = malloc(sizeof(struct walk_ppath_level_info));
> +	if (!wpli)
> +		return -1;
> +	wpli->pc = path_component_init("");
> +	if (!wpli->pc) {
> +		free(wpli);
> +		return -1;
> +	}
> +	wpli->wpi = wpi;
> +	memcpy(&wpli->newhandle, handle, sizeof(struct xfs_handle));
> +
> +	ret = handle_walk_parents(wpi->fd, handle, handle_walk_parent_path_ptr,
> +			wpli);
> +
> +	path_component_free(wpli->pc);
> +	free(wpli);
> +	return ret;
> +}
> +
> +/*
> + * Call the given function on all known paths from the vfs root to the inode
> + * described in the handle.
> + */
> +int
> +handle_walk_ppaths(
> +	void			*hanp,
> +	size_t			hlen,
> +	walk_ppath_fn		fn,
> +	void			*arg)
> +{
> +	struct walk_ppaths_info	wpi;
> +	ssize_t			ret;
> +
> +	if (hlen != sizeof(struct xfs_handle)) {
> +		errno = EINVAL;
> +		return -1;
> +	}
> +
> +	wpi.fd = handle_to_fsfd(hanp, &wpi.mntpt);
> +	if (wpi.fd < 0)
> +		return -1;
> +	wpi.path = path_list_init();
> +	if (!wpi.path)
> +		return -1;
> +	wpi.fn = fn;
> +	wpi.arg = arg;
> +
> +	ret = handle_walk_parent_paths(&wpi, hanp);
> +	path_list_free(wpi.path);
> +
> +	return ret;
> +}
> +
> +/*
> + * Call the given function on all known paths from the vfs root to the inode
> + * referred to by the file description.
> + */
> +int
> +fd_walk_ppaths(
> +	int			fd,
> +	walk_ppath_fn		fn,
> +	void			*arg)
> +{
> +	struct walk_ppaths_info	wpi;
> +	void			*hanp;
> +	size_t			hlen;
> +	int			fsfd;
> +	int			ret;
> +
> +	ret = fd_to_handle(fd, &hanp, &hlen);
> +	if (ret)
> +		return ret;
> +
> +	fsfd = handle_to_fsfd(hanp, &wpi.mntpt);
> +	if (fsfd < 0)
> +		return -1;
> +	wpi.fd = fd;
> +	wpi.path = path_list_init();
> +	if (!wpi.path)
> +		return -1;
> +	wpi.fn = fn;
> +	wpi.arg = arg;
> +
> +	ret = handle_walk_parent_paths(&wpi, hanp);
> +	path_list_free(wpi.path);
> +
> +	return ret;
> +}
> +
> +struct path_walk_info {
> +	char			*buf;
> +	size_t			len;
> +};
> +
> +/* Helper that stringifies the first full path that we find. */
> +static int
> +handle_to_path_walk(
> +	const char		*mntpt,
> +	struct path_list	*path,
> +	void			*arg)
> +{
> +	struct path_walk_info	*pwi = arg;
> +	int			ret;
> +
> +	ret = snprintf(pwi->buf, pwi->len, "%s", mntpt);
> +	if (ret != strlen(mntpt)) {
> +		errno = ENOMEM;
> +		return -1;
> +	}
> +
> +	ret = path_list_to_string(path, pwi->buf + ret, pwi->len - ret);
> +	if (ret < 0)
> +		return ret;
> +
> +	return WALK_PPATHS_ABORT;
> +}
> +
> +/* Return any eligible path to this file handle. */
> +int
> +handle_to_path(
> +	void			*hanp,
> +	size_t			hlen,
> +	char			*path,
> +	size_t			pathlen)
> +{
> +	struct path_walk_info	pwi;
> +
> +	pwi.buf = path;
> +	pwi.len = pathlen;
> +	return handle_walk_ppaths(hanp, hlen, handle_to_path_walk, &pwi);
> +}
> +
> +/* Return any eligible path to this file description. */
> +int
> +fd_to_path(
> +	int			fd,
> +	char			*path,
> +	size_t			pathlen)
> +{
> +	struct path_walk_info	pwi;
> +
> +	pwi.buf = path;
> +	pwi.len = pathlen;
> +	return fd_walk_ppaths(fd, handle_to_path_walk, &pwi);
> +}
> diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
> index e3ce233..aa613f9 100644
> --- a/libxfs/xfs_fs.h
> +++ b/libxfs/xfs_fs.h
> @@ -610,6 +610,14 @@ struct xfs_pptr_info {
>  #define XFS_PPINFO_TO_PP(info, idx)    \
>  	(&(((struct xfs_parent_ptr *)((char *)(info) + sizeof(*(info))))[(idx)]))
>  
> +#define XFS_PPTR_ALL_IFLAGS    (XFS_PPTR_IFLAG_HANDLE)
> +
> +/* partial results only */
> +#define XFS_PPTR_OFLAG_PARTIAL (1U << 0)
> +
> +/* target was the root directory */
> +#define XFS_PPTR_OFLAG_ROOT    (1U << 1)

Uhoh, I forgot about this chunk, which should be in the kernel patches
somewhere I guess...

--D

> +
>  /*
>   * ioctl limits
>   */
> diff --git a/scrub/inodes.c b/scrub/inodes.c
> index ccfb9e0..3fbcd1a 100644
> --- a/scrub/inodes.c
> +++ b/scrub/inodes.c
> @@ -31,6 +31,7 @@
>  #include "xfs_scrub.h"
>  #include "common.h"
>  #include "inodes.h"
> +#include "parent.h"
>  
>  /*
>   * Iterate a range of inodes.
> @@ -293,3 +294,28 @@ xfs_open_handle(
>  	return open_by_fshandle(handle, sizeof(*handle),
>  			O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY);
>  }
> +
> +/* Construct a description for an inode. */
> +void
> +xfs_scrub_ino_descr(
> +	struct scrub_ctx	*ctx,
> +	struct xfs_handle	*handle,
> +	char			*buf,
> +	size_t			buflen)
> +{
> +	uint64_t		ino;
> +	xfs_agnumber_t		agno;
> +	xfs_agino_t		agino;
> +	int			ret;
> +
> +	ret = handle_to_path(handle, sizeof(struct xfs_handle), buf, buflen);
> +	if (ret >= 0)
> +		return;
> +
> +	ino = handle->ha_fid.fid_ino;
> +	agno = ino / (1ULL << (ctx->inopblog + ctx->agblklog));
> +	agino = ino % (1ULL << (ctx->inopblog + ctx->agblklog));
> +	snprintf(buf, buflen, _("inode %"PRIu64" (%u/%u)"), ino, agno,
> +			agino);
> +}
> +
> diff --git a/scrub/inodes.h b/scrub/inodes.h
> index 693cb05..e94de0a 100644
> --- a/scrub/inodes.h
> +++ b/scrub/inodes.h
> @@ -28,5 +28,7 @@ bool xfs_scan_all_inodes(struct scrub_ctx *ctx, xfs_inode_iter_fn fn,
>  		void *arg);
>  
>  int xfs_open_handle(struct xfs_handle *handle);
> +void xfs_scrub_ino_descr(struct scrub_ctx *ctx, struct xfs_handle *handle,
> +		char *buf, size_t buflen);
>  
>  #endif /* XFS_SCRUB_INODES_H_ */
> diff --git a/scrub/phase5.c b/scrub/phase5.c
> index 01038f7..ecaaaaa 100644
> --- a/scrub/phase5.c
> +++ b/scrub/phase5.c
> @@ -245,16 +245,11 @@ xfs_scrub_connections(
>  	void			*arg)
>  {
>  	bool			*pmoveon = arg;
> -	char			descr[DESCR_BUFSZ];
> +	char			descr[PATH_MAX];
>  	bool			moveon = true;
> -	xfs_agnumber_t		agno;
> -	xfs_agino_t		agino;
>  	int			fd = -1;
>  
> -	agno = bstat->bs_ino / (1ULL << (ctx->inopblog + ctx->agblklog));
> -	agino = bstat->bs_ino % (1ULL << (ctx->inopblog + ctx->agblklog));
> -	snprintf(descr, DESCR_BUFSZ, _("inode %"PRIu64" (%u/%u)"),
> -			(uint64_t)bstat->bs_ino, agno, agino);
> +	xfs_scrub_ino_descr(ctx, handle, descr, PATH_MAX);
>  	background_sleep();
>  
>  	/* Warn about naming problems in xattrs. */
> -- 
> 2.7.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Eric Sandeen May 8, 2018, 8:52 p.m. UTC | #2
On 5/7/18 11:41 PM, Allison Henderson wrote:
> From: "Darrick J. Wong" <darrick.wong@oracle.com>
> 
> Add ioctl definitions to libxfs, build the necessary helpers into
> libfrog and libhandle to iterate parents (and parent paths), then wire
> up xfs_scrub to be able to query parent pointers from userspace.  The
> goal of this patch is to exercise userspace, and is nowhere near a
> complete solution.  A basic xfs_io parent command implementation
> replaces ... whatever that is that's there now.

I wonder if it'd be better to send a patch to nuke the current parent code,
and then another to add back something that works.  Same result in the end,
but it doesn't look like you're trying to fix old code; the patch itself is
pretty meaningless since it diffs against nonfunctional(?) code.

Not a huge deal, just a thought.

-Eric
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Darrick J. Wong May 8, 2018, 11:04 p.m. UTC | #3
On Tue, May 08, 2018 at 03:52:37PM -0500, Eric Sandeen wrote:
> On 5/7/18 11:41 PM, Allison Henderson wrote:
> > From: "Darrick J. Wong" <darrick.wong@oracle.com>
> > 
> > Add ioctl definitions to libxfs, build the necessary helpers into
> > libfrog and libhandle to iterate parents (and parent paths), then wire
> > up xfs_scrub to be able to query parent pointers from userspace.  The
> > goal of this patch is to exercise userspace, and is nowhere near a
> > complete solution.  A basic xfs_io parent command implementation
> > replaces ... whatever that is that's there now.
> 
> I wonder if it'd be better to send a patch to nuke the current parent code,
> and then another to add back something that works.  Same result in the end,
> but it doesn't look like you're trying to fix old code; the patch itself is
> pretty meaningless since it diffs against nonfunctional(?) code.

Trouble is, it's exported as a shared library in the xfslibs-dev package
(should that be libxfs-dev?) so depending on how conservative you like
to be we can't just rip it out.

(Though I suppose even Linus has occasionally allowed people to rip and
replace kernel/user ABIs when they can demonstrate that it was so broken
it never worked for anybody, ever. :P)

> Not a huge deal, just a thought.

Yeah, this patch was quite quick and dirty when I wrote it, on the
assumption that tons of other stuff was going to need reorganization by
the time there was a need to land this.

--D

> -Eric
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Allison Henderson May 8, 2018, 11:13 p.m. UTC | #4
On 05/08/2018 04:04 PM, Darrick J. Wong wrote:
> On Tue, May 08, 2018 at 03:52:37PM -0500, Eric Sandeen wrote:
>> On 5/7/18 11:41 PM, Allison Henderson wrote:
>>> From: "Darrick J. Wong" <darrick.wong@oracle.com>
>>>
>>> Add ioctl definitions to libxfs, build the necessary helpers into
>>> libfrog and libhandle to iterate parents (and parent paths), then wire
>>> up xfs_scrub to be able to query parent pointers from userspace.  The
>>> goal of this patch is to exercise userspace, and is nowhere near a
>>> complete solution.  A basic xfs_io parent command implementation
>>> replaces ... whatever that is that's there now.
>>
>> I wonder if it'd be better to send a patch to nuke the current parent code,
>> and then another to add back something that works.  Same result in the end,
>> but it doesn't look like you're trying to fix old code; the patch itself is
>> pretty meaningless since it diffs against nonfunctional(?) code.
> 
> Trouble is, it's exported as a shared library in the xfslibs-dev package
> (should that be libxfs-dev?) so depending on how conservative you like
> to be we can't just rip it out.
> 
> (Though I suppose even Linus has occasionally allowed people to rip and
> replace kernel/user ABIs when they can demonstrate that it was so broken
> it never worked for anybody, ever. :P)
> 
>> Not a huge deal, just a thought.
> 
> Yeah, this patch was quite quick and dirty when I wrote it, on the
> assumption that tons of other stuff was going to need reorganization by
> the time there was a need to land this.
> 
> --D

Oh, would you prefer I not include it then?  I do have an xfstest that's 
using it, but it's not a giant gap to close.  I just assumed you 
probably had a reason for the api you set up. :-)


> 
>> -Eric
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Darrick J. Wong May 9, 2018, 1:22 a.m. UTC | #5
On Tue, May 08, 2018 at 04:13:49PM -0700, Allison Henderson wrote:
> 
> 
> On 05/08/2018 04:04 PM, Darrick J. Wong wrote:
> > On Tue, May 08, 2018 at 03:52:37PM -0500, Eric Sandeen wrote:
> > > On 5/7/18 11:41 PM, Allison Henderson wrote:
> > > > From: "Darrick J. Wong" <darrick.wong@oracle.com>
> > > > 
> > > > Add ioctl definitions to libxfs, build the necessary helpers into
> > > > libfrog and libhandle to iterate parents (and parent paths), then wire
> > > > up xfs_scrub to be able to query parent pointers from userspace.  The
> > > > goal of this patch is to exercise userspace, and is nowhere near a
> > > > complete solution.  A basic xfs_io parent command implementation
> > > > replaces ... whatever that is that's there now.
> > > 
> > > I wonder if it'd be better to send a patch to nuke the current parent code,
> > > and then another to add back something that works.  Same result in the end,
> > > but it doesn't look like you're trying to fix old code; the patch itself is
> > > pretty meaningless since it diffs against nonfunctional(?) code.
> > 
> > Trouble is, it's exported as a shared library in the xfslibs-dev package
> > (should that be libxfs-dev?) so depending on how conservative you like
> > to be we can't just rip it out.
> > 
> > (Though I suppose even Linus has occasionally allowed people to rip and
> > replace kernel/user ABIs when they can demonstrate that it was so broken
> > it never worked for anybody, ever. :P)
> > 
> > > Not a huge deal, just a thought.
> > 
> > Yeah, this patch was quite quick and dirty when I wrote it, on the
> > assumption that tons of other stuff was going to need reorganization by
> > the time there was a need to land this.
> > 
> > --D
> 
> Oh, would you prefer I not include it then?  I do have an xfstest that's
> using it, but it's not a giant gap to close.  I just assumed you probably
> had a reason for the api you set up. :-)

I prefer my new APIs.  None of this parse my way through variable-length
records in a buffer crap, just call my callback function for every pptr
you find.  But I might be biased. :)

Let's have a look at what we'd be killing off:

> typedef struct parent {
> 	__u64	p_ino;
> 	__u32	p_gen;
> 	__u16	p_reclen;
> 	char	p_name[1];
> } parent_t;
> 
> typedef struct parent_cursor {
> 	__u32	opaque[4];      /* an opaque cookie */
> } parent_cursor_t;
> 
> extern int
> jdm_parents( jdm_fshandle_t *fshp,
> 		xfs_bstat_t *statp,
> 		struct parent *bufp, size_t bufsz,
> 		unsigned int *count);
> 
> extern int
> jdm_parentpaths( jdm_fshandle_t *fshp,
> 		xfs_bstat_t *statp,
> 		struct parent *bufp, size_t bufsz,
> 		unsigned int *count);

I suppose it wouldn't be hard to emulate these with the other code, but
do we care?

--D

> 
> > 
> > > -Eric
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Allison Henderson May 9, 2018, 1:32 a.m. UTC | #6
On 05/08/2018 06:22 PM, Darrick J. Wong wrote:
> On Tue, May 08, 2018 at 04:13:49PM -0700, Allison Henderson wrote:
>>
>>
>> On 05/08/2018 04:04 PM, Darrick J. Wong wrote:
>>> On Tue, May 08, 2018 at 03:52:37PM -0500, Eric Sandeen wrote:
>>>> On 5/7/18 11:41 PM, Allison Henderson wrote:
>>>>> From: "Darrick J. Wong" <darrick.wong@oracle.com>
>>>>>
>>>>> Add ioctl definitions to libxfs, build the necessary helpers into
>>>>> libfrog and libhandle to iterate parents (and parent paths), then wire
>>>>> up xfs_scrub to be able to query parent pointers from userspace.  The
>>>>> goal of this patch is to exercise userspace, and is nowhere near a
>>>>> complete solution.  A basic xfs_io parent command implementation
>>>>> replaces ... whatever that is that's there now.
>>>>
>>>> I wonder if it'd be better to send a patch to nuke the current parent code,
>>>> and then another to add back something that works.  Same result in the end,
>>>> but it doesn't look like you're trying to fix old code; the patch itself is
>>>> pretty meaningless since it diffs against nonfunctional(?) code.
>>>
>>> Trouble is, it's exported as a shared library in the xfslibs-dev package
>>> (should that be libxfs-dev?) so depending on how conservative you like
>>> to be we can't just rip it out.
>>>
>>> (Though I suppose even Linus has occasionally allowed people to rip and
>>> replace kernel/user ABIs when they can demonstrate that it was so broken
>>> it never worked for anybody, ever. :P)
>>>
>>>> Not a huge deal, just a thought.
>>>
>>> Yeah, this patch was quite quick and dirty when I wrote it, on the
>>> assumption that tons of other stuff was going to need reorganization by
>>> the time there was a need to land this.
>>>
>>> --D
>>
>> Oh, would you prefer I not include it then?  I do have an xfstest that's
>> using it, but it's not a giant gap to close.  I just assumed you probably
>> had a reason for the api you set up. :-)
> 
> I prefer my new APIs.  None of this parse my way through variable-length
> records in a buffer crap, just call my callback function for every pptr
> you find.  But I might be biased. :)
> 
> Let's have a look at what we'd be killing off:
> 
>> typedef struct parent {
>> 	__u64	p_ino;
>> 	__u32	p_gen;
>> 	__u16	p_reclen;
>> 	char	p_name[1];
>> } parent_t;
>>
>> typedef struct parent_cursor {
>> 	__u32	opaque[4];      /* an opaque cookie */
>> } parent_cursor_t;
>>
>> extern int
>> jdm_parents( jdm_fshandle_t *fshp,
>> 		xfs_bstat_t *statp,
>> 		struct parent *bufp, size_t bufsz,
>> 		unsigned int *count);
>>
>> extern int
>> jdm_parentpaths( jdm_fshandle_t *fshp,
>> 		xfs_bstat_t *statp,
>> 		struct parent *bufp, size_t bufsz,
>> 		unsigned int *count);
> 
> I suppose it wouldn't be hard to emulate these with the other code, but
> do we care?
> 
> --D
Alrighty, I can clean those in the next revision then.

Allison
> 
>>
>>>
>>>> -Eric
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> More majordomo info at  https://urldefense.proofpoint.com/v2/url?u=http-3A__vger.kernel.org_majordomo-2Dinfo.html&d=DwIBAg&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=LHZQ8fHvy6wDKXGTWcm97burZH5sQKHRDMaY1UthQxc&m=ZYhT49wgnXi-BAqQb6XvZtIJlZmC9-GTIfYkkgzS3x8&s=oDk5xa1iaNp87c2xfX0TDTQc9Hs9-RraSgWXeoZvQkU&e=
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  https://urldefense.proofpoint.com/v2/url?u=http-3A__vger.kernel.org_majordomo-2Dinfo.html&d=DwIBAg&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=LHZQ8fHvy6wDKXGTWcm97burZH5sQKHRDMaY1UthQxc&m=ZYhT49wgnXi-BAqQb6XvZtIJlZmC9-GTIfYkkgzS3x8&s=oDk5xa1iaNp87c2xfX0TDTQc9Hs9-RraSgWXeoZvQkU&e=
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  https://urldefense.proofpoint.com/v2/url?u=http-3A__vger.kernel.org_majordomo-2Dinfo.html&d=DwIBAg&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=LHZQ8fHvy6wDKXGTWcm97burZH5sQKHRDMaY1UthQxc&m=ZYhT49wgnXi-BAqQb6XvZtIJlZmC9-GTIfYkkgzS3x8&s=oDk5xa1iaNp87c2xfX0TDTQc9Hs9-RraSgWXeoZvQkU&e=
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Allison Henderson May 9, 2018, 1:39 a.m. UTC | #7
On 05/08/2018 10:45 AM, Darrick J. Wong wrote:
> On Mon, May 07, 2018 at 09:41:19PM -0700, Allison Henderson wrote:
>> From: "Darrick J. Wong" <darrick.wong@oracle.com>
>>
>> Add ioctl definitions to libxfs, build the necessary helpers into
>> libfrog and libhandle to iterate parents (and parent paths), then wire
>> up xfs_scrub to be able to query parent pointers from userspace.  The
>> goal of this patch is to exercise userspace, and is nowhere near a
>> complete solution.  A basic xfs_io parent command implementation
>> replaces ... whatever that is that's there now.
>>
>> Totally missing: actual support in libxfs for working with parent ptrs
>> straight off the disk (mkfs, xfs_db, xfs_repair).
>>
>> [achender: Minor syntax adjustments to sew solution in actual support
>> 	   in libxfs for working with parent ptrs]
>>
>> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
>>   include/handle.h   |   2 +
>>   include/parent.h   |  18 ++
>>   include/path.h     |  19 +++
>>   io/parent.c        | 471 ++++++++++++++---------------------------------------
>>   libfrog/paths.c    | 136 ++++++++++++++++
>>   libhandle/Makefile |   2 +-
>>   libhandle/handle.c |   7 +-
>>   libhandle/parent.c | 325 ++++++++++++++++++++++++++++++++++++
>>   libxfs/xfs_fs.h    |   8 +
>>   scrub/inodes.c     |  26 +++
>>   scrub/inodes.h     |   2 +
>>   scrub/phase5.c     |   9 +-
>>   12 files changed, 661 insertions(+), 364 deletions(-)
>>
>> diff --git a/include/handle.h b/include/handle.h
>> index 49f1441..00aa43d 100644
>> --- a/include/handle.h
>> +++ b/include/handle.h
>> @@ -52,6 +52,8 @@ extern int  fssetdm_by_handle (void *__hanp, size_t __hlen,
>>   
>>   void fshandle_destroy(void);
>>   
>> +int handle_to_fsfd(void *hanp, char **path);
>> +
>>   #ifdef __cplusplus
>>   }
>>   #endif
>> diff --git a/include/parent.h b/include/parent.h
>> index 85cef85..33f8d85 100644
>> --- a/include/parent.h
>> +++ b/include/parent.h
>> @@ -28,4 +28,22 @@ typedef struct parent_cursor {
>>   	__u32	opaque[4];      /* an opaque cookie */
>>   } parent_cursor_t;
>>   
>> +struct path_list;
>> +
>> +typedef int (*walk_pptr_fn)(struct xfs_pptr_info *pi, struct xfs_parent_ptr *pptr,
>> +		void *arg);
>> +typedef int (*walk_ppath_fn)(const char *mntpt, struct path_list *path,
>> +		void *arg);
>> +
>> +#define WALK_PPTRS_ABORT	1
>> +int fd_walk_pptrs(int fd, walk_pptr_fn fn, void *arg);
>> +int handle_walk_pptrs(void *hanp, size_t hanlen, walk_pptr_fn fn, void *arg);
>> +
>> +#define WALK_PPATHS_ABORT	1
>> +int fd_walk_ppaths(int fd, walk_ppath_fn fn, void *arg);
>> +int handle_walk_ppaths(void *hanp, size_t hanlen, walk_ppath_fn fn, void *arg);
>> +
>> +int fd_to_path(int fd, char *path, size_t pathlen);
>> +int handle_to_path(void *hanp, size_t hlen, char *path, size_t pathlen);
>> +
>>   #endif
>> diff --git a/include/path.h b/include/path.h
>> index 88dc44b..cbe4e19 100644
>> --- a/include/path.h
>> +++ b/include/path.h
>> @@ -70,4 +70,23 @@ typedef struct fs_cursor {
>>   extern void fs_cursor_initialise(char *__dir, uint __flags, fs_cursor_t *__cp);
>>   extern fs_path_t *fs_cursor_next_entry(fs_cursor_t *__cp);
>>   
>> +/* Path information. */
>> +
>> +struct path_list;
>> +struct path_component;
>> +
>> +struct path_component *path_component_init(const char *name);
>> +void path_component_free(struct path_component *pc);
>> +int path_component_change(struct path_component *pc, void *name,
>> +		size_t namelen);
>> +
>> +struct path_list *path_list_init(void);
>> +void path_list_free(struct path_list *path);
>> +void path_list_add_parent_component(struct path_list *path,
>> +		struct path_component *pc);
>> +void path_list_add_component(struct path_list *path, struct path_component *pc);
>> +void path_list_del_component(struct path_list *path, struct path_component *pc);
>> +
>> +ssize_t path_list_to_string(struct path_list *path, char *buf, size_t buflen);
>> +
>>   #endif	/* __PATH_H__ */
>> diff --git a/io/parent.c b/io/parent.c
>> index 55b8b49..ad51fe6 100644
>> --- a/io/parent.c
>> +++ b/io/parent.c
>> @@ -21,366 +21,105 @@
>>   #include "path.h"
>>   #include "parent.h"
>>   #include "handle.h"
>> -#include "jdm.h"
>>   #include "init.h"
>>   #include "io.h"
>>   
>> -#define PARENTBUF_SZ		16384
>> -#define BSTATBUF_SZ		16384
>> -
>>   static cmdinfo_t parent_cmd;
>> -static int verbose_flag;
>> -static int err_status;
>> -static __u64 inodes_checked;
>>   static char *mntpt;
>>   
>> -/*
>> - * check out a parent entry to see if the values seem valid
>> - */
>> -static void
>> -check_parent_entry(xfs_bstat_t *bstatp, parent_t *parent)
>> -{
>> -	int sts;
>> -	char fullpath[PATH_MAX];
>> -	struct stat statbuf;
>> -	char *str;
>> -
>> -	snprintf(fullpath, parent->p_reclen, _("%s%s"), mntpt,
>> -				((char*)parent)+sizeof(struct parent));
>> -
>> -	sts = lstat(fullpath, &statbuf);
>> -	if (sts != 0) {
>> -		fprintf(stderr,
>> -			_("inode-path for inode: %llu is incorrect - path \"%s\" non-existent\n"),
>> -			(unsigned long long) bstatp->bs_ino, fullpath);
>> -		if (verbose_flag) {
>> -			fprintf(stderr,
>> -				_("path \"%s\" does not stat for inode: %llu; err = %s\n"),
>> -				fullpath,
>> -			       (unsigned long long) bstatp->bs_ino,
>> -				strerror(errno));
>> -		}
>> -		err_status++;
>> -		return;
>> -	} else {
>> -		if (verbose_flag > 1) {
>> -			printf(_("path \"%s\" found\n"), fullpath);
>> -		}
>> -	}
>> -
>> -	if (statbuf.st_ino != bstatp->bs_ino) {
>> -		fprintf(stderr,
>> -			_("inode-path for inode: %llu is incorrect - wrong inode#\n"),
>> -		       (unsigned long long) bstatp->bs_ino);
>> -		if (verbose_flag) {
>> -			fprintf(stderr,
>> -				_("ino mismatch for path \"%s\" %llu vs %llu\n"),
>> -				fullpath,
>> -				(unsigned long long)statbuf.st_ino,
>> -				(unsigned long long)bstatp->bs_ino);
>> -		}
>> -		err_status++;
>> -		return;
>> -	} else if (verbose_flag > 1) {
>> -		printf(_("inode number match: %llu\n"),
>> -			(unsigned long long)statbuf.st_ino);
>> -	}
>> -
>> -	/* get parent path */
>> -	str = strrchr(fullpath, '/');
>> -	*str = '\0';
>> -	sts = stat(fullpath, &statbuf);
>> -	if (sts != 0) {
>> -		fprintf(stderr,
>> -			_("parent path \"%s\" does not stat: %s\n"),
>> -			fullpath,
>> -			strerror(errno));
>> -		err_status++;
>> -		return;
>> -	} else {
>> -		if (parent->p_ino != statbuf.st_ino) {
>> -			fprintf(stderr,
>> -				_("inode-path for inode: %llu is incorrect - wrong parent inode#\n"),
>> -			       (unsigned long long) bstatp->bs_ino);
>> -			if (verbose_flag) {
>> -				fprintf(stderr,
>> -					_("ino mismatch for path \"%s\" %llu vs %llu\n"),
>> -					fullpath,
>> -					(unsigned long long)parent->p_ino,
>> -					(unsigned long long)statbuf.st_ino);
>> -			}
>> -			err_status++;
>> -			return;
>> -		} else {
>> -			if (verbose_flag > 1) {
>> -			       printf(_("parent ino match for %llu\n"),
>> -				       (unsigned long long) parent->p_ino);
>> -			}
>> -		}
>> -	}
>> -}
>> -
>> -static void
>> -check_parents(parent_t *parentbuf, size_t *parentbuf_size,
>> -	     jdm_fshandle_t *fshandlep, xfs_bstat_t *statp)
>> -{
>> -	int error, i;
>> -	__u32 count;
>> -	parent_t *entryp;
>> -
>> -	do {
>> -		error = jdm_parentpaths(fshandlep, statp, parentbuf, *parentbuf_size, &count);
>> -
>> -		if (error == ERANGE) {
>> -			*parentbuf_size *= 2;
>> -			parentbuf = (parent_t *)realloc(parentbuf, *parentbuf_size);
>> -		} else if (error) {
>> -			fprintf(stderr, _("parentpaths failed for ino %llu: %s\n"),
>> -			       (unsigned long long) statp->bs_ino,
>> -				strerror(errno));
>> -			err_status++;
>> -			break;
>> -		}
>> -	} while (error == ERANGE);
>> -
>> -
>> -	if (count == 0) {
>> -		/* no links for inode - something wrong here */
>> -	       fprintf(stderr, _("inode-path for inode: %llu is missing\n"),
>> -			       (unsigned long long) statp->bs_ino);
>> -		err_status++;
>> -	}
>> -
>> -	entryp = parentbuf;
>> -	for (i = 0; i < count; i++) {
>> -		check_parent_entry(statp, entryp);
>> -		entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
>> -	}
>> -}
>> -
>>   static int
>> -do_bulkstat(parent_t *parentbuf, size_t *parentbuf_size, xfs_bstat_t *bstatbuf,
>> -	    int fsfd, jdm_fshandle_t *fshandlep)
>> +pptr_print(
>> +	struct xfs_pptr_info	*pi,
>> +	struct xfs_parent_ptr	*pptr,
>> +	void			*arg)
>>   {
>> -	__s32 buflenout;
>> -	__u64 lastino = 0;
>> -	xfs_bstat_t *p;
>> -	xfs_bstat_t *endp;
>> -	xfs_fsop_bulkreq_t bulkreq;
>> -	struct stat mntstat;
>> +	char			buf[XFS_PPTR_MAXNAMELEN + 1];
>>   
>> -	if (stat(mntpt, &mntstat)) {
>> -		fprintf(stderr, _("can't stat mount point \"%s\": %s\n"),
>> -			mntpt, strerror(errno));
>> -		return 1;
>> +	if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT) {
>> +		printf(_("Root directory.\n"));
>> +		return 0;
>>   	}
>>   
>> -	bulkreq.lastip  = &lastino;
>> -	bulkreq.icount  = BSTATBUF_SZ;
>> -	bulkreq.ubuffer = (void *)bstatbuf;
>> -	bulkreq.ocount  = &buflenout;
>> -
>> -	while (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT, &bulkreq) == 0) {
>> -		if (*(bulkreq.ocount) == 0) {
>> -			return 0;
>> -		}
>> -		for (p = bstatbuf, endp = bstatbuf + *bulkreq.ocount; p < endp; p++) {
>> -
>> -			/* inode being modified, get synced data with iget */
>> -			if ( (!p->bs_nlink || !p->bs_mode) && p->bs_ino != 0 ) {
>> -
>> -				if (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) < 0) {
>> -				    fprintf(stderr,
>> -					  _("failed to get bulkstat information for inode %llu\n"),
>> -					 (unsigned long long) p->bs_ino);
>> -				    continue;
>> -				}
>> -				if (!p->bs_nlink || !p->bs_mode || !p->bs_ino) {
>> -				    fprintf(stderr,
>> -					  _("failed to get valid bulkstat information for inode %llu\n"),
>> -					 (unsigned long long) p->bs_ino);
>> -				    continue;
>> -				}
>> -			}
>> -
>> -			/* skip root */
>> -			if (p->bs_ino == mntstat.st_ino) {
>> -				continue;
>> -			}
>> -
>> -			if (verbose_flag > 1) {
>> -			       printf(_("checking inode %llu\n"),
>> -				       (unsigned long long) p->bs_ino);
>> -			}
>> -
>> -			/* print dotted progress */
>> -			if ((inodes_checked % 100) == 0 && verbose_flag == 1) {
>> -				printf("."); fflush(stdout);
>> -			}
>> -			inodes_checked++;
>> -
>> -			check_parents(parentbuf, parentbuf_size, fshandlep, p);
>> -		}
>> -
>> -	}/*while*/
>> -
>> -	fprintf(stderr, _("syssgi bulkstat failed: %s\n"), strerror(errno));
>> -	return 1;
>> +	memcpy(buf, pptr->xpp_name, pptr->xpp_namelen);
>> +	buf[pptr->xpp_namelen] = 0;
>> +	printf(_("p_ino    = %llu\n"), (unsigned long long)pptr->xpp_ino);
>> +	printf(_("p_gen    = %u\n"), (unsigned int)pptr->xpp_gen);
>> +	printf(_("p_reclen = %u\n"), (unsigned int)pptr->xpp_namelen);
>> +	printf(_("p_name   = \"%s\"\n\n"), buf);
>> +	return 0;
>>   }
>>   
>> -static int
>> -parent_check(void)
>> +int
>> +print_parents(
>> +	struct xfs_handle	*handle)
>>   {
>> -	int fsfd;
>> -	jdm_fshandle_t *fshandlep;
>> -	parent_t *parentbuf;
>> -	size_t parentbuf_size = PARENTBUF_SZ;
>> -	xfs_bstat_t *bstatbuf;
>> -
>> -	err_status = 0;
>> -	inodes_checked = 0;
>> -
>> -	sync();
>> -
>> -        fsfd = file->fd;
>> -
>> -	fshandlep = jdm_getfshandle(mntpt);
>> -	if (fshandlep == NULL) {
>> -		fprintf(stderr, _("unable to open \"%s\" for jdm: %s\n"),
>> -		      mntpt,
>> -		      strerror(errno));
>> -		return 1;
>> -	}
>> -
>> -	/* allocate buffers */
>> -        bstatbuf = (xfs_bstat_t *)calloc(BSTATBUF_SZ, sizeof(xfs_bstat_t));
>> -	parentbuf = (parent_t *)malloc(parentbuf_size);
>> -	if (!bstatbuf || !parentbuf) {
>> -		fprintf(stderr, _("unable to allocate buffers: %s\n"),
>> -			strerror(errno));
>> -		err_status = 1;
>> -		goto out;
>> -	}
>> +	int			ret;
>>   
>> -	if (do_bulkstat(parentbuf, &parentbuf_size, bstatbuf, fsfd, fshandlep) != 0)
>> -		err_status++;
>> -
>> -	if (err_status > 0)
>> -		fprintf(stderr, _("num errors: %d\n"), err_status);
>> +	if (handle)
>> +		ret = handle_walk_pptrs(handle, sizeof(*handle), pptr_print,
>> +				NULL);
>>   	else
>> -		printf(_("succeeded checking %llu inodes\n"),
>> -			(unsigned long long) inodes_checked);
>> -
>> -out:
>> -	free(bstatbuf);
>> -	free(parentbuf);
>> -	free(fshandlep);
>> -	return err_status;
>> -}
>> +		ret = fd_walk_pptrs(file->fd, pptr_print, NULL);
>> +	if (ret)
>> +		perror(file->name);
>>   
>> -static void
>> -print_parent_entry(parent_t *parent, int fullpath)
>> -{
>> -       printf(_("p_ino    = %llu\n"),  (unsigned long long) parent->p_ino);
>> -	printf(_("p_gen    = %u\n"),	parent->p_gen);
>> -	printf(_("p_reclen = %u\n"),	parent->p_reclen);
>> -	if (fullpath)
>> -		printf(_("p_name   = \"%s%s\"\n"), mntpt,
>> -					((char*)parent)+sizeof(struct parent));
>> -	else
>> -		printf(_("p_name   = \"%s\"\n"),
>> -					((char*)parent)+sizeof(struct parent));
>> +	return 0;
>>   }
>>   
>>   static int
>> -parent_list(int fullpath)
>> -{
>> -	void *handlep = NULL;
>> -	size_t handlen;
>> -	int error, i;
>> -	int retval = 1;
>> -	__u32 count;
>> -	parent_t *entryp;
>> -	parent_t *parentbuf = NULL;
>> -	char *path = file->name;
>> -	int pb_size = PARENTBUF_SZ;
>> -
>> -	/* XXXX for linux libhandle version - to set libhandle fsfd cache */
>> -	{
>> -		void *fshandle;
>> -		size_t fshlen;
>> -
>> -		if (path_to_fshandle(mntpt, &fshandle, &fshlen) != 0) {
>> -			fprintf(stderr, _("%s: failed path_to_fshandle \"%s\": %s\n"),
>> -				progname, path, strerror(errno));
>> -			goto error;
>> -		}
>> -		free_handle(fshandle, fshlen);
>> -	}
>> -
>> -	if (path_to_handle(path, &handlep, &handlen) != 0) {
>> -		fprintf(stderr, _("%s: path_to_handle failed for \"%s\"\n"), progname, path);
>> -		goto error;
>> -	}
>> -
>> -	do {
>> -		parentbuf = (parent_t *)realloc(parentbuf, pb_size);
>> -		if (!parentbuf) {
>> -			fprintf(stderr, _("%s: unable to allocate parent buffer: %s\n"),
>> -				progname, strerror(errno));
>> -			goto error;
>> -		}
>> +path_print(
>> +	const char		*mntpt,
>> +	struct path_list	*path,
>> +	void			*arg) {
>>   
>> -		if (fullpath) {
>> -			error = parentpaths_by_handle(handlep,
>> -						       handlen,
>> -						       parentbuf,
>> -						       pb_size,
>> -						       &count);
>> -		} else {
>> -			error = parents_by_handle(handlep,
>> -						   handlen,
>> -						   parentbuf,
>> -						   pb_size,
>> -						   &count);
>> -		}
>> -		if (error == ERANGE) {
>> -			pb_size *= 2;
>> -		} else if (error) {
>> -			fprintf(stderr, _("%s: %s call failed for \"%s\": %s\n"),
>> -				progname, fullpath ? "parentpaths" : "parents",
>> -				path, strerror(errno));
>> -			goto error;
>> -		}
>> -	} while (error == ERANGE);
>> +	char			buf[PATH_MAX];
>> +	size_t			len = PATH_MAX;
>> +	int			ret;
>>   
>> -	if (count == 0) {
>> -		/* no links for inode - something wrong here */
>> -		fprintf(stderr, _("%s: inode-path is missing\n"), progname);
>> -		goto error;
>> +	ret = snprintf(buf, len, "%s", mntpt);
>> +	if (ret != strlen(mntpt)) {
>> +		errno = ENOMEM;
>> +		return -1;
>>   	}
>>   
>> -	entryp = parentbuf;
>> -	for (i = 0; i < count; i++) {
>> -		print_parent_entry(entryp, fullpath);
>> -		entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
>> -	}
>> +	ret = path_list_to_string(path, buf + ret, len - ret);
>> +	if (ret < 0)
>> +		return ret;
>> +	return 0;
>> +}
>>   
>> -	retval = 0;
>> -error:
>> -	free(handlep);
>> -	free(parentbuf);
>> -	return retval;
>> +int
>> +print_paths(
>> +	struct xfs_handle	*handle)
>> +{
>> +	int			ret;
>> +
>> +	if (handle)
>> +		ret = handle_walk_ppaths(handle, sizeof(*handle), path_print,
>> +				NULL);
>> + 	else
>> +		ret = fd_walk_ppaths(file->fd, path_print, NULL);
>> +	if (ret)
>> +		perror(file->name);
>> +	return 0;
>>   }
>>   
>>   int
>> -parent_f(int argc, char **argv)
>> +parent_f(
>> +	int			argc,
>> +	char			**argv)
>>   {
>> -	int c;
>> -	int listpath_flag = 0;
>> -	int check_flag = 0;
>> -	fs_path_t *fs;
>> -	static int tab_init;
>> +	struct xfs_handle	handle;
>> +	void			*hanp = NULL;
>> +	size_t			hlen;
>> +	struct fs_path		*fs;
>> +	char			*p;
>> +	uint64_t		ino = 0;
>> +	uint32_t		gen = 0;
>> +	int			c;
>> +	int			listpath_flag = 0;
>> +	int			ret;
>> +	static int		tab_init;
>>   
>>   	if (!tab_init) {
>>   		tab_init = 1;
>> @@ -394,46 +133,72 @@ parent_f(int argc, char **argv)
>>   	}
>>   	mntpt = fs->fs_dir;
>>   
>> -	verbose_flag = 0;
>> -
>> -	while ((c = getopt(argc, argv, "cpv")) != EOF) {
>> +	while ((c = getopt(argc, argv, "p")) != EOF) {
>>   		switch (c) {
>> -		case 'c':
>> -			check_flag = 1;
>> -			break;
>>   		case 'p':
>>   			listpath_flag = 1;
>>   			break;
>> -		case 'v':
>> -			verbose_flag++;
>> -			break;
>>   		default:
>>   			return command_usage(&parent_cmd);
>>   		}
>>   	}
>>   
>> -	if (!check_flag && !listpath_flag) /* default case */
>> -		exitcode = parent_list(listpath_flag);
>> -	else {
>> -		if (listpath_flag)
>> -			exitcode = parent_list(listpath_flag);
>> -		if (check_flag)
>> -			exitcode = parent_check();
>> +	/*
>> +	 * Always initialize the fshandle table because we need it for
>> +	 * the ppaths functions to work.
>> +	 */
>> +	ret = path_to_fshandle((char *)mntpt, &hanp, &hlen);
>> +	if (ret) {
>> +		perror(mntpt);
>> +		return 0;
>> + 	}
>> +
>> +	if (optind + 2 == argc) {
>> +		ino = strtoull(argv[optind], &p, 0);
>> +		if (*p != '\0' || ino == 0) {
>> +			fprintf(stderr,
>> +				_("Bad inode number '%s'.\n"),
>> +				argv[optind]);
>> +			return 0;
>> +		}
>> +		gen = strtoul(argv[optind + 1], &p, 0);
>> +		if (*p != '\0') {
>> +			fprintf(stderr,
>> +				_("Bad generation number '%s'.\n"),
>> +				argv[optind + 1]);
>> +			return 0;
>> +		}
>> +
>> +		memcpy(&handle, hanp, sizeof(handle));
>> +		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
>> +				sizeof(handle.ha_fid.fid_len);
>> +		handle.ha_fid.fid_pad = 0;
>> +		handle.ha_fid.fid_ino = ino;
>> +		handle.ha_fid.fid_gen = gen;
>> +
>>   	}
>>   
>> +	if (listpath_flag)
>> +		exitcode = print_paths(ino ? &handle : NULL);
>> +	else
>> +		exitcode = print_parents(ino ? &handle : NULL);
>> +
>> +	if (hanp)
>> +		free_handle(hanp, hlen);
>> +
>>   	return 0;
>>   }
>>   
>>   static void
>>   parent_help(void)
>>   {
>> -	printf(_(
>> +printf(_(
>>   "\n"
>>   " list the current file's parents and their filenames\n"
>>   "\n"
>> -" -c -- check the current file's file system for parent consistency\n"
>> -" -p -- list the current file's parents and their full paths\n"
>> -" -v -- verbose mode\n"
>> +" -p -- list the current file's paths up to the root\n"
>> +"\n"
>> +"If ino and gen are supplied, use them instead.\n"
>>   "\n"));
>>   }
>>   
>> @@ -444,9 +209,9 @@ parent_init(void)
>>   	parent_cmd.cfunc = parent_f;
>>   	parent_cmd.argmin = 0;
>>   	parent_cmd.argmax = -1;
>> -	parent_cmd.args = _("[-cpv]");
>> +	parent_cmd.args = _("[-p] [ino gen]");
>>   	parent_cmd.flags = CMD_NOMAP_OK;
>> -	parent_cmd.oneline = _("print or check parent inodes");
>> +	parent_cmd.oneline = _("print parent inodes");
>>   	parent_cmd.help = parent_help;
>>   
>>   	if (expert)
>> diff --git a/libfrog/paths.c b/libfrog/paths.c
>> index c7895e9..9fb0140 100644
>> --- a/libfrog/paths.c
>> +++ b/libfrog/paths.c
>> @@ -27,6 +27,7 @@
>>   #include "path.h"
>>   #include "input.h"
>>   #include "project.h"
>> +#include "list.h"
>>   #include <limits.h>
>>   
>>   extern char *progname;
>> @@ -632,3 +633,138 @@ fs_table_insert_project_path(
>>   		exit(1);
>>   	}
>>   }
>> +
>> +
>> +/* Structured path components. */
>> +
>> +struct path_list {
>> +	struct list_head	p_head;
>> +};
>> +
>> +struct path_component {
>> +	struct list_head	pc_list;
>> +	char			*pc_fname;
>> +};
>> +
>> +/* Initialize a path component with a given name. */
>> +struct path_component *
>> +path_component_init(
>> +	const char		*name)
>> +{
>> +	struct path_component	*pc;
>> +
>> +	pc = malloc(sizeof(struct path_component));
>> +	if (!pc)
>> +		return NULL;
>> +	INIT_LIST_HEAD(&pc->pc_list);
>> +	pc->pc_fname = strdup(name);
>> +	if (!pc->pc_fname) {
>> +		free(pc);
>> +		return NULL;
>> +	}
>> +	return pc;
>> +}
>> +
>> +/* Free a path component. */
>> +void
>> +path_component_free(
>> +	struct path_component	*pc)
>> +{
>> +	free(pc->pc_fname);
>> +	free(pc);
>> +}
>> +
>> +/* Change a path component's filename. */
>> +int
>> +path_component_change(
>> +	struct path_component	*pc,
>> +	void			*name,
>> +	size_t			namelen)
>> +{
>> +	void			*p;
>> +
>> +	p = realloc(pc->pc_fname, namelen + 1);
>> +	if (!p)
>> +		return -1;
>> +	pc->pc_fname = p;
>> +	memcpy(pc->pc_fname, name, namelen);
>> +	pc->pc_fname[namelen] = 0;
>> +	return 0;
>> +}
>> +
>> +/* Initialize a pathname. */
>> +struct path_list *
>> +path_list_init(void)
>> +{
>> +	struct path_list	*path;
>> +
>> +	path = malloc(sizeof(struct path_list));
>> +	if (!path)
>> +		return NULL;
>> +	INIT_LIST_HEAD(&path->p_head);
>> +	return path;
>> +}
>> +
>> +/* Empty out a pathname. */
>> +void
>> +path_list_free(
>> +	struct path_list	*path)
>> +{
>> +	struct path_component	*pos;
>> +	struct path_component	*n;
>> +
>> +	list_for_each_entry_safe(pos, n, &path->p_head, pc_list) {
>> +		path_list_del_component(path, pos);
>> +		path_component_free(pos);
>> +	}
>> +	free(path);
>> +}
>> +
>> +/* Add a parent component to a pathname. */
>> +void
>> +path_list_add_parent_component(
>> +	struct path_list	*path,
>> +	struct path_component	*pc)
>> +{
>> +	list_add(&pc->pc_list, &path->p_head);
>> +}
>> +
>> +/* Add a component to a pathname. */
>> +void
>> +path_list_add_component(
>> +	struct path_list	*path,
>> +	struct path_component	*pc)
>> +{
>> +	list_add_tail(&pc->pc_list, &path->p_head);
>> +}
>> +
>> +/* Remove a component from a pathname. */
>> +void
>> +path_list_del_component(
>> +	struct path_list	*path,
>> +	struct path_component	*pc)
>> +{
>> +	list_del_init(&pc->pc_list);
>> +}
>> +
>> +/* Convert a pathname into a string. */
>> +ssize_t
>> +path_list_to_string(
>> +	struct path_list	*path,
>> +	char			*buf,
>> +	size_t			buflen)
>> +{
>> +	struct path_component	*pos;
>> +	ssize_t			bytes = 0;
>> +	int			ret;
>> +
>> +	list_for_each_entry(pos, &path->p_head, pc_list) {
>> +		ret = snprintf(buf, buflen, "/%s", pos->pc_fname);
>> +		if (ret != 1 + strlen(pos->pc_fname))
>> +			return -1;
>> +		bytes += ret;
>> +		buf += ret;
>> +		buflen -= ret;
>> +	}
>> +	return bytes;
>> +}
>> diff --git a/libhandle/Makefile b/libhandle/Makefile
>> index fe1a2af..d3cea41 100644
>> --- a/libhandle/Makefile
>> +++ b/libhandle/Makefile
>> @@ -16,7 +16,7 @@ else
>>   LTLDFLAGS += -Wl,--version-script,libhandle.sym
>>   endif
>>   
>> -CFILES = handle.c jdm.c
>> +CFILES = handle.c jdm.c parent.c
>>   LSRCFILES = libhandle.sym
>>   
>>   default: ltdepend $(LTLIBRARY)
>> diff --git a/libhandle/handle.c b/libhandle/handle.c
>> index 878d14d..a70fa32 100644
>> --- a/libhandle/handle.c
>> +++ b/libhandle/handle.c
>> @@ -41,7 +41,6 @@ typedef union {
>>   } comarg_t;
>>   
>>   static int obj_to_handle(char *, int, unsigned int, comarg_t, void**, size_t*);
>> -static int handle_to_fsfd(void *, char **);
>>   static char *path_to_fspath(char *path);
>>   
>>   
>> @@ -214,8 +213,10 @@ handle_to_fshandle(
>>   	return 0;
>>   }
>>   
>> -static int
>> -handle_to_fsfd(void *hanp, char **path)
>> +int
>> +handle_to_fsfd(
>> +	void		*hanp,
>> +	char		**path)
>>   {
>>   	struct fdhash	*fdhp;
>>   
>> diff --git a/libhandle/parent.c b/libhandle/parent.c
>> new file mode 100644
>> index 0000000..f6be3bd
>> --- /dev/null
>> +++ b/libhandle/parent.c
>> @@ -0,0 +1,325 @@
>> +/*
>> + * Copyright (C) 2017 Oracle.  All Rights Reserved.
>> + *
>> + * Author: Darrick J. Wong <darrick.wong@oracle.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version 2
>> + * of the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it would be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write the Free Software Foundation,
>> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
>> + */
>> +#include "platform_defs.h"
>> +#include "xfs.h"
>> +#include "xfs_arch.h"
>> +#include "list.h"
>> +#include "path.h"
>> +#include "handle.h"
>> +#include "parent.h"
>> +
>> +/* Allocate a buffer large enough for some parent pointer records. */
>> +static inline struct xfs_pptr_info *
>> +xfs_pptr_alloc(
>> +      size_t                  nr_ptrs)
>> +{
>> +      struct xfs_pptr_info    *pi;
>> +
>> +      pi = malloc(XFS_PPTR_INFO_SIZEOF(nr_ptrs));
>> +      if (!pi)
>> +              return NULL;
>> +      memset(pi, 0, sizeof(struct xfs_pptr_info));
>> +      pi->pi_ptrs_size = nr_ptrs;
>> +      return pi;
>> +}
>> +
>> +/* Walk all parents of the given file handle. */
>> +static int
>> +handle_walk_parents(
>> +	int			fd,
>> +	struct xfs_handle	*handle,
>> +	walk_pptr_fn		fn,
>> +	void			*arg)
>> +{
>> +	struct xfs_pptr_info	*pi;
>> +	struct xfs_parent_ptr	*p;
>> +	unsigned int		i;
>> +	ssize_t			ret = -1;
>> +
>> +	pi = xfs_pptr_alloc(4);
>> +	if (!pi)
>> +		return -1;
>> +
>> +	if (handle) {
>> +		memcpy(&pi->pi_handle, handle, sizeof(struct xfs_handle));
>> +		pi->pi_flags = XFS_PPTR_IFLAG_HANDLE;
>> +	}
>> +
>> +	ret = ioctl(fd, XFS_IOC_GETPPOINTER, pi);
>> +	while (!ret) {
>> +		if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT) {
>> +			ret = fn(pi, NULL, arg);
>> +			break;
>> +		}
>> +		if (pi->pi_ptrs_used == 0)
>> +			break;
>> +		for (i = 0; i < pi->pi_ptrs_used; i++) {
>> +			p = XFS_PPINFO_TO_PP(pi, i);
>> +			ret = fn(pi, p, arg);
>> +			if (ret)
>> +				goto out_pi;
>> +		}
>> +		ret = ioctl(fd, XFS_IOC_GETPPOINTER, pi);
>> +	}
>> +
>> +out_pi:
>> +	free(pi);
>> +	return ret;
>> +}
>> +
>> +/* Walk all parent pointers of this handle. */
>> +int
>> +handle_walk_pptrs(
>> +	void			*hanp,
>> +	size_t			hlen,
>> +	walk_pptr_fn		fn,
>> +	void			*arg)
>> +{
>> +	char			*mntpt;
>> +	int			fd;
>> +
>> +	if (hlen != sizeof(struct xfs_handle)) {
>> +		errno = EINVAL;
>> +		return -1;
>> +	}
>> +
>> +	fd = handle_to_fsfd(hanp, &mntpt);
>> +	if (fd < 0)
>> +		return -1;
>> +
>> +	return handle_walk_parents(fd, hanp, fn, arg);
>> +}
>> +
>> +/* Walk all parent pointers of this fd. */
>> +int
>> +fd_walk_pptrs(
>> +	int			fd,
>> +	walk_pptr_fn		fn,
>> +	void			*arg)
>> +{
>> +	return handle_walk_parents(fd, NULL, fn, arg);
>> +}
>> +
>> +struct walk_ppaths_info {
>> +	walk_ppath_fn			fn;
>> +	void				*arg;
>> +	char				*mntpt;
>> +	struct path_list		*path;
>> +	int				fd;
>> +};
>> +
>> +struct walk_ppath_level_info {
>> +	struct xfs_handle		newhandle;
>> +	struct path_component		*pc;
>> +	struct walk_ppaths_info		*wpi;
>> +};
>> +
>> +static int handle_walk_parent_paths(struct walk_ppaths_info *wpi,
>> +		struct xfs_handle *handle);
>> +
>> +static int
>> +handle_walk_parent_path_ptr(
>> +	struct xfs_pptr_info		*pi,
>> +	struct xfs_parent_ptr		*p,
>> +	void				*arg)
>> +{
>> +	struct walk_ppath_level_info	*wpli = arg;
>> +	struct walk_ppaths_info		*wpi = wpli->wpi;
>> +	unsigned int			i;
>> +	int				ret = 0;
>> +
>> +	if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT)
>> +		return wpi->fn(wpi->mntpt, wpi->path, wpi->arg);
>> +
>> +	for (i = 0; i < pi->pi_ptrs_used; i++) {
>> +		p = XFS_PPINFO_TO_PP(pi, i);
>> +		ret = path_component_change(wpli->pc, p->xpp_name,
>> +				p->xpp_namelen);
>> +		if (ret)
>> +			break;
>> +		wpli->newhandle.ha_fid.fid_ino = p->xpp_ino;
>> +		wpli->newhandle.ha_fid.fid_gen = p->xpp_gen;
>> +		path_list_add_parent_component(wpi->path, wpli->pc);
>> +		ret = handle_walk_parent_paths(wpi, &wpli->newhandle);
>> +		path_list_del_component(wpi->path, wpli->pc);
>> +		if (ret)
>> +			break;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +/*
>> + * Recursively walk all parents of the given file handle; if we hit the
>> + * fs root then we call the associated function with the constructed path.
>> + */
>> +static int
>> +handle_walk_parent_paths(
>> +	struct walk_ppaths_info		*wpi,
>> +	struct xfs_handle		*handle)
>> +{
>> +	struct walk_ppath_level_info	*wpli;
>> +	int				ret;
>> +
>> +	wpli = malloc(sizeof(struct walk_ppath_level_info));
>> +	if (!wpli)
>> +		return -1;
>> +	wpli->pc = path_component_init("");
>> +	if (!wpli->pc) {
>> +		free(wpli);
>> +		return -1;
>> +	}
>> +	wpli->wpi = wpi;
>> +	memcpy(&wpli->newhandle, handle, sizeof(struct xfs_handle));
>> +
>> +	ret = handle_walk_parents(wpi->fd, handle, handle_walk_parent_path_ptr,
>> +			wpli);
>> +
>> +	path_component_free(wpli->pc);
>> +	free(wpli);
>> +	return ret;
>> +}
>> +
>> +/*
>> + * Call the given function on all known paths from the vfs root to the inode
>> + * described in the handle.
>> + */
>> +int
>> +handle_walk_ppaths(
>> +	void			*hanp,
>> +	size_t			hlen,
>> +	walk_ppath_fn		fn,
>> +	void			*arg)
>> +{
>> +	struct walk_ppaths_info	wpi;
>> +	ssize_t			ret;
>> +
>> +	if (hlen != sizeof(struct xfs_handle)) {
>> +		errno = EINVAL;
>> +		return -1;
>> +	}
>> +
>> +	wpi.fd = handle_to_fsfd(hanp, &wpi.mntpt);
>> +	if (wpi.fd < 0)
>> +		return -1;
>> +	wpi.path = path_list_init();
>> +	if (!wpi.path)
>> +		return -1;
>> +	wpi.fn = fn;
>> +	wpi.arg = arg;
>> +
>> +	ret = handle_walk_parent_paths(&wpi, hanp);
>> +	path_list_free(wpi.path);
>> +
>> +	return ret;
>> +}
>> +
>> +/*
>> + * Call the given function on all known paths from the vfs root to the inode
>> + * referred to by the file description.
>> + */
>> +int
>> +fd_walk_ppaths(
>> +	int			fd,
>> +	walk_ppath_fn		fn,
>> +	void			*arg)
>> +{
>> +	struct walk_ppaths_info	wpi;
>> +	void			*hanp;
>> +	size_t			hlen;
>> +	int			fsfd;
>> +	int			ret;
>> +
>> +	ret = fd_to_handle(fd, &hanp, &hlen);
>> +	if (ret)
>> +		return ret;
>> +
>> +	fsfd = handle_to_fsfd(hanp, &wpi.mntpt);
>> +	if (fsfd < 0)
>> +		return -1;
>> +	wpi.fd = fd;
>> +	wpi.path = path_list_init();
>> +	if (!wpi.path)
>> +		return -1;
>> +	wpi.fn = fn;
>> +	wpi.arg = arg;
>> +
>> +	ret = handle_walk_parent_paths(&wpi, hanp);
>> +	path_list_free(wpi.path);
>> +
>> +	return ret;
>> +}
>> +
>> +struct path_walk_info {
>> +	char			*buf;
>> +	size_t			len;
>> +};
>> +
>> +/* Helper that stringifies the first full path that we find. */
>> +static int
>> +handle_to_path_walk(
>> +	const char		*mntpt,
>> +	struct path_list	*path,
>> +	void			*arg)
>> +{
>> +	struct path_walk_info	*pwi = arg;
>> +	int			ret;
>> +
>> +	ret = snprintf(pwi->buf, pwi->len, "%s", mntpt);
>> +	if (ret != strlen(mntpt)) {
>> +		errno = ENOMEM;
>> +		return -1;
>> +	}
>> +
>> +	ret = path_list_to_string(path, pwi->buf + ret, pwi->len - ret);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return WALK_PPATHS_ABORT;
>> +}
>> +
>> +/* Return any eligible path to this file handle. */
>> +int
>> +handle_to_path(
>> +	void			*hanp,
>> +	size_t			hlen,
>> +	char			*path,
>> +	size_t			pathlen)
>> +{
>> +	struct path_walk_info	pwi;
>> +
>> +	pwi.buf = path;
>> +	pwi.len = pathlen;
>> +	return handle_walk_ppaths(hanp, hlen, handle_to_path_walk, &pwi);
>> +}
>> +
>> +/* Return any eligible path to this file description. */
>> +int
>> +fd_to_path(
>> +	int			fd,
>> +	char			*path,
>> +	size_t			pathlen)
>> +{
>> +	struct path_walk_info	pwi;
>> +
>> +	pwi.buf = path;
>> +	pwi.len = pathlen;
>> +	return fd_walk_ppaths(fd, handle_to_path_walk, &pwi);
>> +}
>> diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
>> index e3ce233..aa613f9 100644
>> --- a/libxfs/xfs_fs.h
>> +++ b/libxfs/xfs_fs.h
>> @@ -610,6 +610,14 @@ struct xfs_pptr_info {
>>   #define XFS_PPINFO_TO_PP(info, idx)    \
>>   	(&(((struct xfs_parent_ptr *)((char *)(info) + sizeof(*(info))))[(idx)]))
>>   
>> +#define XFS_PPTR_ALL_IFLAGS    (XFS_PPTR_IFLAG_HANDLE)
>> +
>> +/* partial results only */
>> +#define XFS_PPTR_OFLAG_PARTIAL (1U << 0)
>> +
>> +/* target was the root directory */
>> +#define XFS_PPTR_OFLAG_ROOT    (1U << 1)
> 
> Uhoh, I forgot about this chunk, which should be in the kernel patches
> somewhere I guess...
> 
> --D
> 

Do we want to keep these?  Should I add behavior of these in the kernel 
side set?

Allison

>> +
>>   /*
>>    * ioctl limits
>>    */
>> diff --git a/scrub/inodes.c b/scrub/inodes.c
>> index ccfb9e0..3fbcd1a 100644
>> --- a/scrub/inodes.c
>> +++ b/scrub/inodes.c
>> @@ -31,6 +31,7 @@
>>   #include "xfs_scrub.h"
>>   #include "common.h"
>>   #include "inodes.h"
>> +#include "parent.h"
>>   
>>   /*
>>    * Iterate a range of inodes.
>> @@ -293,3 +294,28 @@ xfs_open_handle(
>>   	return open_by_fshandle(handle, sizeof(*handle),
>>   			O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY);
>>   }
>> +
>> +/* Construct a description for an inode. */
>> +void
>> +xfs_scrub_ino_descr(
>> +	struct scrub_ctx	*ctx,
>> +	struct xfs_handle	*handle,
>> +	char			*buf,
>> +	size_t			buflen)
>> +{
>> +	uint64_t		ino;
>> +	xfs_agnumber_t		agno;
>> +	xfs_agino_t		agino;
>> +	int			ret;
>> +
>> +	ret = handle_to_path(handle, sizeof(struct xfs_handle), buf, buflen);
>> +	if (ret >= 0)
>> +		return;
>> +
>> +	ino = handle->ha_fid.fid_ino;
>> +	agno = ino / (1ULL << (ctx->inopblog + ctx->agblklog));
>> +	agino = ino % (1ULL << (ctx->inopblog + ctx->agblklog));
>> +	snprintf(buf, buflen, _("inode %"PRIu64" (%u/%u)"), ino, agno,
>> +			agino);
>> +}
>> +
>> diff --git a/scrub/inodes.h b/scrub/inodes.h
>> index 693cb05..e94de0a 100644
>> --- a/scrub/inodes.h
>> +++ b/scrub/inodes.h
>> @@ -28,5 +28,7 @@ bool xfs_scan_all_inodes(struct scrub_ctx *ctx, xfs_inode_iter_fn fn,
>>   		void *arg);
>>   
>>   int xfs_open_handle(struct xfs_handle *handle);
>> +void xfs_scrub_ino_descr(struct scrub_ctx *ctx, struct xfs_handle *handle,
>> +		char *buf, size_t buflen);
>>   
>>   #endif /* XFS_SCRUB_INODES_H_ */
>> diff --git a/scrub/phase5.c b/scrub/phase5.c
>> index 01038f7..ecaaaaa 100644
>> --- a/scrub/phase5.c
>> +++ b/scrub/phase5.c
>> @@ -245,16 +245,11 @@ xfs_scrub_connections(
>>   	void			*arg)
>>   {
>>   	bool			*pmoveon = arg;
>> -	char			descr[DESCR_BUFSZ];
>> +	char			descr[PATH_MAX];
>>   	bool			moveon = true;
>> -	xfs_agnumber_t		agno;
>> -	xfs_agino_t		agino;
>>   	int			fd = -1;
>>   
>> -	agno = bstat->bs_ino / (1ULL << (ctx->inopblog + ctx->agblklog));
>> -	agino = bstat->bs_ino % (1ULL << (ctx->inopblog + ctx->agblklog));
>> -	snprintf(descr, DESCR_BUFSZ, _("inode %"PRIu64" (%u/%u)"),
>> -			(uint64_t)bstat->bs_ino, agno, agino);
>> +	xfs_scrub_ino_descr(ctx, handle, descr, PATH_MAX);
>>   	background_sleep();
>>   
>>   	/* Warn about naming problems in xattrs. */
>> -- 
>> 2.7.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Darrick J. Wong May 9, 2018, 1:44 a.m. UTC | #8
> On 05/08/2018 10:45 AM, Darrick J. Wong wrote:
> > On Mon, May 07, 2018 at 09:41:19PM -0700, Allison Henderson wrote:
> > > From: "Darrick J. Wong" <darrick.wong@oracle.com>
> > > 
> > > Add ioctl definitions to libxfs, build the necessary helpers into
> > > libfrog and libhandle to iterate parents (and parent paths), then wire
> > > up xfs_scrub to be able to query parent pointers from userspace.  The
> > > goal of this patch is to exercise userspace, and is nowhere near a
> > > complete solution.  A basic xfs_io parent command implementation
> > > replaces ... whatever that is that's there now.
> > > 
> > > Totally missing: actual support in libxfs for working with parent ptrs
> > > straight off the disk (mkfs, xfs_db, xfs_repair).
> > > 
> > > [achender: Minor syntax adjustments to sew solution in actual support
> > > 	   in libxfs for working with parent ptrs]
> > > 
> > > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > > ---
> > >   include/handle.h   |   2 +
> > >   include/parent.h   |  18 ++
> > >   include/path.h     |  19 +++
> > >   io/parent.c        | 471 ++++++++++++++---------------------------------------
> > >   libfrog/paths.c    | 136 ++++++++++++++++
> > >   libhandle/Makefile |   2 +-
> > >   libhandle/handle.c |   7 +-
> > >   libhandle/parent.c | 325 ++++++++++++++++++++++++++++++++++++
> > >   libxfs/xfs_fs.h    |   8 +
> > >   scrub/inodes.c     |  26 +++
> > >   scrub/inodes.h     |   2 +
> > >   scrub/phase5.c     |   9 +-
> > >   12 files changed, 661 insertions(+), 364 deletions(-)
> > > 
> > > diff --git a/include/handle.h b/include/handle.h
> > > index 49f1441..00aa43d 100644
> > > --- a/include/handle.h
> > > +++ b/include/handle.h
> > > @@ -52,6 +52,8 @@ extern int  fssetdm_by_handle (void *__hanp, size_t __hlen,
> > >   void fshandle_destroy(void);
> > > +int handle_to_fsfd(void *hanp, char **path);
> > > +
> > >   #ifdef __cplusplus
> > >   }
> > >   #endif
> > > diff --git a/include/parent.h b/include/parent.h
> > > index 85cef85..33f8d85 100644
> > > --- a/include/parent.h
> > > +++ b/include/parent.h
> > > @@ -28,4 +28,22 @@ typedef struct parent_cursor {
> > >   	__u32	opaque[4];      /* an opaque cookie */
> > >   } parent_cursor_t;
> > > +struct path_list;
> > > +
> > > +typedef int (*walk_pptr_fn)(struct xfs_pptr_info *pi, struct xfs_parent_ptr *pptr,
> > > +		void *arg);
> > > +typedef int (*walk_ppath_fn)(const char *mntpt, struct path_list *path,
> > > +		void *arg);
> > > +
> > > +#define WALK_PPTRS_ABORT	1
> > > +int fd_walk_pptrs(int fd, walk_pptr_fn fn, void *arg);
> > > +int handle_walk_pptrs(void *hanp, size_t hanlen, walk_pptr_fn fn, void *arg);
> > > +
> > > +#define WALK_PPATHS_ABORT	1
> > > +int fd_walk_ppaths(int fd, walk_ppath_fn fn, void *arg);
> > > +int handle_walk_ppaths(void *hanp, size_t hanlen, walk_ppath_fn fn, void *arg);
> > > +
> > > +int fd_to_path(int fd, char *path, size_t pathlen);
> > > +int handle_to_path(void *hanp, size_t hlen, char *path, size_t pathlen);
> > > +
> > >   #endif
> > > diff --git a/include/path.h b/include/path.h
> > > index 88dc44b..cbe4e19 100644
> > > --- a/include/path.h
> > > +++ b/include/path.h
> > > @@ -70,4 +70,23 @@ typedef struct fs_cursor {
> > >   extern void fs_cursor_initialise(char *__dir, uint __flags, fs_cursor_t *__cp);
> > >   extern fs_path_t *fs_cursor_next_entry(fs_cursor_t *__cp);
> > > +/* Path information. */
> > > +
> > > +struct path_list;
> > > +struct path_component;
> > > +
> > > +struct path_component *path_component_init(const char *name);
> > > +void path_component_free(struct path_component *pc);
> > > +int path_component_change(struct path_component *pc, void *name,
> > > +		size_t namelen);
> > > +
> > > +struct path_list *path_list_init(void);
> > > +void path_list_free(struct path_list *path);
> > > +void path_list_add_parent_component(struct path_list *path,
> > > +		struct path_component *pc);
> > > +void path_list_add_component(struct path_list *path, struct path_component *pc);
> > > +void path_list_del_component(struct path_list *path, struct path_component *pc);
> > > +
> > > +ssize_t path_list_to_string(struct path_list *path, char *buf, size_t buflen);
> > > +
> > >   #endif	/* __PATH_H__ */
> > > diff --git a/io/parent.c b/io/parent.c
> > > index 55b8b49..ad51fe6 100644
> > > --- a/io/parent.c
> > > +++ b/io/parent.c
> > > @@ -21,366 +21,105 @@
> > >   #include "path.h"
> > >   #include "parent.h"
> > >   #include "handle.h"
> > > -#include "jdm.h"
> > >   #include "init.h"
> > >   #include "io.h"
> > > -#define PARENTBUF_SZ		16384
> > > -#define BSTATBUF_SZ		16384
> > > -
> > >   static cmdinfo_t parent_cmd;
> > > -static int verbose_flag;
> > > -static int err_status;
> > > -static __u64 inodes_checked;
> > >   static char *mntpt;
> > > -/*
> > > - * check out a parent entry to see if the values seem valid
> > > - */
> > > -static void
> > > -check_parent_entry(xfs_bstat_t *bstatp, parent_t *parent)
> > > -{
> > > -	int sts;
> > > -	char fullpath[PATH_MAX];
> > > -	struct stat statbuf;
> > > -	char *str;
> > > -
> > > -	snprintf(fullpath, parent->p_reclen, _("%s%s"), mntpt,
> > > -				((char*)parent)+sizeof(struct parent));
> > > -
> > > -	sts = lstat(fullpath, &statbuf);
> > > -	if (sts != 0) {
> > > -		fprintf(stderr,
> > > -			_("inode-path for inode: %llu is incorrect - path \"%s\" non-existent\n"),
> > > -			(unsigned long long) bstatp->bs_ino, fullpath);
> > > -		if (verbose_flag) {
> > > -			fprintf(stderr,
> > > -				_("path \"%s\" does not stat for inode: %llu; err = %s\n"),
> > > -				fullpath,
> > > -			       (unsigned long long) bstatp->bs_ino,
> > > -				strerror(errno));
> > > -		}
> > > -		err_status++;
> > > -		return;
> > > -	} else {
> > > -		if (verbose_flag > 1) {
> > > -			printf(_("path \"%s\" found\n"), fullpath);
> > > -		}
> > > -	}
> > > -
> > > -	if (statbuf.st_ino != bstatp->bs_ino) {
> > > -		fprintf(stderr,
> > > -			_("inode-path for inode: %llu is incorrect - wrong inode#\n"),
> > > -		       (unsigned long long) bstatp->bs_ino);
> > > -		if (verbose_flag) {
> > > -			fprintf(stderr,
> > > -				_("ino mismatch for path \"%s\" %llu vs %llu\n"),
> > > -				fullpath,
> > > -				(unsigned long long)statbuf.st_ino,
> > > -				(unsigned long long)bstatp->bs_ino);
> > > -		}
> > > -		err_status++;
> > > -		return;
> > > -	} else if (verbose_flag > 1) {
> > > -		printf(_("inode number match: %llu\n"),
> > > -			(unsigned long long)statbuf.st_ino);
> > > -	}
> > > -
> > > -	/* get parent path */
> > > -	str = strrchr(fullpath, '/');
> > > -	*str = '\0';
> > > -	sts = stat(fullpath, &statbuf);
> > > -	if (sts != 0) {
> > > -		fprintf(stderr,
> > > -			_("parent path \"%s\" does not stat: %s\n"),
> > > -			fullpath,
> > > -			strerror(errno));
> > > -		err_status++;
> > > -		return;
> > > -	} else {
> > > -		if (parent->p_ino != statbuf.st_ino) {
> > > -			fprintf(stderr,
> > > -				_("inode-path for inode: %llu is incorrect - wrong parent inode#\n"),
> > > -			       (unsigned long long) bstatp->bs_ino);
> > > -			if (verbose_flag) {
> > > -				fprintf(stderr,
> > > -					_("ino mismatch for path \"%s\" %llu vs %llu\n"),
> > > -					fullpath,
> > > -					(unsigned long long)parent->p_ino,
> > > -					(unsigned long long)statbuf.st_ino);
> > > -			}
> > > -			err_status++;
> > > -			return;
> > > -		} else {
> > > -			if (verbose_flag > 1) {
> > > -			       printf(_("parent ino match for %llu\n"),
> > > -				       (unsigned long long) parent->p_ino);
> > > -			}
> > > -		}
> > > -	}
> > > -}
> > > -
> > > -static void
> > > -check_parents(parent_t *parentbuf, size_t *parentbuf_size,
> > > -	     jdm_fshandle_t *fshandlep, xfs_bstat_t *statp)
> > > -{
> > > -	int error, i;
> > > -	__u32 count;
> > > -	parent_t *entryp;
> > > -
> > > -	do {
> > > -		error = jdm_parentpaths(fshandlep, statp, parentbuf, *parentbuf_size, &count);
> > > -
> > > -		if (error == ERANGE) {
> > > -			*parentbuf_size *= 2;
> > > -			parentbuf = (parent_t *)realloc(parentbuf, *parentbuf_size);
> > > -		} else if (error) {
> > > -			fprintf(stderr, _("parentpaths failed for ino %llu: %s\n"),
> > > -			       (unsigned long long) statp->bs_ino,
> > > -				strerror(errno));
> > > -			err_status++;
> > > -			break;
> > > -		}
> > > -	} while (error == ERANGE);
> > > -
> > > -
> > > -	if (count == 0) {
> > > -		/* no links for inode - something wrong here */
> > > -	       fprintf(stderr, _("inode-path for inode: %llu is missing\n"),
> > > -			       (unsigned long long) statp->bs_ino);
> > > -		err_status++;
> > > -	}
> > > -
> > > -	entryp = parentbuf;
> > > -	for (i = 0; i < count; i++) {
> > > -		check_parent_entry(statp, entryp);
> > > -		entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
> > > -	}
> > > -}
> > > -
> > >   static int
> > > -do_bulkstat(parent_t *parentbuf, size_t *parentbuf_size, xfs_bstat_t *bstatbuf,
> > > -	    int fsfd, jdm_fshandle_t *fshandlep)
> > > +pptr_print(
> > > +	struct xfs_pptr_info	*pi,
> > > +	struct xfs_parent_ptr	*pptr,
> > > +	void			*arg)
> > >   {
> > > -	__s32 buflenout;
> > > -	__u64 lastino = 0;
> > > -	xfs_bstat_t *p;
> > > -	xfs_bstat_t *endp;
> > > -	xfs_fsop_bulkreq_t bulkreq;
> > > -	struct stat mntstat;
> > > +	char			buf[XFS_PPTR_MAXNAMELEN + 1];
> > > -	if (stat(mntpt, &mntstat)) {
> > > -		fprintf(stderr, _("can't stat mount point \"%s\": %s\n"),
> > > -			mntpt, strerror(errno));
> > > -		return 1;
> > > +	if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT) {
> > > +		printf(_("Root directory.\n"));
> > > +		return 0;
> > >   	}
> > > -	bulkreq.lastip  = &lastino;
> > > -	bulkreq.icount  = BSTATBUF_SZ;
> > > -	bulkreq.ubuffer = (void *)bstatbuf;
> > > -	bulkreq.ocount  = &buflenout;
> > > -
> > > -	while (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT, &bulkreq) == 0) {
> > > -		if (*(bulkreq.ocount) == 0) {
> > > -			return 0;
> > > -		}
> > > -		for (p = bstatbuf, endp = bstatbuf + *bulkreq.ocount; p < endp; p++) {
> > > -
> > > -			/* inode being modified, get synced data with iget */
> > > -			if ( (!p->bs_nlink || !p->bs_mode) && p->bs_ino != 0 ) {
> > > -
> > > -				if (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) < 0) {
> > > -				    fprintf(stderr,
> > > -					  _("failed to get bulkstat information for inode %llu\n"),
> > > -					 (unsigned long long) p->bs_ino);
> > > -				    continue;
> > > -				}
> > > -				if (!p->bs_nlink || !p->bs_mode || !p->bs_ino) {
> > > -				    fprintf(stderr,
> > > -					  _("failed to get valid bulkstat information for inode %llu\n"),
> > > -					 (unsigned long long) p->bs_ino);
> > > -				    continue;
> > > -				}
> > > -			}
> > > -
> > > -			/* skip root */
> > > -			if (p->bs_ino == mntstat.st_ino) {
> > > -				continue;
> > > -			}
> > > -
> > > -			if (verbose_flag > 1) {
> > > -			       printf(_("checking inode %llu\n"),
> > > -				       (unsigned long long) p->bs_ino);
> > > -			}
> > > -
> > > -			/* print dotted progress */
> > > -			if ((inodes_checked % 100) == 0 && verbose_flag == 1) {
> > > -				printf("."); fflush(stdout);
> > > -			}
> > > -			inodes_checked++;
> > > -
> > > -			check_parents(parentbuf, parentbuf_size, fshandlep, p);
> > > -		}
> > > -
> > > -	}/*while*/
> > > -
> > > -	fprintf(stderr, _("syssgi bulkstat failed: %s\n"), strerror(errno));
> > > -	return 1;
> > > +	memcpy(buf, pptr->xpp_name, pptr->xpp_namelen);
> > > +	buf[pptr->xpp_namelen] = 0;
> > > +	printf(_("p_ino    = %llu\n"), (unsigned long long)pptr->xpp_ino);
> > > +	printf(_("p_gen    = %u\n"), (unsigned int)pptr->xpp_gen);
> > > +	printf(_("p_reclen = %u\n"), (unsigned int)pptr->xpp_namelen);
> > > +	printf(_("p_name   = \"%s\"\n\n"), buf);
> > > +	return 0;
> > >   }
> > > -static int
> > > -parent_check(void)
> > > +int
> > > +print_parents(
> > > +	struct xfs_handle	*handle)
> > >   {
> > > -	int fsfd;
> > > -	jdm_fshandle_t *fshandlep;
> > > -	parent_t *parentbuf;
> > > -	size_t parentbuf_size = PARENTBUF_SZ;
> > > -	xfs_bstat_t *bstatbuf;
> > > -
> > > -	err_status = 0;
> > > -	inodes_checked = 0;
> > > -
> > > -	sync();
> > > -
> > > -        fsfd = file->fd;
> > > -
> > > -	fshandlep = jdm_getfshandle(mntpt);
> > > -	if (fshandlep == NULL) {
> > > -		fprintf(stderr, _("unable to open \"%s\" for jdm: %s\n"),
> > > -		      mntpt,
> > > -		      strerror(errno));
> > > -		return 1;
> > > -	}
> > > -
> > > -	/* allocate buffers */
> > > -        bstatbuf = (xfs_bstat_t *)calloc(BSTATBUF_SZ, sizeof(xfs_bstat_t));
> > > -	parentbuf = (parent_t *)malloc(parentbuf_size);
> > > -	if (!bstatbuf || !parentbuf) {
> > > -		fprintf(stderr, _("unable to allocate buffers: %s\n"),
> > > -			strerror(errno));
> > > -		err_status = 1;
> > > -		goto out;
> > > -	}
> > > +	int			ret;
> > > -	if (do_bulkstat(parentbuf, &parentbuf_size, bstatbuf, fsfd, fshandlep) != 0)
> > > -		err_status++;
> > > -
> > > -	if (err_status > 0)
> > > -		fprintf(stderr, _("num errors: %d\n"), err_status);
> > > +	if (handle)
> > > +		ret = handle_walk_pptrs(handle, sizeof(*handle), pptr_print,
> > > +				NULL);
> > >   	else
> > > -		printf(_("succeeded checking %llu inodes\n"),
> > > -			(unsigned long long) inodes_checked);
> > > -
> > > -out:
> > > -	free(bstatbuf);
> > > -	free(parentbuf);
> > > -	free(fshandlep);
> > > -	return err_status;
> > > -}
> > > +		ret = fd_walk_pptrs(file->fd, pptr_print, NULL);
> > > +	if (ret)
> > > +		perror(file->name);
> > > -static void
> > > -print_parent_entry(parent_t *parent, int fullpath)
> > > -{
> > > -       printf(_("p_ino    = %llu\n"),  (unsigned long long) parent->p_ino);
> > > -	printf(_("p_gen    = %u\n"),	parent->p_gen);
> > > -	printf(_("p_reclen = %u\n"),	parent->p_reclen);
> > > -	if (fullpath)
> > > -		printf(_("p_name   = \"%s%s\"\n"), mntpt,
> > > -					((char*)parent)+sizeof(struct parent));
> > > -	else
> > > -		printf(_("p_name   = \"%s\"\n"),
> > > -					((char*)parent)+sizeof(struct parent));
> > > +	return 0;
> > >   }
> > >   static int
> > > -parent_list(int fullpath)
> > > -{
> > > -	void *handlep = NULL;
> > > -	size_t handlen;
> > > -	int error, i;
> > > -	int retval = 1;
> > > -	__u32 count;
> > > -	parent_t *entryp;
> > > -	parent_t *parentbuf = NULL;
> > > -	char *path = file->name;
> > > -	int pb_size = PARENTBUF_SZ;
> > > -
> > > -	/* XXXX for linux libhandle version - to set libhandle fsfd cache */
> > > -	{
> > > -		void *fshandle;
> > > -		size_t fshlen;
> > > -
> > > -		if (path_to_fshandle(mntpt, &fshandle, &fshlen) != 0) {
> > > -			fprintf(stderr, _("%s: failed path_to_fshandle \"%s\": %s\n"),
> > > -				progname, path, strerror(errno));
> > > -			goto error;
> > > -		}
> > > -		free_handle(fshandle, fshlen);
> > > -	}
> > > -
> > > -	if (path_to_handle(path, &handlep, &handlen) != 0) {
> > > -		fprintf(stderr, _("%s: path_to_handle failed for \"%s\"\n"), progname, path);
> > > -		goto error;
> > > -	}
> > > -
> > > -	do {
> > > -		parentbuf = (parent_t *)realloc(parentbuf, pb_size);
> > > -		if (!parentbuf) {
> > > -			fprintf(stderr, _("%s: unable to allocate parent buffer: %s\n"),
> > > -				progname, strerror(errno));
> > > -			goto error;
> > > -		}
> > > +path_print(
> > > +	const char		*mntpt,
> > > +	struct path_list	*path,
> > > +	void			*arg) {
> > > -		if (fullpath) {
> > > -			error = parentpaths_by_handle(handlep,
> > > -						       handlen,
> > > -						       parentbuf,
> > > -						       pb_size,
> > > -						       &count);
> > > -		} else {
> > > -			error = parents_by_handle(handlep,
> > > -						   handlen,
> > > -						   parentbuf,
> > > -						   pb_size,
> > > -						   &count);
> > > -		}
> > > -		if (error == ERANGE) {
> > > -			pb_size *= 2;
> > > -		} else if (error) {
> > > -			fprintf(stderr, _("%s: %s call failed for \"%s\": %s\n"),
> > > -				progname, fullpath ? "parentpaths" : "parents",
> > > -				path, strerror(errno));
> > > -			goto error;
> > > -		}
> > > -	} while (error == ERANGE);
> > > +	char			buf[PATH_MAX];
> > > +	size_t			len = PATH_MAX;
> > > +	int			ret;
> > > -	if (count == 0) {
> > > -		/* no links for inode - something wrong here */
> > > -		fprintf(stderr, _("%s: inode-path is missing\n"), progname);
> > > -		goto error;
> > > +	ret = snprintf(buf, len, "%s", mntpt);
> > > +	if (ret != strlen(mntpt)) {
> > > +		errno = ENOMEM;
> > > +		return -1;
> > >   	}
> > > -	entryp = parentbuf;
> > > -	for (i = 0; i < count; i++) {
> > > -		print_parent_entry(entryp, fullpath);
> > > -		entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
> > > -	}
> > > +	ret = path_list_to_string(path, buf + ret, len - ret);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +	return 0;
> > > +}
> > > -	retval = 0;
> > > -error:
> > > -	free(handlep);
> > > -	free(parentbuf);
> > > -	return retval;
> > > +int
> > > +print_paths(
> > > +	struct xfs_handle	*handle)
> > > +{
> > > +	int			ret;
> > > +
> > > +	if (handle)
> > > +		ret = handle_walk_ppaths(handle, sizeof(*handle), path_print,
> > > +				NULL);
> > > + 	else
> > > +		ret = fd_walk_ppaths(file->fd, path_print, NULL);
> > > +	if (ret)
> > > +		perror(file->name);
> > > +	return 0;
> > >   }
> > >   int
> > > -parent_f(int argc, char **argv)
> > > +parent_f(
> > > +	int			argc,
> > > +	char			**argv)
> > >   {
> > > -	int c;
> > > -	int listpath_flag = 0;
> > > -	int check_flag = 0;
> > > -	fs_path_t *fs;
> > > -	static int tab_init;
> > > +	struct xfs_handle	handle;
> > > +	void			*hanp = NULL;
> > > +	size_t			hlen;
> > > +	struct fs_path		*fs;
> > > +	char			*p;
> > > +	uint64_t		ino = 0;
> > > +	uint32_t		gen = 0;
> > > +	int			c;
> > > +	int			listpath_flag = 0;
> > > +	int			ret;
> > > +	static int		tab_init;
> > >   	if (!tab_init) {
> > >   		tab_init = 1;
> > > @@ -394,46 +133,72 @@ parent_f(int argc, char **argv)
> > >   	}
> > >   	mntpt = fs->fs_dir;
> > > -	verbose_flag = 0;
> > > -
> > > -	while ((c = getopt(argc, argv, "cpv")) != EOF) {
> > > +	while ((c = getopt(argc, argv, "p")) != EOF) {
> > >   		switch (c) {
> > > -		case 'c':
> > > -			check_flag = 1;
> > > -			break;
> > >   		case 'p':
> > >   			listpath_flag = 1;
> > >   			break;
> > > -		case 'v':
> > > -			verbose_flag++;
> > > -			break;
> > >   		default:
> > >   			return command_usage(&parent_cmd);
> > >   		}
> > >   	}
> > > -	if (!check_flag && !listpath_flag) /* default case */
> > > -		exitcode = parent_list(listpath_flag);
> > > -	else {
> > > -		if (listpath_flag)
> > > -			exitcode = parent_list(listpath_flag);
> > > -		if (check_flag)
> > > -			exitcode = parent_check();
> > > +	/*
> > > +	 * Always initialize the fshandle table because we need it for
> > > +	 * the ppaths functions to work.
> > > +	 */
> > > +	ret = path_to_fshandle((char *)mntpt, &hanp, &hlen);
> > > +	if (ret) {
> > > +		perror(mntpt);
> > > +		return 0;
> > > + 	}
> > > +
> > > +	if (optind + 2 == argc) {
> > > +		ino = strtoull(argv[optind], &p, 0);
> > > +		if (*p != '\0' || ino == 0) {
> > > +			fprintf(stderr,
> > > +				_("Bad inode number '%s'.\n"),
> > > +				argv[optind]);
> > > +			return 0;
> > > +		}
> > > +		gen = strtoul(argv[optind + 1], &p, 0);
> > > +		if (*p != '\0') {
> > > +			fprintf(stderr,
> > > +				_("Bad generation number '%s'.\n"),
> > > +				argv[optind + 1]);
> > > +			return 0;
> > > +		}
> > > +
> > > +		memcpy(&handle, hanp, sizeof(handle));
> > > +		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
> > > +				sizeof(handle.ha_fid.fid_len);
> > > +		handle.ha_fid.fid_pad = 0;
> > > +		handle.ha_fid.fid_ino = ino;
> > > +		handle.ha_fid.fid_gen = gen;
> > > +
> > >   	}
> > > +	if (listpath_flag)
> > > +		exitcode = print_paths(ino ? &handle : NULL);
> > > +	else
> > > +		exitcode = print_parents(ino ? &handle : NULL);
> > > +
> > > +	if (hanp)
> > > +		free_handle(hanp, hlen);
> > > +
> > >   	return 0;
> > >   }
> > >   static void
> > >   parent_help(void)
> > >   {
> > > -	printf(_(
> > > +printf(_(
> > >   "\n"
> > >   " list the current file's parents and their filenames\n"
> > >   "\n"
> > > -" -c -- check the current file's file system for parent consistency\n"
> > > -" -p -- list the current file's parents and their full paths\n"
> > > -" -v -- verbose mode\n"
> > > +" -p -- list the current file's paths up to the root\n"
> > > +"\n"
> > > +"If ino and gen are supplied, use them instead.\n"
> > >   "\n"));
> > >   }
> > > @@ -444,9 +209,9 @@ parent_init(void)
> > >   	parent_cmd.cfunc = parent_f;
> > >   	parent_cmd.argmin = 0;
> > >   	parent_cmd.argmax = -1;
> > > -	parent_cmd.args = _("[-cpv]");
> > > +	parent_cmd.args = _("[-p] [ino gen]");
> > >   	parent_cmd.flags = CMD_NOMAP_OK;
> > > -	parent_cmd.oneline = _("print or check parent inodes");
> > > +	parent_cmd.oneline = _("print parent inodes");
> > >   	parent_cmd.help = parent_help;
> > >   	if (expert)
> > > diff --git a/libfrog/paths.c b/libfrog/paths.c
> > > index c7895e9..9fb0140 100644
> > > --- a/libfrog/paths.c
> > > +++ b/libfrog/paths.c
> > > @@ -27,6 +27,7 @@
> > >   #include "path.h"
> > >   #include "input.h"
> > >   #include "project.h"
> > > +#include "list.h"
> > >   #include <limits.h>
> > >   extern char *progname;
> > > @@ -632,3 +633,138 @@ fs_table_insert_project_path(
> > >   		exit(1);
> > >   	}
> > >   }
> > > +
> > > +
> > > +/* Structured path components. */
> > > +
> > > +struct path_list {
> > > +	struct list_head	p_head;
> > > +};
> > > +
> > > +struct path_component {
> > > +	struct list_head	pc_list;
> > > +	char			*pc_fname;
> > > +};
> > > +
> > > +/* Initialize a path component with a given name. */
> > > +struct path_component *
> > > +path_component_init(
> > > +	const char		*name)
> > > +{
> > > +	struct path_component	*pc;
> > > +
> > > +	pc = malloc(sizeof(struct path_component));
> > > +	if (!pc)
> > > +		return NULL;
> > > +	INIT_LIST_HEAD(&pc->pc_list);
> > > +	pc->pc_fname = strdup(name);
> > > +	if (!pc->pc_fname) {
> > > +		free(pc);
> > > +		return NULL;
> > > +	}
> > > +	return pc;
> > > +}
> > > +
> > > +/* Free a path component. */
> > > +void
> > > +path_component_free(
> > > +	struct path_component	*pc)
> > > +{
> > > +	free(pc->pc_fname);
> > > +	free(pc);
> > > +}
> > > +
> > > +/* Change a path component's filename. */
> > > +int
> > > +path_component_change(
> > > +	struct path_component	*pc,
> > > +	void			*name,
> > > +	size_t			namelen)
> > > +{
> > > +	void			*p;
> > > +
> > > +	p = realloc(pc->pc_fname, namelen + 1);
> > > +	if (!p)
> > > +		return -1;
> > > +	pc->pc_fname = p;
> > > +	memcpy(pc->pc_fname, name, namelen);
> > > +	pc->pc_fname[namelen] = 0;
> > > +	return 0;
> > > +}
> > > +
> > > +/* Initialize a pathname. */
> > > +struct path_list *
> > > +path_list_init(void)
> > > +{
> > > +	struct path_list	*path;
> > > +
> > > +	path = malloc(sizeof(struct path_list));
> > > +	if (!path)
> > > +		return NULL;
> > > +	INIT_LIST_HEAD(&path->p_head);
> > > +	return path;
> > > +}
> > > +
> > > +/* Empty out a pathname. */
> > > +void
> > > +path_list_free(
> > > +	struct path_list	*path)
> > > +{
> > > +	struct path_component	*pos;
> > > +	struct path_component	*n;
> > > +
> > > +	list_for_each_entry_safe(pos, n, &path->p_head, pc_list) {
> > > +		path_list_del_component(path, pos);
> > > +		path_component_free(pos);
> > > +	}
> > > +	free(path);
> > > +}
> > > +
> > > +/* Add a parent component to a pathname. */
> > > +void
> > > +path_list_add_parent_component(
> > > +	struct path_list	*path,
> > > +	struct path_component	*pc)
> > > +{
> > > +	list_add(&pc->pc_list, &path->p_head);
> > > +}
> > > +
> > > +/* Add a component to a pathname. */
> > > +void
> > > +path_list_add_component(
> > > +	struct path_list	*path,
> > > +	struct path_component	*pc)
> > > +{
> > > +	list_add_tail(&pc->pc_list, &path->p_head);
> > > +}
> > > +
> > > +/* Remove a component from a pathname. */
> > > +void
> > > +path_list_del_component(
> > > +	struct path_list	*path,
> > > +	struct path_component	*pc)
> > > +{
> > > +	list_del_init(&pc->pc_list);
> > > +}
> > > +
> > > +/* Convert a pathname into a string. */
> > > +ssize_t
> > > +path_list_to_string(
> > > +	struct path_list	*path,
> > > +	char			*buf,
> > > +	size_t			buflen)
> > > +{
> > > +	struct path_component	*pos;
> > > +	ssize_t			bytes = 0;
> > > +	int			ret;
> > > +
> > > +	list_for_each_entry(pos, &path->p_head, pc_list) {
> > > +		ret = snprintf(buf, buflen, "/%s", pos->pc_fname);
> > > +		if (ret != 1 + strlen(pos->pc_fname))
> > > +			return -1;
> > > +		bytes += ret;
> > > +		buf += ret;
> > > +		buflen -= ret;
> > > +	}
> > > +	return bytes;
> > > +}
> > > diff --git a/libhandle/Makefile b/libhandle/Makefile
> > > index fe1a2af..d3cea41 100644
> > > --- a/libhandle/Makefile
> > > +++ b/libhandle/Makefile
> > > @@ -16,7 +16,7 @@ else
> > >   LTLDFLAGS += -Wl,--version-script,libhandle.sym
> > >   endif
> > > -CFILES = handle.c jdm.c
> > > +CFILES = handle.c jdm.c parent.c
> > >   LSRCFILES = libhandle.sym
> > >   default: ltdepend $(LTLIBRARY)
> > > diff --git a/libhandle/handle.c b/libhandle/handle.c
> > > index 878d14d..a70fa32 100644
> > > --- a/libhandle/handle.c
> > > +++ b/libhandle/handle.c
> > > @@ -41,7 +41,6 @@ typedef union {
> > >   } comarg_t;
> > >   static int obj_to_handle(char *, int, unsigned int, comarg_t, void**, size_t*);
> > > -static int handle_to_fsfd(void *, char **);
> > >   static char *path_to_fspath(char *path);
> > > @@ -214,8 +213,10 @@ handle_to_fshandle(
> > >   	return 0;
> > >   }
> > > -static int
> > > -handle_to_fsfd(void *hanp, char **path)
> > > +int
> > > +handle_to_fsfd(
> > > +	void		*hanp,
> > > +	char		**path)
> > >   {
> > >   	struct fdhash	*fdhp;
> > > diff --git a/libhandle/parent.c b/libhandle/parent.c
> > > new file mode 100644
> > > index 0000000..f6be3bd
> > > --- /dev/null
> > > +++ b/libhandle/parent.c
> > > @@ -0,0 +1,325 @@
> > > +/*
> > > + * Copyright (C) 2017 Oracle.  All Rights Reserved.
> > > + *
> > > + * Author: Darrick J. Wong <darrick.wong@oracle.com>
> > > + *
> > > + * This program is free software; you can redistribute it and/or
> > > + * modify it under the terms of the GNU General Public License
> > > + * as published by the Free Software Foundation; either version 2
> > > + * of the License, or (at your option) any later version.
> > > + *
> > > + * This program is distributed in the hope that it would be useful,
> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > + * GNU General Public License for more details.
> > > + *
> > > + * You should have received a copy of the GNU General Public License
> > > + * along with this program; if not, write the Free Software Foundation,
> > > + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
> > > + */
> > > +#include "platform_defs.h"
> > > +#include "xfs.h"
> > > +#include "xfs_arch.h"
> > > +#include "list.h"
> > > +#include "path.h"
> > > +#include "handle.h"
> > > +#include "parent.h"
> > > +
> > > +/* Allocate a buffer large enough for some parent pointer records. */
> > > +static inline struct xfs_pptr_info *
> > > +xfs_pptr_alloc(
> > > +      size_t                  nr_ptrs)
> > > +{
> > > +      struct xfs_pptr_info    *pi;
> > > +
> > > +      pi = malloc(XFS_PPTR_INFO_SIZEOF(nr_ptrs));
> > > +      if (!pi)
> > > +              return NULL;
> > > +      memset(pi, 0, sizeof(struct xfs_pptr_info));
> > > +      pi->pi_ptrs_size = nr_ptrs;
> > > +      return pi;
> > > +}
> > > +
> > > +/* Walk all parents of the given file handle. */
> > > +static int
> > > +handle_walk_parents(
> > > +	int			fd,
> > > +	struct xfs_handle	*handle,
> > > +	walk_pptr_fn		fn,
> > > +	void			*arg)
> > > +{
> > > +	struct xfs_pptr_info	*pi;
> > > +	struct xfs_parent_ptr	*p;
> > > +	unsigned int		i;
> > > +	ssize_t			ret = -1;
> > > +
> > > +	pi = xfs_pptr_alloc(4);
> > > +	if (!pi)
> > > +		return -1;
> > > +
> > > +	if (handle) {
> > > +		memcpy(&pi->pi_handle, handle, sizeof(struct xfs_handle));
> > > +		pi->pi_flags = XFS_PPTR_IFLAG_HANDLE;
> > > +	}
> > > +
> > > +	ret = ioctl(fd, XFS_IOC_GETPPOINTER, pi);
> > > +	while (!ret) {
> > > +		if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT) {
> > > +			ret = fn(pi, NULL, arg);
> > > +			break;
> > > +		}
> > > +		if (pi->pi_ptrs_used == 0)
> > > +			break;
> > > +		for (i = 0; i < pi->pi_ptrs_used; i++) {
> > > +			p = XFS_PPINFO_TO_PP(pi, i);
> > > +			ret = fn(pi, p, arg);
> > > +			if (ret)
> > > +				goto out_pi;
> > > +		}
> > > +		ret = ioctl(fd, XFS_IOC_GETPPOINTER, pi);
> > > +	}
> > > +
> > > +out_pi:
> > > +	free(pi);
> > > +	return ret;
> > > +}
> > > +
> > > +/* Walk all parent pointers of this handle. */
> > > +int
> > > +handle_walk_pptrs(
> > > +	void			*hanp,
> > > +	size_t			hlen,
> > > +	walk_pptr_fn		fn,
> > > +	void			*arg)
> > > +{
> > > +	char			*mntpt;
> > > +	int			fd;
> > > +
> > > +	if (hlen != sizeof(struct xfs_handle)) {
> > > +		errno = EINVAL;
> > > +		return -1;
> > > +	}
> > > +
> > > +	fd = handle_to_fsfd(hanp, &mntpt);
> > > +	if (fd < 0)
> > > +		return -1;
> > > +
> > > +	return handle_walk_parents(fd, hanp, fn, arg);
> > > +}
> > > +
> > > +/* Walk all parent pointers of this fd. */
> > > +int
> > > +fd_walk_pptrs(
> > > +	int			fd,
> > > +	walk_pptr_fn		fn,
> > > +	void			*arg)
> > > +{
> > > +	return handle_walk_parents(fd, NULL, fn, arg);
> > > +}
> > > +
> > > +struct walk_ppaths_info {
> > > +	walk_ppath_fn			fn;
> > > +	void				*arg;
> > > +	char				*mntpt;
> > > +	struct path_list		*path;
> > > +	int				fd;
> > > +};
> > > +
> > > +struct walk_ppath_level_info {
> > > +	struct xfs_handle		newhandle;
> > > +	struct path_component		*pc;
> > > +	struct walk_ppaths_info		*wpi;
> > > +};
> > > +
> > > +static int handle_walk_parent_paths(struct walk_ppaths_info *wpi,
> > > +		struct xfs_handle *handle);
> > > +
> > > +static int
> > > +handle_walk_parent_path_ptr(
> > > +	struct xfs_pptr_info		*pi,
> > > +	struct xfs_parent_ptr		*p,
> > > +	void				*arg)
> > > +{
> > > +	struct walk_ppath_level_info	*wpli = arg;
> > > +	struct walk_ppaths_info		*wpi = wpli->wpi;
> > > +	unsigned int			i;
> > > +	int				ret = 0;
> > > +
> > > +	if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT)
> > > +		return wpi->fn(wpi->mntpt, wpi->path, wpi->arg);
> > > +
> > > +	for (i = 0; i < pi->pi_ptrs_used; i++) {
> > > +		p = XFS_PPINFO_TO_PP(pi, i);
> > > +		ret = path_component_change(wpli->pc, p->xpp_name,
> > > +				p->xpp_namelen);
> > > +		if (ret)
> > > +			break;
> > > +		wpli->newhandle.ha_fid.fid_ino = p->xpp_ino;
> > > +		wpli->newhandle.ha_fid.fid_gen = p->xpp_gen;
> > > +		path_list_add_parent_component(wpi->path, wpli->pc);
> > > +		ret = handle_walk_parent_paths(wpi, &wpli->newhandle);
> > > +		path_list_del_component(wpi->path, wpli->pc);
> > > +		if (ret)
> > > +			break;
> > > +	}
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +/*
> > > + * Recursively walk all parents of the given file handle; if we hit the
> > > + * fs root then we call the associated function with the constructed path.
> > > + */
> > > +static int
> > > +handle_walk_parent_paths(
> > > +	struct walk_ppaths_info		*wpi,
> > > +	struct xfs_handle		*handle)
> > > +{
> > > +	struct walk_ppath_level_info	*wpli;
> > > +	int				ret;
> > > +
> > > +	wpli = malloc(sizeof(struct walk_ppath_level_info));
> > > +	if (!wpli)
> > > +		return -1;
> > > +	wpli->pc = path_component_init("");
> > > +	if (!wpli->pc) {
> > > +		free(wpli);
> > > +		return -1;
> > > +	}
> > > +	wpli->wpi = wpi;
> > > +	memcpy(&wpli->newhandle, handle, sizeof(struct xfs_handle));
> > > +
> > > +	ret = handle_walk_parents(wpi->fd, handle, handle_walk_parent_path_ptr,
> > > +			wpli);
> > > +
> > > +	path_component_free(wpli->pc);
> > > +	free(wpli);
> > > +	return ret;
> > > +}
> > > +
> > > +/*
> > > + * Call the given function on all known paths from the vfs root to the inode
> > > + * described in the handle.
> > > + */
> > > +int
> > > +handle_walk_ppaths(
> > > +	void			*hanp,
> > > +	size_t			hlen,
> > > +	walk_ppath_fn		fn,
> > > +	void			*arg)
> > > +{
> > > +	struct walk_ppaths_info	wpi;
> > > +	ssize_t			ret;
> > > +
> > > +	if (hlen != sizeof(struct xfs_handle)) {
> > > +		errno = EINVAL;
> > > +		return -1;
> > > +	}
> > > +
> > > +	wpi.fd = handle_to_fsfd(hanp, &wpi.mntpt);
> > > +	if (wpi.fd < 0)
> > > +		return -1;
> > > +	wpi.path = path_list_init();
> > > +	if (!wpi.path)
> > > +		return -1;
> > > +	wpi.fn = fn;
> > > +	wpi.arg = arg;
> > > +
> > > +	ret = handle_walk_parent_paths(&wpi, hanp);
> > > +	path_list_free(wpi.path);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +/*
> > > + * Call the given function on all known paths from the vfs root to the inode
> > > + * referred to by the file description.
> > > + */
> > > +int
> > > +fd_walk_ppaths(
> > > +	int			fd,
> > > +	walk_ppath_fn		fn,
> > > +	void			*arg)
> > > +{
> > > +	struct walk_ppaths_info	wpi;
> > > +	void			*hanp;
> > > +	size_t			hlen;
> > > +	int			fsfd;
> > > +	int			ret;
> > > +
> > > +	ret = fd_to_handle(fd, &hanp, &hlen);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	fsfd = handle_to_fsfd(hanp, &wpi.mntpt);
> > > +	if (fsfd < 0)
> > > +		return -1;
> > > +	wpi.fd = fd;
> > > +	wpi.path = path_list_init();
> > > +	if (!wpi.path)
> > > +		return -1;
> > > +	wpi.fn = fn;
> > > +	wpi.arg = arg;
> > > +
> > > +	ret = handle_walk_parent_paths(&wpi, hanp);
> > > +	path_list_free(wpi.path);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +struct path_walk_info {
> > > +	char			*buf;
> > > +	size_t			len;
> > > +};
> > > +
> > > +/* Helper that stringifies the first full path that we find. */
> > > +static int
> > > +handle_to_path_walk(
> > > +	const char		*mntpt,
> > > +	struct path_list	*path,
> > > +	void			*arg)
> > > +{
> > > +	struct path_walk_info	*pwi = arg;
> > > +	int			ret;
> > > +
> > > +	ret = snprintf(pwi->buf, pwi->len, "%s", mntpt);
> > > +	if (ret != strlen(mntpt)) {
> > > +		errno = ENOMEM;
> > > +		return -1;
> > > +	}
> > > +
> > > +	ret = path_list_to_string(path, pwi->buf + ret, pwi->len - ret);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	return WALK_PPATHS_ABORT;
> > > +}
> > > +
> > > +/* Return any eligible path to this file handle. */
> > > +int
> > > +handle_to_path(
> > > +	void			*hanp,
> > > +	size_t			hlen,
> > > +	char			*path,
> > > +	size_t			pathlen)
> > > +{
> > > +	struct path_walk_info	pwi;
> > > +
> > > +	pwi.buf = path;
> > > +	pwi.len = pathlen;
> > > +	return handle_walk_ppaths(hanp, hlen, handle_to_path_walk, &pwi);
> > > +}
> > > +
> > > +/* Return any eligible path to this file description. */
> > > +int
> > > +fd_to_path(
> > > +	int			fd,
> > > +	char			*path,
> > > +	size_t			pathlen)
> > > +{
> > > +	struct path_walk_info	pwi;
> > > +
> > > +	pwi.buf = path;
> > > +	pwi.len = pathlen;
> > > +	return fd_walk_ppaths(fd, handle_to_path_walk, &pwi);
> > > +}
> > > diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
> > > index e3ce233..aa613f9 100644
> > > --- a/libxfs/xfs_fs.h
> > > +++ b/libxfs/xfs_fs.h
> > > @@ -610,6 +610,14 @@ struct xfs_pptr_info {
> > >   #define XFS_PPINFO_TO_PP(info, idx)    \
> > >   	(&(((struct xfs_parent_ptr *)((char *)(info) + sizeof(*(info))))[(idx)]))
> > > +#define XFS_PPTR_ALL_IFLAGS    (XFS_PPTR_IFLAG_HANDLE)
> > > +
> > > +/* partial results only */
> > > +#define XFS_PPTR_OFLAG_PARTIAL (1U << 0)
> > > +
> > > +/* target was the root directory */
> > > +#define XFS_PPTR_OFLAG_ROOT    (1U << 1)
> > 
> > Uhoh, I forgot about this chunk, which should be in the kernel patches
> > somewhere I guess...
> > 
> > --D
> > 
> 
> Do we want to keep these?  Should I add behavior of these in the kernel side
> set?

I don't even remember why OFLAG_PARTIAL exists, it can certainly go.

OFLAG_ROOT is returned for the root directory so that callers can
distinguish it from an unlinked inode (which also has no parents).

--D

> 
> Allison
> 
> > > +
> > >   /*
> > >    * ioctl limits
> > >    */
> > > diff --git a/scrub/inodes.c b/scrub/inodes.c
> > > index ccfb9e0..3fbcd1a 100644
> > > --- a/scrub/inodes.c
> > > +++ b/scrub/inodes.c
> > > @@ -31,6 +31,7 @@
> > >   #include "xfs_scrub.h"
> > >   #include "common.h"
> > >   #include "inodes.h"
> > > +#include "parent.h"
> > >   /*
> > >    * Iterate a range of inodes.
> > > @@ -293,3 +294,28 @@ xfs_open_handle(
> > >   	return open_by_fshandle(handle, sizeof(*handle),
> > >   			O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY);
> > >   }
> > > +
> > > +/* Construct a description for an inode. */
> > > +void
> > > +xfs_scrub_ino_descr(
> > > +	struct scrub_ctx	*ctx,
> > > +	struct xfs_handle	*handle,
> > > +	char			*buf,
> > > +	size_t			buflen)
> > > +{
> > > +	uint64_t		ino;
> > > +	xfs_agnumber_t		agno;
> > > +	xfs_agino_t		agino;
> > > +	int			ret;
> > > +
> > > +	ret = handle_to_path(handle, sizeof(struct xfs_handle), buf, buflen);
> > > +	if (ret >= 0)
> > > +		return;
> > > +
> > > +	ino = handle->ha_fid.fid_ino;
> > > +	agno = ino / (1ULL << (ctx->inopblog + ctx->agblklog));
> > > +	agino = ino % (1ULL << (ctx->inopblog + ctx->agblklog));
> > > +	snprintf(buf, buflen, _("inode %"PRIu64" (%u/%u)"), ino, agno,
> > > +			agino);
> > > +}
> > > +
> > > diff --git a/scrub/inodes.h b/scrub/inodes.h
> > > index 693cb05..e94de0a 100644
> > > --- a/scrub/inodes.h
> > > +++ b/scrub/inodes.h
> > > @@ -28,5 +28,7 @@ bool xfs_scan_all_inodes(struct scrub_ctx *ctx, xfs_inode_iter_fn fn,
> > >   		void *arg);
> > >   int xfs_open_handle(struct xfs_handle *handle);
> > > +void xfs_scrub_ino_descr(struct scrub_ctx *ctx, struct xfs_handle *handle,
> > > +		char *buf, size_t buflen);
> > >   #endif /* XFS_SCRUB_INODES_H_ */
> > > diff --git a/scrub/phase5.c b/scrub/phase5.c
> > > index 01038f7..ecaaaaa 100644
> > > --- a/scrub/phase5.c
> > > +++ b/scrub/phase5.c
> > > @@ -245,16 +245,11 @@ xfs_scrub_connections(
> > >   	void			*arg)
> > >   {
> > >   	bool			*pmoveon = arg;
> > > -	char			descr[DESCR_BUFSZ];
> > > +	char			descr[PATH_MAX];
> > >   	bool			moveon = true;
> > > -	xfs_agnumber_t		agno;
> > > -	xfs_agino_t		agino;
> > >   	int			fd = -1;
> > > -	agno = bstat->bs_ino / (1ULL << (ctx->inopblog + ctx->agblklog));
> > > -	agino = bstat->bs_ino % (1ULL << (ctx->inopblog + ctx->agblklog));
> > > -	snprintf(descr, DESCR_BUFSZ, _("inode %"PRIu64" (%u/%u)"),
> > > -			(uint64_t)bstat->bs_ino, agno, agino);
> > > +	xfs_scrub_ino_descr(ctx, handle, descr, PATH_MAX);
> > >   	background_sleep();
> > >   	/* Warn about naming problems in xattrs. */
> > > -- 
> > > 2.7.4
> > > 
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Allison Henderson May 9, 2018, 1:47 a.m. UTC | #9
On 05/08/2018 06:44 PM, Darrick J. Wong wrote:
> 
>> On 05/08/2018 10:45 AM, Darrick J. Wong wrote:
>>> On Mon, May 07, 2018 at 09:41:19PM -0700, Allison Henderson wrote:
>>>> From: "Darrick J. Wong" <darrick.wong@oracle.com>
>>>>
>>>> Add ioctl definitions to libxfs, build the necessary helpers into
>>>> libfrog and libhandle to iterate parents (and parent paths), then wire
>>>> up xfs_scrub to be able to query parent pointers from userspace.  The
>>>> goal of this patch is to exercise userspace, and is nowhere near a
>>>> complete solution.  A basic xfs_io parent command implementation
>>>> replaces ... whatever that is that's there now.
>>>>
>>>> Totally missing: actual support in libxfs for working with parent ptrs
>>>> straight off the disk (mkfs, xfs_db, xfs_repair).
>>>>
>>>> [achender: Minor syntax adjustments to sew solution in actual support
>>>> 	   in libxfs for working with parent ptrs]
>>>>
>>>> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
>>>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>>>> ---
>>>>    include/handle.h   |   2 +
>>>>    include/parent.h   |  18 ++
>>>>    include/path.h     |  19 +++
>>>>    io/parent.c        | 471 ++++++++++++++---------------------------------------
>>>>    libfrog/paths.c    | 136 ++++++++++++++++
>>>>    libhandle/Makefile |   2 +-
>>>>    libhandle/handle.c |   7 +-
>>>>    libhandle/parent.c | 325 ++++++++++++++++++++++++++++++++++++
>>>>    libxfs/xfs_fs.h    |   8 +
>>>>    scrub/inodes.c     |  26 +++
>>>>    scrub/inodes.h     |   2 +
>>>>    scrub/phase5.c     |   9 +-
>>>>    12 files changed, 661 insertions(+), 364 deletions(-)
>>>>
>>>> diff --git a/include/handle.h b/include/handle.h
>>>> index 49f1441..00aa43d 100644
>>>> --- a/include/handle.h
>>>> +++ b/include/handle.h
>>>> @@ -52,6 +52,8 @@ extern int  fssetdm_by_handle (void *__hanp, size_t __hlen,
>>>>    void fshandle_destroy(void);
>>>> +int handle_to_fsfd(void *hanp, char **path);
>>>> +
>>>>    #ifdef __cplusplus
>>>>    }
>>>>    #endif
>>>> diff --git a/include/parent.h b/include/parent.h
>>>> index 85cef85..33f8d85 100644
>>>> --- a/include/parent.h
>>>> +++ b/include/parent.h
>>>> @@ -28,4 +28,22 @@ typedef struct parent_cursor {
>>>>    	__u32	opaque[4];      /* an opaque cookie */
>>>>    } parent_cursor_t;
>>>> +struct path_list;
>>>> +
>>>> +typedef int (*walk_pptr_fn)(struct xfs_pptr_info *pi, struct xfs_parent_ptr *pptr,
>>>> +		void *arg);
>>>> +typedef int (*walk_ppath_fn)(const char *mntpt, struct path_list *path,
>>>> +		void *arg);
>>>> +
>>>> +#define WALK_PPTRS_ABORT	1
>>>> +int fd_walk_pptrs(int fd, walk_pptr_fn fn, void *arg);
>>>> +int handle_walk_pptrs(void *hanp, size_t hanlen, walk_pptr_fn fn, void *arg);
>>>> +
>>>> +#define WALK_PPATHS_ABORT	1
>>>> +int fd_walk_ppaths(int fd, walk_ppath_fn fn, void *arg);
>>>> +int handle_walk_ppaths(void *hanp, size_t hanlen, walk_ppath_fn fn, void *arg);
>>>> +
>>>> +int fd_to_path(int fd, char *path, size_t pathlen);
>>>> +int handle_to_path(void *hanp, size_t hlen, char *path, size_t pathlen);
>>>> +
>>>>    #endif
>>>> diff --git a/include/path.h b/include/path.h
>>>> index 88dc44b..cbe4e19 100644
>>>> --- a/include/path.h
>>>> +++ b/include/path.h
>>>> @@ -70,4 +70,23 @@ typedef struct fs_cursor {
>>>>    extern void fs_cursor_initialise(char *__dir, uint __flags, fs_cursor_t *__cp);
>>>>    extern fs_path_t *fs_cursor_next_entry(fs_cursor_t *__cp);
>>>> +/* Path information. */
>>>> +
>>>> +struct path_list;
>>>> +struct path_component;
>>>> +
>>>> +struct path_component *path_component_init(const char *name);
>>>> +void path_component_free(struct path_component *pc);
>>>> +int path_component_change(struct path_component *pc, void *name,
>>>> +		size_t namelen);
>>>> +
>>>> +struct path_list *path_list_init(void);
>>>> +void path_list_free(struct path_list *path);
>>>> +void path_list_add_parent_component(struct path_list *path,
>>>> +		struct path_component *pc);
>>>> +void path_list_add_component(struct path_list *path, struct path_component *pc);
>>>> +void path_list_del_component(struct path_list *path, struct path_component *pc);
>>>> +
>>>> +ssize_t path_list_to_string(struct path_list *path, char *buf, size_t buflen);
>>>> +
>>>>    #endif	/* __PATH_H__ */
>>>> diff --git a/io/parent.c b/io/parent.c
>>>> index 55b8b49..ad51fe6 100644
>>>> --- a/io/parent.c
>>>> +++ b/io/parent.c
>>>> @@ -21,366 +21,105 @@
>>>>    #include "path.h"
>>>>    #include "parent.h"
>>>>    #include "handle.h"
>>>> -#include "jdm.h"
>>>>    #include "init.h"
>>>>    #include "io.h"
>>>> -#define PARENTBUF_SZ		16384
>>>> -#define BSTATBUF_SZ		16384
>>>> -
>>>>    static cmdinfo_t parent_cmd;
>>>> -static int verbose_flag;
>>>> -static int err_status;
>>>> -static __u64 inodes_checked;
>>>>    static char *mntpt;
>>>> -/*
>>>> - * check out a parent entry to see if the values seem valid
>>>> - */
>>>> -static void
>>>> -check_parent_entry(xfs_bstat_t *bstatp, parent_t *parent)
>>>> -{
>>>> -	int sts;
>>>> -	char fullpath[PATH_MAX];
>>>> -	struct stat statbuf;
>>>> -	char *str;
>>>> -
>>>> -	snprintf(fullpath, parent->p_reclen, _("%s%s"), mntpt,
>>>> -				((char*)parent)+sizeof(struct parent));
>>>> -
>>>> -	sts = lstat(fullpath, &statbuf);
>>>> -	if (sts != 0) {
>>>> -		fprintf(stderr,
>>>> -			_("inode-path for inode: %llu is incorrect - path \"%s\" non-existent\n"),
>>>> -			(unsigned long long) bstatp->bs_ino, fullpath);
>>>> -		if (verbose_flag) {
>>>> -			fprintf(stderr,
>>>> -				_("path \"%s\" does not stat for inode: %llu; err = %s\n"),
>>>> -				fullpath,
>>>> -			       (unsigned long long) bstatp->bs_ino,
>>>> -				strerror(errno));
>>>> -		}
>>>> -		err_status++;
>>>> -		return;
>>>> -	} else {
>>>> -		if (verbose_flag > 1) {
>>>> -			printf(_("path \"%s\" found\n"), fullpath);
>>>> -		}
>>>> -	}
>>>> -
>>>> -	if (statbuf.st_ino != bstatp->bs_ino) {
>>>> -		fprintf(stderr,
>>>> -			_("inode-path for inode: %llu is incorrect - wrong inode#\n"),
>>>> -		       (unsigned long long) bstatp->bs_ino);
>>>> -		if (verbose_flag) {
>>>> -			fprintf(stderr,
>>>> -				_("ino mismatch for path \"%s\" %llu vs %llu\n"),
>>>> -				fullpath,
>>>> -				(unsigned long long)statbuf.st_ino,
>>>> -				(unsigned long long)bstatp->bs_ino);
>>>> -		}
>>>> -		err_status++;
>>>> -		return;
>>>> -	} else if (verbose_flag > 1) {
>>>> -		printf(_("inode number match: %llu\n"),
>>>> -			(unsigned long long)statbuf.st_ino);
>>>> -	}
>>>> -
>>>> -	/* get parent path */
>>>> -	str = strrchr(fullpath, '/');
>>>> -	*str = '\0';
>>>> -	sts = stat(fullpath, &statbuf);
>>>> -	if (sts != 0) {
>>>> -		fprintf(stderr,
>>>> -			_("parent path \"%s\" does not stat: %s\n"),
>>>> -			fullpath,
>>>> -			strerror(errno));
>>>> -		err_status++;
>>>> -		return;
>>>> -	} else {
>>>> -		if (parent->p_ino != statbuf.st_ino) {
>>>> -			fprintf(stderr,
>>>> -				_("inode-path for inode: %llu is incorrect - wrong parent inode#\n"),
>>>> -			       (unsigned long long) bstatp->bs_ino);
>>>> -			if (verbose_flag) {
>>>> -				fprintf(stderr,
>>>> -					_("ino mismatch for path \"%s\" %llu vs %llu\n"),
>>>> -					fullpath,
>>>> -					(unsigned long long)parent->p_ino,
>>>> -					(unsigned long long)statbuf.st_ino);
>>>> -			}
>>>> -			err_status++;
>>>> -			return;
>>>> -		} else {
>>>> -			if (verbose_flag > 1) {
>>>> -			       printf(_("parent ino match for %llu\n"),
>>>> -				       (unsigned long long) parent->p_ino);
>>>> -			}
>>>> -		}
>>>> -	}
>>>> -}
>>>> -
>>>> -static void
>>>> -check_parents(parent_t *parentbuf, size_t *parentbuf_size,
>>>> -	     jdm_fshandle_t *fshandlep, xfs_bstat_t *statp)
>>>> -{
>>>> -	int error, i;
>>>> -	__u32 count;
>>>> -	parent_t *entryp;
>>>> -
>>>> -	do {
>>>> -		error = jdm_parentpaths(fshandlep, statp, parentbuf, *parentbuf_size, &count);
>>>> -
>>>> -		if (error == ERANGE) {
>>>> -			*parentbuf_size *= 2;
>>>> -			parentbuf = (parent_t *)realloc(parentbuf, *parentbuf_size);
>>>> -		} else if (error) {
>>>> -			fprintf(stderr, _("parentpaths failed for ino %llu: %s\n"),
>>>> -			       (unsigned long long) statp->bs_ino,
>>>> -				strerror(errno));
>>>> -			err_status++;
>>>> -			break;
>>>> -		}
>>>> -	} while (error == ERANGE);
>>>> -
>>>> -
>>>> -	if (count == 0) {
>>>> -		/* no links for inode - something wrong here */
>>>> -	       fprintf(stderr, _("inode-path for inode: %llu is missing\n"),
>>>> -			       (unsigned long long) statp->bs_ino);
>>>> -		err_status++;
>>>> -	}
>>>> -
>>>> -	entryp = parentbuf;
>>>> -	for (i = 0; i < count; i++) {
>>>> -		check_parent_entry(statp, entryp);
>>>> -		entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
>>>> -	}
>>>> -}
>>>> -
>>>>    static int
>>>> -do_bulkstat(parent_t *parentbuf, size_t *parentbuf_size, xfs_bstat_t *bstatbuf,
>>>> -	    int fsfd, jdm_fshandle_t *fshandlep)
>>>> +pptr_print(
>>>> +	struct xfs_pptr_info	*pi,
>>>> +	struct xfs_parent_ptr	*pptr,
>>>> +	void			*arg)
>>>>    {
>>>> -	__s32 buflenout;
>>>> -	__u64 lastino = 0;
>>>> -	xfs_bstat_t *p;
>>>> -	xfs_bstat_t *endp;
>>>> -	xfs_fsop_bulkreq_t bulkreq;
>>>> -	struct stat mntstat;
>>>> +	char			buf[XFS_PPTR_MAXNAMELEN + 1];
>>>> -	if (stat(mntpt, &mntstat)) {
>>>> -		fprintf(stderr, _("can't stat mount point \"%s\": %s\n"),
>>>> -			mntpt, strerror(errno));
>>>> -		return 1;
>>>> +	if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT) {
>>>> +		printf(_("Root directory.\n"));
>>>> +		return 0;
>>>>    	}
>>>> -	bulkreq.lastip  = &lastino;
>>>> -	bulkreq.icount  = BSTATBUF_SZ;
>>>> -	bulkreq.ubuffer = (void *)bstatbuf;
>>>> -	bulkreq.ocount  = &buflenout;
>>>> -
>>>> -	while (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT, &bulkreq) == 0) {
>>>> -		if (*(bulkreq.ocount) == 0) {
>>>> -			return 0;
>>>> -		}
>>>> -		for (p = bstatbuf, endp = bstatbuf + *bulkreq.ocount; p < endp; p++) {
>>>> -
>>>> -			/* inode being modified, get synced data with iget */
>>>> -			if ( (!p->bs_nlink || !p->bs_mode) && p->bs_ino != 0 ) {
>>>> -
>>>> -				if (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) < 0) {
>>>> -				    fprintf(stderr,
>>>> -					  _("failed to get bulkstat information for inode %llu\n"),
>>>> -					 (unsigned long long) p->bs_ino);
>>>> -				    continue;
>>>> -				}
>>>> -				if (!p->bs_nlink || !p->bs_mode || !p->bs_ino) {
>>>> -				    fprintf(stderr,
>>>> -					  _("failed to get valid bulkstat information for inode %llu\n"),
>>>> -					 (unsigned long long) p->bs_ino);
>>>> -				    continue;
>>>> -				}
>>>> -			}
>>>> -
>>>> -			/* skip root */
>>>> -			if (p->bs_ino == mntstat.st_ino) {
>>>> -				continue;
>>>> -			}
>>>> -
>>>> -			if (verbose_flag > 1) {
>>>> -			       printf(_("checking inode %llu\n"),
>>>> -				       (unsigned long long) p->bs_ino);
>>>> -			}
>>>> -
>>>> -			/* print dotted progress */
>>>> -			if ((inodes_checked % 100) == 0 && verbose_flag == 1) {
>>>> -				printf("."); fflush(stdout);
>>>> -			}
>>>> -			inodes_checked++;
>>>> -
>>>> -			check_parents(parentbuf, parentbuf_size, fshandlep, p);
>>>> -		}
>>>> -
>>>> -	}/*while*/
>>>> -
>>>> -	fprintf(stderr, _("syssgi bulkstat failed: %s\n"), strerror(errno));
>>>> -	return 1;
>>>> +	memcpy(buf, pptr->xpp_name, pptr->xpp_namelen);
>>>> +	buf[pptr->xpp_namelen] = 0;
>>>> +	printf(_("p_ino    = %llu\n"), (unsigned long long)pptr->xpp_ino);
>>>> +	printf(_("p_gen    = %u\n"), (unsigned int)pptr->xpp_gen);
>>>> +	printf(_("p_reclen = %u\n"), (unsigned int)pptr->xpp_namelen);
>>>> +	printf(_("p_name   = \"%s\"\n\n"), buf);
>>>> +	return 0;
>>>>    }
>>>> -static int
>>>> -parent_check(void)
>>>> +int
>>>> +print_parents(
>>>> +	struct xfs_handle	*handle)
>>>>    {
>>>> -	int fsfd;
>>>> -	jdm_fshandle_t *fshandlep;
>>>> -	parent_t *parentbuf;
>>>> -	size_t parentbuf_size = PARENTBUF_SZ;
>>>> -	xfs_bstat_t *bstatbuf;
>>>> -
>>>> -	err_status = 0;
>>>> -	inodes_checked = 0;
>>>> -
>>>> -	sync();
>>>> -
>>>> -        fsfd = file->fd;
>>>> -
>>>> -	fshandlep = jdm_getfshandle(mntpt);
>>>> -	if (fshandlep == NULL) {
>>>> -		fprintf(stderr, _("unable to open \"%s\" for jdm: %s\n"),
>>>> -		      mntpt,
>>>> -		      strerror(errno));
>>>> -		return 1;
>>>> -	}
>>>> -
>>>> -	/* allocate buffers */
>>>> -        bstatbuf = (xfs_bstat_t *)calloc(BSTATBUF_SZ, sizeof(xfs_bstat_t));
>>>> -	parentbuf = (parent_t *)malloc(parentbuf_size);
>>>> -	if (!bstatbuf || !parentbuf) {
>>>> -		fprintf(stderr, _("unable to allocate buffers: %s\n"),
>>>> -			strerror(errno));
>>>> -		err_status = 1;
>>>> -		goto out;
>>>> -	}
>>>> +	int			ret;
>>>> -	if (do_bulkstat(parentbuf, &parentbuf_size, bstatbuf, fsfd, fshandlep) != 0)
>>>> -		err_status++;
>>>> -
>>>> -	if (err_status > 0)
>>>> -		fprintf(stderr, _("num errors: %d\n"), err_status);
>>>> +	if (handle)
>>>> +		ret = handle_walk_pptrs(handle, sizeof(*handle), pptr_print,
>>>> +				NULL);
>>>>    	else
>>>> -		printf(_("succeeded checking %llu inodes\n"),
>>>> -			(unsigned long long) inodes_checked);
>>>> -
>>>> -out:
>>>> -	free(bstatbuf);
>>>> -	free(parentbuf);
>>>> -	free(fshandlep);
>>>> -	return err_status;
>>>> -}
>>>> +		ret = fd_walk_pptrs(file->fd, pptr_print, NULL);
>>>> +	if (ret)
>>>> +		perror(file->name);
>>>> -static void
>>>> -print_parent_entry(parent_t *parent, int fullpath)
>>>> -{
>>>> -       printf(_("p_ino    = %llu\n"),  (unsigned long long) parent->p_ino);
>>>> -	printf(_("p_gen    = %u\n"),	parent->p_gen);
>>>> -	printf(_("p_reclen = %u\n"),	parent->p_reclen);
>>>> -	if (fullpath)
>>>> -		printf(_("p_name   = \"%s%s\"\n"), mntpt,
>>>> -					((char*)parent)+sizeof(struct parent));
>>>> -	else
>>>> -		printf(_("p_name   = \"%s\"\n"),
>>>> -					((char*)parent)+sizeof(struct parent));
>>>> +	return 0;
>>>>    }
>>>>    static int
>>>> -parent_list(int fullpath)
>>>> -{
>>>> -	void *handlep = NULL;
>>>> -	size_t handlen;
>>>> -	int error, i;
>>>> -	int retval = 1;
>>>> -	__u32 count;
>>>> -	parent_t *entryp;
>>>> -	parent_t *parentbuf = NULL;
>>>> -	char *path = file->name;
>>>> -	int pb_size = PARENTBUF_SZ;
>>>> -
>>>> -	/* XXXX for linux libhandle version - to set libhandle fsfd cache */
>>>> -	{
>>>> -		void *fshandle;
>>>> -		size_t fshlen;
>>>> -
>>>> -		if (path_to_fshandle(mntpt, &fshandle, &fshlen) != 0) {
>>>> -			fprintf(stderr, _("%s: failed path_to_fshandle \"%s\": %s\n"),
>>>> -				progname, path, strerror(errno));
>>>> -			goto error;
>>>> -		}
>>>> -		free_handle(fshandle, fshlen);
>>>> -	}
>>>> -
>>>> -	if (path_to_handle(path, &handlep, &handlen) != 0) {
>>>> -		fprintf(stderr, _("%s: path_to_handle failed for \"%s\"\n"), progname, path);
>>>> -		goto error;
>>>> -	}
>>>> -
>>>> -	do {
>>>> -		parentbuf = (parent_t *)realloc(parentbuf, pb_size);
>>>> -		if (!parentbuf) {
>>>> -			fprintf(stderr, _("%s: unable to allocate parent buffer: %s\n"),
>>>> -				progname, strerror(errno));
>>>> -			goto error;
>>>> -		}
>>>> +path_print(
>>>> +	const char		*mntpt,
>>>> +	struct path_list	*path,
>>>> +	void			*arg) {
>>>> -		if (fullpath) {
>>>> -			error = parentpaths_by_handle(handlep,
>>>> -						       handlen,
>>>> -						       parentbuf,
>>>> -						       pb_size,
>>>> -						       &count);
>>>> -		} else {
>>>> -			error = parents_by_handle(handlep,
>>>> -						   handlen,
>>>> -						   parentbuf,
>>>> -						   pb_size,
>>>> -						   &count);
>>>> -		}
>>>> -		if (error == ERANGE) {
>>>> -			pb_size *= 2;
>>>> -		} else if (error) {
>>>> -			fprintf(stderr, _("%s: %s call failed for \"%s\": %s\n"),
>>>> -				progname, fullpath ? "parentpaths" : "parents",
>>>> -				path, strerror(errno));
>>>> -			goto error;
>>>> -		}
>>>> -	} while (error == ERANGE);
>>>> +	char			buf[PATH_MAX];
>>>> +	size_t			len = PATH_MAX;
>>>> +	int			ret;
>>>> -	if (count == 0) {
>>>> -		/* no links for inode - something wrong here */
>>>> -		fprintf(stderr, _("%s: inode-path is missing\n"), progname);
>>>> -		goto error;
>>>> +	ret = snprintf(buf, len, "%s", mntpt);
>>>> +	if (ret != strlen(mntpt)) {
>>>> +		errno = ENOMEM;
>>>> +		return -1;
>>>>    	}
>>>> -	entryp = parentbuf;
>>>> -	for (i = 0; i < count; i++) {
>>>> -		print_parent_entry(entryp, fullpath);
>>>> -		entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
>>>> -	}
>>>> +	ret = path_list_to_string(path, buf + ret, len - ret);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +	return 0;
>>>> +}
>>>> -	retval = 0;
>>>> -error:
>>>> -	free(handlep);
>>>> -	free(parentbuf);
>>>> -	return retval;
>>>> +int
>>>> +print_paths(
>>>> +	struct xfs_handle	*handle)
>>>> +{
>>>> +	int			ret;
>>>> +
>>>> +	if (handle)
>>>> +		ret = handle_walk_ppaths(handle, sizeof(*handle), path_print,
>>>> +				NULL);
>>>> + 	else
>>>> +		ret = fd_walk_ppaths(file->fd, path_print, NULL);
>>>> +	if (ret)
>>>> +		perror(file->name);
>>>> +	return 0;
>>>>    }
>>>>    int
>>>> -parent_f(int argc, char **argv)
>>>> +parent_f(
>>>> +	int			argc,
>>>> +	char			**argv)
>>>>    {
>>>> -	int c;
>>>> -	int listpath_flag = 0;
>>>> -	int check_flag = 0;
>>>> -	fs_path_t *fs;
>>>> -	static int tab_init;
>>>> +	struct xfs_handle	handle;
>>>> +	void			*hanp = NULL;
>>>> +	size_t			hlen;
>>>> +	struct fs_path		*fs;
>>>> +	char			*p;
>>>> +	uint64_t		ino = 0;
>>>> +	uint32_t		gen = 0;
>>>> +	int			c;
>>>> +	int			listpath_flag = 0;
>>>> +	int			ret;
>>>> +	static int		tab_init;
>>>>    	if (!tab_init) {
>>>>    		tab_init = 1;
>>>> @@ -394,46 +133,72 @@ parent_f(int argc, char **argv)
>>>>    	}
>>>>    	mntpt = fs->fs_dir;
>>>> -	verbose_flag = 0;
>>>> -
>>>> -	while ((c = getopt(argc, argv, "cpv")) != EOF) {
>>>> +	while ((c = getopt(argc, argv, "p")) != EOF) {
>>>>    		switch (c) {
>>>> -		case 'c':
>>>> -			check_flag = 1;
>>>> -			break;
>>>>    		case 'p':
>>>>    			listpath_flag = 1;
>>>>    			break;
>>>> -		case 'v':
>>>> -			verbose_flag++;
>>>> -			break;
>>>>    		default:
>>>>    			return command_usage(&parent_cmd);
>>>>    		}
>>>>    	}
>>>> -	if (!check_flag && !listpath_flag) /* default case */
>>>> -		exitcode = parent_list(listpath_flag);
>>>> -	else {
>>>> -		if (listpath_flag)
>>>> -			exitcode = parent_list(listpath_flag);
>>>> -		if (check_flag)
>>>> -			exitcode = parent_check();
>>>> +	/*
>>>> +	 * Always initialize the fshandle table because we need it for
>>>> +	 * the ppaths functions to work.
>>>> +	 */
>>>> +	ret = path_to_fshandle((char *)mntpt, &hanp, &hlen);
>>>> +	if (ret) {
>>>> +		perror(mntpt);
>>>> +		return 0;
>>>> + 	}
>>>> +
>>>> +	if (optind + 2 == argc) {
>>>> +		ino = strtoull(argv[optind], &p, 0);
>>>> +		if (*p != '\0' || ino == 0) {
>>>> +			fprintf(stderr,
>>>> +				_("Bad inode number '%s'.\n"),
>>>> +				argv[optind]);
>>>> +			return 0;
>>>> +		}
>>>> +		gen = strtoul(argv[optind + 1], &p, 0);
>>>> +		if (*p != '\0') {
>>>> +			fprintf(stderr,
>>>> +				_("Bad generation number '%s'.\n"),
>>>> +				argv[optind + 1]);
>>>> +			return 0;
>>>> +		}
>>>> +
>>>> +		memcpy(&handle, hanp, sizeof(handle));
>>>> +		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
>>>> +				sizeof(handle.ha_fid.fid_len);
>>>> +		handle.ha_fid.fid_pad = 0;
>>>> +		handle.ha_fid.fid_ino = ino;
>>>> +		handle.ha_fid.fid_gen = gen;
>>>> +
>>>>    	}
>>>> +	if (listpath_flag)
>>>> +		exitcode = print_paths(ino ? &handle : NULL);
>>>> +	else
>>>> +		exitcode = print_parents(ino ? &handle : NULL);
>>>> +
>>>> +	if (hanp)
>>>> +		free_handle(hanp, hlen);
>>>> +
>>>>    	return 0;
>>>>    }
>>>>    static void
>>>>    parent_help(void)
>>>>    {
>>>> -	printf(_(
>>>> +printf(_(
>>>>    "\n"
>>>>    " list the current file's parents and their filenames\n"
>>>>    "\n"
>>>> -" -c -- check the current file's file system for parent consistency\n"
>>>> -" -p -- list the current file's parents and their full paths\n"
>>>> -" -v -- verbose mode\n"
>>>> +" -p -- list the current file's paths up to the root\n"
>>>> +"\n"
>>>> +"If ino and gen are supplied, use them instead.\n"
>>>>    "\n"));
>>>>    }
>>>> @@ -444,9 +209,9 @@ parent_init(void)
>>>>    	parent_cmd.cfunc = parent_f;
>>>>    	parent_cmd.argmin = 0;
>>>>    	parent_cmd.argmax = -1;
>>>> -	parent_cmd.args = _("[-cpv]");
>>>> +	parent_cmd.args = _("[-p] [ino gen]");
>>>>    	parent_cmd.flags = CMD_NOMAP_OK;
>>>> -	parent_cmd.oneline = _("print or check parent inodes");
>>>> +	parent_cmd.oneline = _("print parent inodes");
>>>>    	parent_cmd.help = parent_help;
>>>>    	if (expert)
>>>> diff --git a/libfrog/paths.c b/libfrog/paths.c
>>>> index c7895e9..9fb0140 100644
>>>> --- a/libfrog/paths.c
>>>> +++ b/libfrog/paths.c
>>>> @@ -27,6 +27,7 @@
>>>>    #include "path.h"
>>>>    #include "input.h"
>>>>    #include "project.h"
>>>> +#include "list.h"
>>>>    #include <limits.h>
>>>>    extern char *progname;
>>>> @@ -632,3 +633,138 @@ fs_table_insert_project_path(
>>>>    		exit(1);
>>>>    	}
>>>>    }
>>>> +
>>>> +
>>>> +/* Structured path components. */
>>>> +
>>>> +struct path_list {
>>>> +	struct list_head	p_head;
>>>> +};
>>>> +
>>>> +struct path_component {
>>>> +	struct list_head	pc_list;
>>>> +	char			*pc_fname;
>>>> +};
>>>> +
>>>> +/* Initialize a path component with a given name. */
>>>> +struct path_component *
>>>> +path_component_init(
>>>> +	const char		*name)
>>>> +{
>>>> +	struct path_component	*pc;
>>>> +
>>>> +	pc = malloc(sizeof(struct path_component));
>>>> +	if (!pc)
>>>> +		return NULL;
>>>> +	INIT_LIST_HEAD(&pc->pc_list);
>>>> +	pc->pc_fname = strdup(name);
>>>> +	if (!pc->pc_fname) {
>>>> +		free(pc);
>>>> +		return NULL;
>>>> +	}
>>>> +	return pc;
>>>> +}
>>>> +
>>>> +/* Free a path component. */
>>>> +void
>>>> +path_component_free(
>>>> +	struct path_component	*pc)
>>>> +{
>>>> +	free(pc->pc_fname);
>>>> +	free(pc);
>>>> +}
>>>> +
>>>> +/* Change a path component's filename. */
>>>> +int
>>>> +path_component_change(
>>>> +	struct path_component	*pc,
>>>> +	void			*name,
>>>> +	size_t			namelen)
>>>> +{
>>>> +	void			*p;
>>>> +
>>>> +	p = realloc(pc->pc_fname, namelen + 1);
>>>> +	if (!p)
>>>> +		return -1;
>>>> +	pc->pc_fname = p;
>>>> +	memcpy(pc->pc_fname, name, namelen);
>>>> +	pc->pc_fname[namelen] = 0;
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +/* Initialize a pathname. */
>>>> +struct path_list *
>>>> +path_list_init(void)
>>>> +{
>>>> +	struct path_list	*path;
>>>> +
>>>> +	path = malloc(sizeof(struct path_list));
>>>> +	if (!path)
>>>> +		return NULL;
>>>> +	INIT_LIST_HEAD(&path->p_head);
>>>> +	return path;
>>>> +}
>>>> +
>>>> +/* Empty out a pathname. */
>>>> +void
>>>> +path_list_free(
>>>> +	struct path_list	*path)
>>>> +{
>>>> +	struct path_component	*pos;
>>>> +	struct path_component	*n;
>>>> +
>>>> +	list_for_each_entry_safe(pos, n, &path->p_head, pc_list) {
>>>> +		path_list_del_component(path, pos);
>>>> +		path_component_free(pos);
>>>> +	}
>>>> +	free(path);
>>>> +}
>>>> +
>>>> +/* Add a parent component to a pathname. */
>>>> +void
>>>> +path_list_add_parent_component(
>>>> +	struct path_list	*path,
>>>> +	struct path_component	*pc)
>>>> +{
>>>> +	list_add(&pc->pc_list, &path->p_head);
>>>> +}
>>>> +
>>>> +/* Add a component to a pathname. */
>>>> +void
>>>> +path_list_add_component(
>>>> +	struct path_list	*path,
>>>> +	struct path_component	*pc)
>>>> +{
>>>> +	list_add_tail(&pc->pc_list, &path->p_head);
>>>> +}
>>>> +
>>>> +/* Remove a component from a pathname. */
>>>> +void
>>>> +path_list_del_component(
>>>> +	struct path_list	*path,
>>>> +	struct path_component	*pc)
>>>> +{
>>>> +	list_del_init(&pc->pc_list);
>>>> +}
>>>> +
>>>> +/* Convert a pathname into a string. */
>>>> +ssize_t
>>>> +path_list_to_string(
>>>> +	struct path_list	*path,
>>>> +	char			*buf,
>>>> +	size_t			buflen)
>>>> +{
>>>> +	struct path_component	*pos;
>>>> +	ssize_t			bytes = 0;
>>>> +	int			ret;
>>>> +
>>>> +	list_for_each_entry(pos, &path->p_head, pc_list) {
>>>> +		ret = snprintf(buf, buflen, "/%s", pos->pc_fname);
>>>> +		if (ret != 1 + strlen(pos->pc_fname))
>>>> +			return -1;
>>>> +		bytes += ret;
>>>> +		buf += ret;
>>>> +		buflen -= ret;
>>>> +	}
>>>> +	return bytes;
>>>> +}
>>>> diff --git a/libhandle/Makefile b/libhandle/Makefile
>>>> index fe1a2af..d3cea41 100644
>>>> --- a/libhandle/Makefile
>>>> +++ b/libhandle/Makefile
>>>> @@ -16,7 +16,7 @@ else
>>>>    LTLDFLAGS += -Wl,--version-script,libhandle.sym
>>>>    endif
>>>> -CFILES = handle.c jdm.c
>>>> +CFILES = handle.c jdm.c parent.c
>>>>    LSRCFILES = libhandle.sym
>>>>    default: ltdepend $(LTLIBRARY)
>>>> diff --git a/libhandle/handle.c b/libhandle/handle.c
>>>> index 878d14d..a70fa32 100644
>>>> --- a/libhandle/handle.c
>>>> +++ b/libhandle/handle.c
>>>> @@ -41,7 +41,6 @@ typedef union {
>>>>    } comarg_t;
>>>>    static int obj_to_handle(char *, int, unsigned int, comarg_t, void**, size_t*);
>>>> -static int handle_to_fsfd(void *, char **);
>>>>    static char *path_to_fspath(char *path);
>>>> @@ -214,8 +213,10 @@ handle_to_fshandle(
>>>>    	return 0;
>>>>    }
>>>> -static int
>>>> -handle_to_fsfd(void *hanp, char **path)
>>>> +int
>>>> +handle_to_fsfd(
>>>> +	void		*hanp,
>>>> +	char		**path)
>>>>    {
>>>>    	struct fdhash	*fdhp;
>>>> diff --git a/libhandle/parent.c b/libhandle/parent.c
>>>> new file mode 100644
>>>> index 0000000..f6be3bd
>>>> --- /dev/null
>>>> +++ b/libhandle/parent.c
>>>> @@ -0,0 +1,325 @@
>>>> +/*
>>>> + * Copyright (C) 2017 Oracle.  All Rights Reserved.
>>>> + *
>>>> + * Author: Darrick J. Wong <darrick.wong@oracle.com>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or
>>>> + * modify it under the terms of the GNU General Public License
>>>> + * as published by the Free Software Foundation; either version 2
>>>> + * of the License, or (at your option) any later version.
>>>> + *
>>>> + * This program is distributed in the hope that it would be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>> + * GNU General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License
>>>> + * along with this program; if not, write the Free Software Foundation,
>>>> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
>>>> + */
>>>> +#include "platform_defs.h"
>>>> +#include "xfs.h"
>>>> +#include "xfs_arch.h"
>>>> +#include "list.h"
>>>> +#include "path.h"
>>>> +#include "handle.h"
>>>> +#include "parent.h"
>>>> +
>>>> +/* Allocate a buffer large enough for some parent pointer records. */
>>>> +static inline struct xfs_pptr_info *
>>>> +xfs_pptr_alloc(
>>>> +      size_t                  nr_ptrs)
>>>> +{
>>>> +      struct xfs_pptr_info    *pi;
>>>> +
>>>> +      pi = malloc(XFS_PPTR_INFO_SIZEOF(nr_ptrs));
>>>> +      if (!pi)
>>>> +              return NULL;
>>>> +      memset(pi, 0, sizeof(struct xfs_pptr_info));
>>>> +      pi->pi_ptrs_size = nr_ptrs;
>>>> +      return pi;
>>>> +}
>>>> +
>>>> +/* Walk all parents of the given file handle. */
>>>> +static int
>>>> +handle_walk_parents(
>>>> +	int			fd,
>>>> +	struct xfs_handle	*handle,
>>>> +	walk_pptr_fn		fn,
>>>> +	void			*arg)
>>>> +{
>>>> +	struct xfs_pptr_info	*pi;
>>>> +	struct xfs_parent_ptr	*p;
>>>> +	unsigned int		i;
>>>> +	ssize_t			ret = -1;
>>>> +
>>>> +	pi = xfs_pptr_alloc(4);
>>>> +	if (!pi)
>>>> +		return -1;
>>>> +
>>>> +	if (handle) {
>>>> +		memcpy(&pi->pi_handle, handle, sizeof(struct xfs_handle));
>>>> +		pi->pi_flags = XFS_PPTR_IFLAG_HANDLE;
>>>> +	}
>>>> +
>>>> +	ret = ioctl(fd, XFS_IOC_GETPPOINTER, pi);
>>>> +	while (!ret) {
>>>> +		if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT) {
>>>> +			ret = fn(pi, NULL, arg);
>>>> +			break;
>>>> +		}
>>>> +		if (pi->pi_ptrs_used == 0)
>>>> +			break;
>>>> +		for (i = 0; i < pi->pi_ptrs_used; i++) {
>>>> +			p = XFS_PPINFO_TO_PP(pi, i);
>>>> +			ret = fn(pi, p, arg);
>>>> +			if (ret)
>>>> +				goto out_pi;
>>>> +		}
>>>> +		ret = ioctl(fd, XFS_IOC_GETPPOINTER, pi);
>>>> +	}
>>>> +
>>>> +out_pi:
>>>> +	free(pi);
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +/* Walk all parent pointers of this handle. */
>>>> +int
>>>> +handle_walk_pptrs(
>>>> +	void			*hanp,
>>>> +	size_t			hlen,
>>>> +	walk_pptr_fn		fn,
>>>> +	void			*arg)
>>>> +{
>>>> +	char			*mntpt;
>>>> +	int			fd;
>>>> +
>>>> +	if (hlen != sizeof(struct xfs_handle)) {
>>>> +		errno = EINVAL;
>>>> +		return -1;
>>>> +	}
>>>> +
>>>> +	fd = handle_to_fsfd(hanp, &mntpt);
>>>> +	if (fd < 0)
>>>> +		return -1;
>>>> +
>>>> +	return handle_walk_parents(fd, hanp, fn, arg);
>>>> +}
>>>> +
>>>> +/* Walk all parent pointers of this fd. */
>>>> +int
>>>> +fd_walk_pptrs(
>>>> +	int			fd,
>>>> +	walk_pptr_fn		fn,
>>>> +	void			*arg)
>>>> +{
>>>> +	return handle_walk_parents(fd, NULL, fn, arg);
>>>> +}
>>>> +
>>>> +struct walk_ppaths_info {
>>>> +	walk_ppath_fn			fn;
>>>> +	void				*arg;
>>>> +	char				*mntpt;
>>>> +	struct path_list		*path;
>>>> +	int				fd;
>>>> +};
>>>> +
>>>> +struct walk_ppath_level_info {
>>>> +	struct xfs_handle		newhandle;
>>>> +	struct path_component		*pc;
>>>> +	struct walk_ppaths_info		*wpi;
>>>> +};
>>>> +
>>>> +static int handle_walk_parent_paths(struct walk_ppaths_info *wpi,
>>>> +		struct xfs_handle *handle);
>>>> +
>>>> +static int
>>>> +handle_walk_parent_path_ptr(
>>>> +	struct xfs_pptr_info		*pi,
>>>> +	struct xfs_parent_ptr		*p,
>>>> +	void				*arg)
>>>> +{
>>>> +	struct walk_ppath_level_info	*wpli = arg;
>>>> +	struct walk_ppaths_info		*wpi = wpli->wpi;
>>>> +	unsigned int			i;
>>>> +	int				ret = 0;
>>>> +
>>>> +	if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT)
>>>> +		return wpi->fn(wpi->mntpt, wpi->path, wpi->arg);
>>>> +
>>>> +	for (i = 0; i < pi->pi_ptrs_used; i++) {
>>>> +		p = XFS_PPINFO_TO_PP(pi, i);
>>>> +		ret = path_component_change(wpli->pc, p->xpp_name,
>>>> +				p->xpp_namelen);
>>>> +		if (ret)
>>>> +			break;
>>>> +		wpli->newhandle.ha_fid.fid_ino = p->xpp_ino;
>>>> +		wpli->newhandle.ha_fid.fid_gen = p->xpp_gen;
>>>> +		path_list_add_parent_component(wpi->path, wpli->pc);
>>>> +		ret = handle_walk_parent_paths(wpi, &wpli->newhandle);
>>>> +		path_list_del_component(wpi->path, wpli->pc);
>>>> +		if (ret)
>>>> +			break;
>>>> +	}
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Recursively walk all parents of the given file handle; if we hit the
>>>> + * fs root then we call the associated function with the constructed path.
>>>> + */
>>>> +static int
>>>> +handle_walk_parent_paths(
>>>> +	struct walk_ppaths_info		*wpi,
>>>> +	struct xfs_handle		*handle)
>>>> +{
>>>> +	struct walk_ppath_level_info	*wpli;
>>>> +	int				ret;
>>>> +
>>>> +	wpli = malloc(sizeof(struct walk_ppath_level_info));
>>>> +	if (!wpli)
>>>> +		return -1;
>>>> +	wpli->pc = path_component_init("");
>>>> +	if (!wpli->pc) {
>>>> +		free(wpli);
>>>> +		return -1;
>>>> +	}
>>>> +	wpli->wpi = wpi;
>>>> +	memcpy(&wpli->newhandle, handle, sizeof(struct xfs_handle));
>>>> +
>>>> +	ret = handle_walk_parents(wpi->fd, handle, handle_walk_parent_path_ptr,
>>>> +			wpli);
>>>> +
>>>> +	path_component_free(wpli->pc);
>>>> +	free(wpli);
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Call the given function on all known paths from the vfs root to the inode
>>>> + * described in the handle.
>>>> + */
>>>> +int
>>>> +handle_walk_ppaths(
>>>> +	void			*hanp,
>>>> +	size_t			hlen,
>>>> +	walk_ppath_fn		fn,
>>>> +	void			*arg)
>>>> +{
>>>> +	struct walk_ppaths_info	wpi;
>>>> +	ssize_t			ret;
>>>> +
>>>> +	if (hlen != sizeof(struct xfs_handle)) {
>>>> +		errno = EINVAL;
>>>> +		return -1;
>>>> +	}
>>>> +
>>>> +	wpi.fd = handle_to_fsfd(hanp, &wpi.mntpt);
>>>> +	if (wpi.fd < 0)
>>>> +		return -1;
>>>> +	wpi.path = path_list_init();
>>>> +	if (!wpi.path)
>>>> +		return -1;
>>>> +	wpi.fn = fn;
>>>> +	wpi.arg = arg;
>>>> +
>>>> +	ret = handle_walk_parent_paths(&wpi, hanp);
>>>> +	path_list_free(wpi.path);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Call the given function on all known paths from the vfs root to the inode
>>>> + * referred to by the file description.
>>>> + */
>>>> +int
>>>> +fd_walk_ppaths(
>>>> +	int			fd,
>>>> +	walk_ppath_fn		fn,
>>>> +	void			*arg)
>>>> +{
>>>> +	struct walk_ppaths_info	wpi;
>>>> +	void			*hanp;
>>>> +	size_t			hlen;
>>>> +	int			fsfd;
>>>> +	int			ret;
>>>> +
>>>> +	ret = fd_to_handle(fd, &hanp, &hlen);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	fsfd = handle_to_fsfd(hanp, &wpi.mntpt);
>>>> +	if (fsfd < 0)
>>>> +		return -1;
>>>> +	wpi.fd = fd;
>>>> +	wpi.path = path_list_init();
>>>> +	if (!wpi.path)
>>>> +		return -1;
>>>> +	wpi.fn = fn;
>>>> +	wpi.arg = arg;
>>>> +
>>>> +	ret = handle_walk_parent_paths(&wpi, hanp);
>>>> +	path_list_free(wpi.path);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +struct path_walk_info {
>>>> +	char			*buf;
>>>> +	size_t			len;
>>>> +};
>>>> +
>>>> +/* Helper that stringifies the first full path that we find. */
>>>> +static int
>>>> +handle_to_path_walk(
>>>> +	const char		*mntpt,
>>>> +	struct path_list	*path,
>>>> +	void			*arg)
>>>> +{
>>>> +	struct path_walk_info	*pwi = arg;
>>>> +	int			ret;
>>>> +
>>>> +	ret = snprintf(pwi->buf, pwi->len, "%s", mntpt);
>>>> +	if (ret != strlen(mntpt)) {
>>>> +		errno = ENOMEM;
>>>> +		return -1;
>>>> +	}
>>>> +
>>>> +	ret = path_list_to_string(path, pwi->buf + ret, pwi->len - ret);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	return WALK_PPATHS_ABORT;
>>>> +}
>>>> +
>>>> +/* Return any eligible path to this file handle. */
>>>> +int
>>>> +handle_to_path(
>>>> +	void			*hanp,
>>>> +	size_t			hlen,
>>>> +	char			*path,
>>>> +	size_t			pathlen)
>>>> +{
>>>> +	struct path_walk_info	pwi;
>>>> +
>>>> +	pwi.buf = path;
>>>> +	pwi.len = pathlen;
>>>> +	return handle_walk_ppaths(hanp, hlen, handle_to_path_walk, &pwi);
>>>> +}
>>>> +
>>>> +/* Return any eligible path to this file description. */
>>>> +int
>>>> +fd_to_path(
>>>> +	int			fd,
>>>> +	char			*path,
>>>> +	size_t			pathlen)
>>>> +{
>>>> +	struct path_walk_info	pwi;
>>>> +
>>>> +	pwi.buf = path;
>>>> +	pwi.len = pathlen;
>>>> +	return fd_walk_ppaths(fd, handle_to_path_walk, &pwi);
>>>> +}
>>>> diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
>>>> index e3ce233..aa613f9 100644
>>>> --- a/libxfs/xfs_fs.h
>>>> +++ b/libxfs/xfs_fs.h
>>>> @@ -610,6 +610,14 @@ struct xfs_pptr_info {
>>>>    #define XFS_PPINFO_TO_PP(info, idx)    \
>>>>    	(&(((struct xfs_parent_ptr *)((char *)(info) + sizeof(*(info))))[(idx)]))
>>>> +#define XFS_PPTR_ALL_IFLAGS    (XFS_PPTR_IFLAG_HANDLE)
>>>> +
>>>> +/* partial results only */
>>>> +#define XFS_PPTR_OFLAG_PARTIAL (1U << 0)
>>>> +
>>>> +/* target was the root directory */
>>>> +#define XFS_PPTR_OFLAG_ROOT    (1U << 1)
>>>
>>> Uhoh, I forgot about this chunk, which should be in the kernel patches
>>> somewhere I guess...
>>>
>>> --D
>>>
>>
>> Do we want to keep these?  Should I add behavior of these in the kernel side
>> set?
> 
> I don't even remember why OFLAG_PARTIAL exists, it can certainly go.
> 
> OFLAG_ROOT is returned for the root directory so that callers can
> distinguish it from an unlinked inode (which also has no parents).
> 
> --D
> 

Ok, I'll keep that one then.  Thx!

>>
>> Allison
>>
>>>> +
>>>>    /*
>>>>     * ioctl limits
>>>>     */
>>>> diff --git a/scrub/inodes.c b/scrub/inodes.c
>>>> index ccfb9e0..3fbcd1a 100644
>>>> --- a/scrub/inodes.c
>>>> +++ b/scrub/inodes.c
>>>> @@ -31,6 +31,7 @@
>>>>    #include "xfs_scrub.h"
>>>>    #include "common.h"
>>>>    #include "inodes.h"
>>>> +#include "parent.h"
>>>>    /*
>>>>     * Iterate a range of inodes.
>>>> @@ -293,3 +294,28 @@ xfs_open_handle(
>>>>    	return open_by_fshandle(handle, sizeof(*handle),
>>>>    			O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY);
>>>>    }
>>>> +
>>>> +/* Construct a description for an inode. */
>>>> +void
>>>> +xfs_scrub_ino_descr(
>>>> +	struct scrub_ctx	*ctx,
>>>> +	struct xfs_handle	*handle,
>>>> +	char			*buf,
>>>> +	size_t			buflen)
>>>> +{
>>>> +	uint64_t		ino;
>>>> +	xfs_agnumber_t		agno;
>>>> +	xfs_agino_t		agino;
>>>> +	int			ret;
>>>> +
>>>> +	ret = handle_to_path(handle, sizeof(struct xfs_handle), buf, buflen);
>>>> +	if (ret >= 0)
>>>> +		return;
>>>> +
>>>> +	ino = handle->ha_fid.fid_ino;
>>>> +	agno = ino / (1ULL << (ctx->inopblog + ctx->agblklog));
>>>> +	agino = ino % (1ULL << (ctx->inopblog + ctx->agblklog));
>>>> +	snprintf(buf, buflen, _("inode %"PRIu64" (%u/%u)"), ino, agno,
>>>> +			agino);
>>>> +}
>>>> +
>>>> diff --git a/scrub/inodes.h b/scrub/inodes.h
>>>> index 693cb05..e94de0a 100644
>>>> --- a/scrub/inodes.h
>>>> +++ b/scrub/inodes.h
>>>> @@ -28,5 +28,7 @@ bool xfs_scan_all_inodes(struct scrub_ctx *ctx, xfs_inode_iter_fn fn,
>>>>    		void *arg);
>>>>    int xfs_open_handle(struct xfs_handle *handle);
>>>> +void xfs_scrub_ino_descr(struct scrub_ctx *ctx, struct xfs_handle *handle,
>>>> +		char *buf, size_t buflen);
>>>>    #endif /* XFS_SCRUB_INODES_H_ */
>>>> diff --git a/scrub/phase5.c b/scrub/phase5.c
>>>> index 01038f7..ecaaaaa 100644
>>>> --- a/scrub/phase5.c
>>>> +++ b/scrub/phase5.c
>>>> @@ -245,16 +245,11 @@ xfs_scrub_connections(
>>>>    	void			*arg)
>>>>    {
>>>>    	bool			*pmoveon = arg;
>>>> -	char			descr[DESCR_BUFSZ];
>>>> +	char			descr[PATH_MAX];
>>>>    	bool			moveon = true;
>>>> -	xfs_agnumber_t		agno;
>>>> -	xfs_agino_t		agino;
>>>>    	int			fd = -1;
>>>> -	agno = bstat->bs_ino / (1ULL << (ctx->inopblog + ctx->agblklog));
>>>> -	agino = bstat->bs_ino % (1ULL << (ctx->inopblog + ctx->agblklog));
>>>> -	snprintf(descr, DESCR_BUFSZ, _("inode %"PRIu64" (%u/%u)"),
>>>> -			(uint64_t)bstat->bs_ino, agno, agino);
>>>> +	xfs_scrub_ino_descr(ctx, handle, descr, PATH_MAX);
>>>>    	background_sleep();
>>>>    	/* Warn about naming problems in xattrs. */
>>>> -- 
>>>> 2.7.4
>>>>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/handle.h b/include/handle.h
index 49f1441..00aa43d 100644
--- a/include/handle.h
+++ b/include/handle.h
@@ -52,6 +52,8 @@  extern int  fssetdm_by_handle (void *__hanp, size_t __hlen,
 
 void fshandle_destroy(void);
 
+int handle_to_fsfd(void *hanp, char **path);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/parent.h b/include/parent.h
index 85cef85..33f8d85 100644
--- a/include/parent.h
+++ b/include/parent.h
@@ -28,4 +28,22 @@  typedef struct parent_cursor {
 	__u32	opaque[4];      /* an opaque cookie */
 } parent_cursor_t;
 
+struct path_list;
+
+typedef int (*walk_pptr_fn)(struct xfs_pptr_info *pi, struct xfs_parent_ptr *pptr,
+		void *arg);
+typedef int (*walk_ppath_fn)(const char *mntpt, struct path_list *path,
+		void *arg);
+
+#define WALK_PPTRS_ABORT	1
+int fd_walk_pptrs(int fd, walk_pptr_fn fn, void *arg);
+int handle_walk_pptrs(void *hanp, size_t hanlen, walk_pptr_fn fn, void *arg);
+
+#define WALK_PPATHS_ABORT	1
+int fd_walk_ppaths(int fd, walk_ppath_fn fn, void *arg);
+int handle_walk_ppaths(void *hanp, size_t hanlen, walk_ppath_fn fn, void *arg);
+
+int fd_to_path(int fd, char *path, size_t pathlen);
+int handle_to_path(void *hanp, size_t hlen, char *path, size_t pathlen);
+
 #endif
diff --git a/include/path.h b/include/path.h
index 88dc44b..cbe4e19 100644
--- a/include/path.h
+++ b/include/path.h
@@ -70,4 +70,23 @@  typedef struct fs_cursor {
 extern void fs_cursor_initialise(char *__dir, uint __flags, fs_cursor_t *__cp);
 extern fs_path_t *fs_cursor_next_entry(fs_cursor_t *__cp);
 
+/* Path information. */
+
+struct path_list;
+struct path_component;
+
+struct path_component *path_component_init(const char *name);
+void path_component_free(struct path_component *pc);
+int path_component_change(struct path_component *pc, void *name,
+		size_t namelen);
+
+struct path_list *path_list_init(void);
+void path_list_free(struct path_list *path);
+void path_list_add_parent_component(struct path_list *path,
+		struct path_component *pc);
+void path_list_add_component(struct path_list *path, struct path_component *pc);
+void path_list_del_component(struct path_list *path, struct path_component *pc);
+
+ssize_t path_list_to_string(struct path_list *path, char *buf, size_t buflen);
+
 #endif	/* __PATH_H__ */
diff --git a/io/parent.c b/io/parent.c
index 55b8b49..ad51fe6 100644
--- a/io/parent.c
+++ b/io/parent.c
@@ -21,366 +21,105 @@ 
 #include "path.h"
 #include "parent.h"
 #include "handle.h"
-#include "jdm.h"
 #include "init.h"
 #include "io.h"
 
-#define PARENTBUF_SZ		16384
-#define BSTATBUF_SZ		16384
-
 static cmdinfo_t parent_cmd;
-static int verbose_flag;
-static int err_status;
-static __u64 inodes_checked;
 static char *mntpt;
 
-/*
- * check out a parent entry to see if the values seem valid
- */
-static void
-check_parent_entry(xfs_bstat_t *bstatp, parent_t *parent)
-{
-	int sts;
-	char fullpath[PATH_MAX];
-	struct stat statbuf;
-	char *str;
-
-	snprintf(fullpath, parent->p_reclen, _("%s%s"), mntpt,
-				((char*)parent)+sizeof(struct parent));
-
-	sts = lstat(fullpath, &statbuf);
-	if (sts != 0) {
-		fprintf(stderr,
-			_("inode-path for inode: %llu is incorrect - path \"%s\" non-existent\n"),
-			(unsigned long long) bstatp->bs_ino, fullpath);
-		if (verbose_flag) {
-			fprintf(stderr,
-				_("path \"%s\" does not stat for inode: %llu; err = %s\n"),
-				fullpath,
-			       (unsigned long long) bstatp->bs_ino,
-				strerror(errno));
-		}
-		err_status++;
-		return;
-	} else {
-		if (verbose_flag > 1) {
-			printf(_("path \"%s\" found\n"), fullpath);
-		}
-	}
-
-	if (statbuf.st_ino != bstatp->bs_ino) {
-		fprintf(stderr,
-			_("inode-path for inode: %llu is incorrect - wrong inode#\n"),
-		       (unsigned long long) bstatp->bs_ino);
-		if (verbose_flag) {
-			fprintf(stderr,
-				_("ino mismatch for path \"%s\" %llu vs %llu\n"),
-				fullpath,
-				(unsigned long long)statbuf.st_ino,
-				(unsigned long long)bstatp->bs_ino);
-		}
-		err_status++;
-		return;
-	} else if (verbose_flag > 1) {
-		printf(_("inode number match: %llu\n"),
-			(unsigned long long)statbuf.st_ino);
-	}
-
-	/* get parent path */
-	str = strrchr(fullpath, '/');
-	*str = '\0';
-	sts = stat(fullpath, &statbuf);
-	if (sts != 0) {
-		fprintf(stderr,
-			_("parent path \"%s\" does not stat: %s\n"),
-			fullpath,
-			strerror(errno));
-		err_status++;
-		return;
-	} else {
-		if (parent->p_ino != statbuf.st_ino) {
-			fprintf(stderr,
-				_("inode-path for inode: %llu is incorrect - wrong parent inode#\n"),
-			       (unsigned long long) bstatp->bs_ino);
-			if (verbose_flag) {
-				fprintf(stderr,
-					_("ino mismatch for path \"%s\" %llu vs %llu\n"),
-					fullpath,
-					(unsigned long long)parent->p_ino,
-					(unsigned long long)statbuf.st_ino);
-			}
-			err_status++;
-			return;
-		} else {
-			if (verbose_flag > 1) {
-			       printf(_("parent ino match for %llu\n"),
-				       (unsigned long long) parent->p_ino);
-			}
-		}
-	}
-}
-
-static void
-check_parents(parent_t *parentbuf, size_t *parentbuf_size,
-	     jdm_fshandle_t *fshandlep, xfs_bstat_t *statp)
-{
-	int error, i;
-	__u32 count;
-	parent_t *entryp;
-
-	do {
-		error = jdm_parentpaths(fshandlep, statp, parentbuf, *parentbuf_size, &count);
-
-		if (error == ERANGE) {
-			*parentbuf_size *= 2;
-			parentbuf = (parent_t *)realloc(parentbuf, *parentbuf_size);
-		} else if (error) {
-			fprintf(stderr, _("parentpaths failed for ino %llu: %s\n"),
-			       (unsigned long long) statp->bs_ino,
-				strerror(errno));
-			err_status++;
-			break;
-		}
-	} while (error == ERANGE);
-
-
-	if (count == 0) {
-		/* no links for inode - something wrong here */
-	       fprintf(stderr, _("inode-path for inode: %llu is missing\n"),
-			       (unsigned long long) statp->bs_ino);
-		err_status++;
-	}
-
-	entryp = parentbuf;
-	for (i = 0; i < count; i++) {
-		check_parent_entry(statp, entryp);
-		entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
-	}
-}
-
 static int
-do_bulkstat(parent_t *parentbuf, size_t *parentbuf_size, xfs_bstat_t *bstatbuf,
-	    int fsfd, jdm_fshandle_t *fshandlep)
+pptr_print(
+	struct xfs_pptr_info	*pi,
+	struct xfs_parent_ptr	*pptr,
+	void			*arg)
 {
-	__s32 buflenout;
-	__u64 lastino = 0;
-	xfs_bstat_t *p;
-	xfs_bstat_t *endp;
-	xfs_fsop_bulkreq_t bulkreq;
-	struct stat mntstat;
+	char			buf[XFS_PPTR_MAXNAMELEN + 1];
 
-	if (stat(mntpt, &mntstat)) {
-		fprintf(stderr, _("can't stat mount point \"%s\": %s\n"),
-			mntpt, strerror(errno));
-		return 1;
+	if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT) {
+		printf(_("Root directory.\n"));
+		return 0;
 	}
 
-	bulkreq.lastip  = &lastino;
-	bulkreq.icount  = BSTATBUF_SZ;
-	bulkreq.ubuffer = (void *)bstatbuf;
-	bulkreq.ocount  = &buflenout;
-
-	while (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT, &bulkreq) == 0) {
-		if (*(bulkreq.ocount) == 0) {
-			return 0;
-		}
-		for (p = bstatbuf, endp = bstatbuf + *bulkreq.ocount; p < endp; p++) {
-
-			/* inode being modified, get synced data with iget */
-			if ( (!p->bs_nlink || !p->bs_mode) && p->bs_ino != 0 ) {
-
-				if (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) < 0) {
-				    fprintf(stderr,
-					  _("failed to get bulkstat information for inode %llu\n"),
-					 (unsigned long long) p->bs_ino);
-				    continue;
-				}
-				if (!p->bs_nlink || !p->bs_mode || !p->bs_ino) {
-				    fprintf(stderr,
-					  _("failed to get valid bulkstat information for inode %llu\n"),
-					 (unsigned long long) p->bs_ino);
-				    continue;
-				}
-			}
-
-			/* skip root */
-			if (p->bs_ino == mntstat.st_ino) {
-				continue;
-			}
-
-			if (verbose_flag > 1) {
-			       printf(_("checking inode %llu\n"),
-				       (unsigned long long) p->bs_ino);
-			}
-
-			/* print dotted progress */
-			if ((inodes_checked % 100) == 0 && verbose_flag == 1) {
-				printf("."); fflush(stdout);
-			}
-			inodes_checked++;
-
-			check_parents(parentbuf, parentbuf_size, fshandlep, p);
-		}
-
-	}/*while*/
-
-	fprintf(stderr, _("syssgi bulkstat failed: %s\n"), strerror(errno));
-	return 1;
+	memcpy(buf, pptr->xpp_name, pptr->xpp_namelen);
+	buf[pptr->xpp_namelen] = 0;
+	printf(_("p_ino    = %llu\n"), (unsigned long long)pptr->xpp_ino);
+	printf(_("p_gen    = %u\n"), (unsigned int)pptr->xpp_gen);
+	printf(_("p_reclen = %u\n"), (unsigned int)pptr->xpp_namelen);
+	printf(_("p_name   = \"%s\"\n\n"), buf);
+	return 0;
 }
 
-static int
-parent_check(void)
+int
+print_parents(
+	struct xfs_handle	*handle)
 {
-	int fsfd;
-	jdm_fshandle_t *fshandlep;
-	parent_t *parentbuf;
-	size_t parentbuf_size = PARENTBUF_SZ;
-	xfs_bstat_t *bstatbuf;
-
-	err_status = 0;
-	inodes_checked = 0;
-
-	sync();
-
-        fsfd = file->fd;
-
-	fshandlep = jdm_getfshandle(mntpt);
-	if (fshandlep == NULL) {
-		fprintf(stderr, _("unable to open \"%s\" for jdm: %s\n"),
-		      mntpt,
-		      strerror(errno));
-		return 1;
-	}
-
-	/* allocate buffers */
-        bstatbuf = (xfs_bstat_t *)calloc(BSTATBUF_SZ, sizeof(xfs_bstat_t));
-	parentbuf = (parent_t *)malloc(parentbuf_size);
-	if (!bstatbuf || !parentbuf) {
-		fprintf(stderr, _("unable to allocate buffers: %s\n"),
-			strerror(errno));
-		err_status = 1;
-		goto out;
-	}
+	int			ret;
 
-	if (do_bulkstat(parentbuf, &parentbuf_size, bstatbuf, fsfd, fshandlep) != 0)
-		err_status++;
-
-	if (err_status > 0)
-		fprintf(stderr, _("num errors: %d\n"), err_status);
+	if (handle)
+		ret = handle_walk_pptrs(handle, sizeof(*handle), pptr_print,
+				NULL);
 	else
-		printf(_("succeeded checking %llu inodes\n"),
-			(unsigned long long) inodes_checked);
-
-out:
-	free(bstatbuf);
-	free(parentbuf);
-	free(fshandlep);
-	return err_status;
-}
+		ret = fd_walk_pptrs(file->fd, pptr_print, NULL);
+	if (ret)
+		perror(file->name);
 
-static void
-print_parent_entry(parent_t *parent, int fullpath)
-{
-       printf(_("p_ino    = %llu\n"),  (unsigned long long) parent->p_ino);
-	printf(_("p_gen    = %u\n"),	parent->p_gen);
-	printf(_("p_reclen = %u\n"),	parent->p_reclen);
-	if (fullpath)
-		printf(_("p_name   = \"%s%s\"\n"), mntpt,
-					((char*)parent)+sizeof(struct parent));
-	else
-		printf(_("p_name   = \"%s\"\n"),
-					((char*)parent)+sizeof(struct parent));
+	return 0;
 }
 
 static int
-parent_list(int fullpath)
-{
-	void *handlep = NULL;
-	size_t handlen;
-	int error, i;
-	int retval = 1;
-	__u32 count;
-	parent_t *entryp;
-	parent_t *parentbuf = NULL;
-	char *path = file->name;
-	int pb_size = PARENTBUF_SZ;
-
-	/* XXXX for linux libhandle version - to set libhandle fsfd cache */
-	{
-		void *fshandle;
-		size_t fshlen;
-
-		if (path_to_fshandle(mntpt, &fshandle, &fshlen) != 0) {
-			fprintf(stderr, _("%s: failed path_to_fshandle \"%s\": %s\n"),
-				progname, path, strerror(errno));
-			goto error;
-		}
-		free_handle(fshandle, fshlen);
-	}
-
-	if (path_to_handle(path, &handlep, &handlen) != 0) {
-		fprintf(stderr, _("%s: path_to_handle failed for \"%s\"\n"), progname, path);
-		goto error;
-	}
-
-	do {
-		parentbuf = (parent_t *)realloc(parentbuf, pb_size);
-		if (!parentbuf) {
-			fprintf(stderr, _("%s: unable to allocate parent buffer: %s\n"),
-				progname, strerror(errno));
-			goto error;
-		}
+path_print(
+	const char		*mntpt,
+	struct path_list	*path,
+	void			*arg) {
 
-		if (fullpath) {
-			error = parentpaths_by_handle(handlep,
-						       handlen,
-						       parentbuf,
-						       pb_size,
-						       &count);
-		} else {
-			error = parents_by_handle(handlep,
-						   handlen,
-						   parentbuf,
-						   pb_size,
-						   &count);
-		}
-		if (error == ERANGE) {
-			pb_size *= 2;
-		} else if (error) {
-			fprintf(stderr, _("%s: %s call failed for \"%s\": %s\n"),
-				progname, fullpath ? "parentpaths" : "parents",
-				path, strerror(errno));
-			goto error;
-		}
-	} while (error == ERANGE);
+	char			buf[PATH_MAX];
+	size_t			len = PATH_MAX;
+	int			ret;
 
-	if (count == 0) {
-		/* no links for inode - something wrong here */
-		fprintf(stderr, _("%s: inode-path is missing\n"), progname);
-		goto error;
+	ret = snprintf(buf, len, "%s", mntpt);
+	if (ret != strlen(mntpt)) {
+		errno = ENOMEM;
+		return -1;
 	}
 
-	entryp = parentbuf;
-	for (i = 0; i < count; i++) {
-		print_parent_entry(entryp, fullpath);
-		entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
-	}
+	ret = path_list_to_string(path, buf + ret, len - ret);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
 
-	retval = 0;
-error:
-	free(handlep);
-	free(parentbuf);
-	return retval;
+int
+print_paths(
+	struct xfs_handle	*handle)
+{
+	int			ret;
+
+	if (handle)
+		ret = handle_walk_ppaths(handle, sizeof(*handle), path_print,
+				NULL);
+ 	else
+		ret = fd_walk_ppaths(file->fd, path_print, NULL);
+	if (ret)
+		perror(file->name);
+	return 0;
 }
 
 int
-parent_f(int argc, char **argv)
+parent_f(
+	int			argc,
+	char			**argv)
 {
-	int c;
-	int listpath_flag = 0;
-	int check_flag = 0;
-	fs_path_t *fs;
-	static int tab_init;
+	struct xfs_handle	handle;
+	void			*hanp = NULL;
+	size_t			hlen;
+	struct fs_path		*fs;
+	char			*p;
+	uint64_t		ino = 0;
+	uint32_t		gen = 0;
+	int			c;
+	int			listpath_flag = 0;
+	int			ret;
+	static int		tab_init;
 
 	if (!tab_init) {
 		tab_init = 1;
@@ -394,46 +133,72 @@  parent_f(int argc, char **argv)
 	}
 	mntpt = fs->fs_dir;
 
-	verbose_flag = 0;
-
-	while ((c = getopt(argc, argv, "cpv")) != EOF) {
+	while ((c = getopt(argc, argv, "p")) != EOF) {
 		switch (c) {
-		case 'c':
-			check_flag = 1;
-			break;
 		case 'p':
 			listpath_flag = 1;
 			break;
-		case 'v':
-			verbose_flag++;
-			break;
 		default:
 			return command_usage(&parent_cmd);
 		}
 	}
 
-	if (!check_flag && !listpath_flag) /* default case */
-		exitcode = parent_list(listpath_flag);
-	else {
-		if (listpath_flag)
-			exitcode = parent_list(listpath_flag);
-		if (check_flag)
-			exitcode = parent_check();
+	/*
+	 * Always initialize the fshandle table because we need it for
+	 * the ppaths functions to work.
+	 */
+	ret = path_to_fshandle((char *)mntpt, &hanp, &hlen);
+	if (ret) {
+		perror(mntpt);
+		return 0;
+ 	}
+ 
+	if (optind + 2 == argc) {
+		ino = strtoull(argv[optind], &p, 0);
+		if (*p != '\0' || ino == 0) {
+			fprintf(stderr,
+				_("Bad inode number '%s'.\n"),
+				argv[optind]);
+			return 0;
+		}
+		gen = strtoul(argv[optind + 1], &p, 0);
+		if (*p != '\0') {
+			fprintf(stderr,
+				_("Bad generation number '%s'.\n"),
+				argv[optind + 1]);
+			return 0;
+		}
+
+		memcpy(&handle, hanp, sizeof(handle));
+		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
+				sizeof(handle.ha_fid.fid_len);
+		handle.ha_fid.fid_pad = 0;
+		handle.ha_fid.fid_ino = ino;
+		handle.ha_fid.fid_gen = gen;
+
 	}
 
+	if (listpath_flag)
+		exitcode = print_paths(ino ? &handle : NULL);
+	else
+		exitcode = print_parents(ino ? &handle : NULL);
+
+	if (hanp)
+		free_handle(hanp, hlen);
+
 	return 0;
 }
 
 static void
 parent_help(void)
 {
-	printf(_(
+printf(_(
 "\n"
 " list the current file's parents and their filenames\n"
 "\n"
-" -c -- check the current file's file system for parent consistency\n"
-" -p -- list the current file's parents and their full paths\n"
-" -v -- verbose mode\n"
+" -p -- list the current file's paths up to the root\n"
+"\n"
+"If ino and gen are supplied, use them instead.\n"
 "\n"));
 }
 
@@ -444,9 +209,9 @@  parent_init(void)
 	parent_cmd.cfunc = parent_f;
 	parent_cmd.argmin = 0;
 	parent_cmd.argmax = -1;
-	parent_cmd.args = _("[-cpv]");
+	parent_cmd.args = _("[-p] [ino gen]");
 	parent_cmd.flags = CMD_NOMAP_OK;
-	parent_cmd.oneline = _("print or check parent inodes");
+	parent_cmd.oneline = _("print parent inodes");
 	parent_cmd.help = parent_help;
 
 	if (expert)
diff --git a/libfrog/paths.c b/libfrog/paths.c
index c7895e9..9fb0140 100644
--- a/libfrog/paths.c
+++ b/libfrog/paths.c
@@ -27,6 +27,7 @@ 
 #include "path.h"
 #include "input.h"
 #include "project.h"
+#include "list.h"
 #include <limits.h>
 
 extern char *progname;
@@ -632,3 +633,138 @@  fs_table_insert_project_path(
 		exit(1);
 	}
 }
+
+
+/* Structured path components. */
+
+struct path_list {
+	struct list_head	p_head;
+};
+
+struct path_component {
+	struct list_head	pc_list;
+	char			*pc_fname;
+};
+
+/* Initialize a path component with a given name. */
+struct path_component *
+path_component_init(
+	const char		*name)
+{
+	struct path_component	*pc;
+
+	pc = malloc(sizeof(struct path_component));
+	if (!pc)
+		return NULL;
+	INIT_LIST_HEAD(&pc->pc_list);
+	pc->pc_fname = strdup(name);
+	if (!pc->pc_fname) {
+		free(pc);
+		return NULL;
+	}
+	return pc;
+}
+
+/* Free a path component. */
+void
+path_component_free(
+	struct path_component	*pc)
+{
+	free(pc->pc_fname);
+	free(pc);
+}
+
+/* Change a path component's filename. */
+int
+path_component_change(
+	struct path_component	*pc,
+	void			*name,
+	size_t			namelen)
+{
+	void			*p;
+
+	p = realloc(pc->pc_fname, namelen + 1);
+	if (!p)
+		return -1;
+	pc->pc_fname = p;
+	memcpy(pc->pc_fname, name, namelen);
+	pc->pc_fname[namelen] = 0;
+	return 0;
+}
+
+/* Initialize a pathname. */
+struct path_list *
+path_list_init(void)
+{
+	struct path_list	*path;
+
+	path = malloc(sizeof(struct path_list));
+	if (!path)
+		return NULL;
+	INIT_LIST_HEAD(&path->p_head);
+	return path;
+}
+
+/* Empty out a pathname. */
+void
+path_list_free(
+	struct path_list	*path)
+{
+	struct path_component	*pos;
+	struct path_component	*n;
+
+	list_for_each_entry_safe(pos, n, &path->p_head, pc_list) {
+		path_list_del_component(path, pos);
+		path_component_free(pos);
+	}
+	free(path);
+}
+
+/* Add a parent component to a pathname. */
+void
+path_list_add_parent_component(
+	struct path_list	*path,
+	struct path_component	*pc)
+{
+	list_add(&pc->pc_list, &path->p_head);
+}
+
+/* Add a component to a pathname. */
+void
+path_list_add_component(
+	struct path_list	*path,
+	struct path_component	*pc)
+{
+	list_add_tail(&pc->pc_list, &path->p_head);
+}
+
+/* Remove a component from a pathname. */
+void
+path_list_del_component(
+	struct path_list	*path,
+	struct path_component	*pc)
+{
+	list_del_init(&pc->pc_list);
+}
+
+/* Convert a pathname into a string. */
+ssize_t
+path_list_to_string(
+	struct path_list	*path,
+	char			*buf,
+	size_t			buflen)
+{
+	struct path_component	*pos;
+	ssize_t			bytes = 0;
+	int			ret;
+
+	list_for_each_entry(pos, &path->p_head, pc_list) {
+		ret = snprintf(buf, buflen, "/%s", pos->pc_fname);
+		if (ret != 1 + strlen(pos->pc_fname))
+			return -1;
+		bytes += ret;
+		buf += ret;
+		buflen -= ret;
+	}
+	return bytes;
+}
diff --git a/libhandle/Makefile b/libhandle/Makefile
index fe1a2af..d3cea41 100644
--- a/libhandle/Makefile
+++ b/libhandle/Makefile
@@ -16,7 +16,7 @@  else
 LTLDFLAGS += -Wl,--version-script,libhandle.sym
 endif
 
-CFILES = handle.c jdm.c
+CFILES = handle.c jdm.c parent.c
 LSRCFILES = libhandle.sym
 
 default: ltdepend $(LTLIBRARY)
diff --git a/libhandle/handle.c b/libhandle/handle.c
index 878d14d..a70fa32 100644
--- a/libhandle/handle.c
+++ b/libhandle/handle.c
@@ -41,7 +41,6 @@  typedef union {
 } comarg_t;
 
 static int obj_to_handle(char *, int, unsigned int, comarg_t, void**, size_t*);
-static int handle_to_fsfd(void *, char **);
 static char *path_to_fspath(char *path);
 
 
@@ -214,8 +213,10 @@  handle_to_fshandle(
 	return 0;
 }
 
-static int
-handle_to_fsfd(void *hanp, char **path)
+int
+handle_to_fsfd(
+	void		*hanp,
+	char		**path)
 {
 	struct fdhash	*fdhp;
 
diff --git a/libhandle/parent.c b/libhandle/parent.c
new file mode 100644
index 0000000..f6be3bd
--- /dev/null
+++ b/libhandle/parent.c
@@ -0,0 +1,325 @@ 
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "platform_defs.h"
+#include "xfs.h"
+#include "xfs_arch.h"
+#include "list.h"
+#include "path.h"
+#include "handle.h"
+#include "parent.h"
+
+/* Allocate a buffer large enough for some parent pointer records. */
+static inline struct xfs_pptr_info *
+xfs_pptr_alloc(
+      size_t                  nr_ptrs)
+{
+      struct xfs_pptr_info    *pi;
+
+      pi = malloc(XFS_PPTR_INFO_SIZEOF(nr_ptrs));
+      if (!pi)
+              return NULL;
+      memset(pi, 0, sizeof(struct xfs_pptr_info));
+      pi->pi_ptrs_size = nr_ptrs;
+      return pi;
+}
+
+/* Walk all parents of the given file handle. */
+static int
+handle_walk_parents(
+	int			fd,
+	struct xfs_handle	*handle,
+	walk_pptr_fn		fn,
+	void			*arg)
+{
+	struct xfs_pptr_info	*pi;
+	struct xfs_parent_ptr	*p;
+	unsigned int		i;
+	ssize_t			ret = -1;
+
+	pi = xfs_pptr_alloc(4);
+	if (!pi)
+		return -1;
+
+	if (handle) {
+		memcpy(&pi->pi_handle, handle, sizeof(struct xfs_handle));
+		pi->pi_flags = XFS_PPTR_IFLAG_HANDLE;
+	}
+
+	ret = ioctl(fd, XFS_IOC_GETPPOINTER, pi);
+	while (!ret) {
+		if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT) {
+			ret = fn(pi, NULL, arg);
+			break;
+		}
+		if (pi->pi_ptrs_used == 0)
+			break;
+		for (i = 0; i < pi->pi_ptrs_used; i++) {
+			p = XFS_PPINFO_TO_PP(pi, i);
+			ret = fn(pi, p, arg);
+			if (ret)
+				goto out_pi;
+		}
+		ret = ioctl(fd, XFS_IOC_GETPPOINTER, pi);
+	}
+
+out_pi:
+	free(pi);
+	return ret;
+}
+
+/* Walk all parent pointers of this handle. */
+int
+handle_walk_pptrs(
+	void			*hanp,
+	size_t			hlen,
+	walk_pptr_fn		fn,
+	void			*arg)
+{
+	char			*mntpt;
+	int			fd;
+
+	if (hlen != sizeof(struct xfs_handle)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	fd = handle_to_fsfd(hanp, &mntpt);
+	if (fd < 0)
+		return -1;
+
+	return handle_walk_parents(fd, hanp, fn, arg);
+}
+
+/* Walk all parent pointers of this fd. */
+int
+fd_walk_pptrs(
+	int			fd,
+	walk_pptr_fn		fn,
+	void			*arg)
+{
+	return handle_walk_parents(fd, NULL, fn, arg);
+}
+
+struct walk_ppaths_info {
+	walk_ppath_fn			fn;
+	void				*arg;
+	char				*mntpt;
+	struct path_list		*path;
+	int				fd;
+};
+
+struct walk_ppath_level_info {
+	struct xfs_handle		newhandle;
+	struct path_component		*pc;
+	struct walk_ppaths_info		*wpi;
+};
+
+static int handle_walk_parent_paths(struct walk_ppaths_info *wpi,
+		struct xfs_handle *handle);
+
+static int
+handle_walk_parent_path_ptr(
+	struct xfs_pptr_info		*pi,
+	struct xfs_parent_ptr		*p,
+	void				*arg)
+{
+	struct walk_ppath_level_info	*wpli = arg;
+	struct walk_ppaths_info		*wpi = wpli->wpi;
+	unsigned int			i;
+	int				ret = 0;
+
+	if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT)
+		return wpi->fn(wpi->mntpt, wpi->path, wpi->arg);
+
+	for (i = 0; i < pi->pi_ptrs_used; i++) {
+		p = XFS_PPINFO_TO_PP(pi, i);
+		ret = path_component_change(wpli->pc, p->xpp_name,
+				p->xpp_namelen);
+		if (ret)
+			break;
+		wpli->newhandle.ha_fid.fid_ino = p->xpp_ino;
+		wpli->newhandle.ha_fid.fid_gen = p->xpp_gen;
+		path_list_add_parent_component(wpi->path, wpli->pc);
+		ret = handle_walk_parent_paths(wpi, &wpli->newhandle);
+		path_list_del_component(wpi->path, wpli->pc);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+/*
+ * Recursively walk all parents of the given file handle; if we hit the
+ * fs root then we call the associated function with the constructed path.
+ */
+static int
+handle_walk_parent_paths(
+	struct walk_ppaths_info		*wpi,
+	struct xfs_handle		*handle)
+{
+	struct walk_ppath_level_info	*wpli;
+	int				ret;
+
+	wpli = malloc(sizeof(struct walk_ppath_level_info));
+	if (!wpli)
+		return -1;
+	wpli->pc = path_component_init("");
+	if (!wpli->pc) {
+		free(wpli);
+		return -1;
+	}
+	wpli->wpi = wpi;
+	memcpy(&wpli->newhandle, handle, sizeof(struct xfs_handle));
+
+	ret = handle_walk_parents(wpi->fd, handle, handle_walk_parent_path_ptr,
+			wpli);
+
+	path_component_free(wpli->pc);
+	free(wpli);
+	return ret;
+}
+
+/*
+ * Call the given function on all known paths from the vfs root to the inode
+ * described in the handle.
+ */
+int
+handle_walk_ppaths(
+	void			*hanp,
+	size_t			hlen,
+	walk_ppath_fn		fn,
+	void			*arg)
+{
+	struct walk_ppaths_info	wpi;
+	ssize_t			ret;
+
+	if (hlen != sizeof(struct xfs_handle)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	wpi.fd = handle_to_fsfd(hanp, &wpi.mntpt);
+	if (wpi.fd < 0)
+		return -1;
+	wpi.path = path_list_init();
+	if (!wpi.path)
+		return -1;
+	wpi.fn = fn;
+	wpi.arg = arg;
+
+	ret = handle_walk_parent_paths(&wpi, hanp);
+	path_list_free(wpi.path);
+
+	return ret;
+}
+
+/*
+ * Call the given function on all known paths from the vfs root to the inode
+ * referred to by the file description.
+ */
+int
+fd_walk_ppaths(
+	int			fd,
+	walk_ppath_fn		fn,
+	void			*arg)
+{
+	struct walk_ppaths_info	wpi;
+	void			*hanp;
+	size_t			hlen;
+	int			fsfd;
+	int			ret;
+
+	ret = fd_to_handle(fd, &hanp, &hlen);
+	if (ret)
+		return ret;
+
+	fsfd = handle_to_fsfd(hanp, &wpi.mntpt);
+	if (fsfd < 0)
+		return -1;
+	wpi.fd = fd;
+	wpi.path = path_list_init();
+	if (!wpi.path)
+		return -1;
+	wpi.fn = fn;
+	wpi.arg = arg;
+
+	ret = handle_walk_parent_paths(&wpi, hanp);
+	path_list_free(wpi.path);
+
+	return ret;
+}
+
+struct path_walk_info {
+	char			*buf;
+	size_t			len;
+};
+
+/* Helper that stringifies the first full path that we find. */
+static int
+handle_to_path_walk(
+	const char		*mntpt,
+	struct path_list	*path,
+	void			*arg)
+{
+	struct path_walk_info	*pwi = arg;
+	int			ret;
+
+	ret = snprintf(pwi->buf, pwi->len, "%s", mntpt);
+	if (ret != strlen(mntpt)) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	ret = path_list_to_string(path, pwi->buf + ret, pwi->len - ret);
+	if (ret < 0)
+		return ret;
+
+	return WALK_PPATHS_ABORT;
+}
+
+/* Return any eligible path to this file handle. */
+int
+handle_to_path(
+	void			*hanp,
+	size_t			hlen,
+	char			*path,
+	size_t			pathlen)
+{
+	struct path_walk_info	pwi;
+
+	pwi.buf = path;
+	pwi.len = pathlen;
+	return handle_walk_ppaths(hanp, hlen, handle_to_path_walk, &pwi);
+}
+
+/* Return any eligible path to this file description. */
+int
+fd_to_path(
+	int			fd,
+	char			*path,
+	size_t			pathlen)
+{
+	struct path_walk_info	pwi;
+
+	pwi.buf = path;
+	pwi.len = pathlen;
+	return fd_walk_ppaths(fd, handle_to_path_walk, &pwi);
+}
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index e3ce233..aa613f9 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -610,6 +610,14 @@  struct xfs_pptr_info {
 #define XFS_PPINFO_TO_PP(info, idx)    \
 	(&(((struct xfs_parent_ptr *)((char *)(info) + sizeof(*(info))))[(idx)]))
 
+#define XFS_PPTR_ALL_IFLAGS    (XFS_PPTR_IFLAG_HANDLE)
+
+/* partial results only */
+#define XFS_PPTR_OFLAG_PARTIAL (1U << 0)
+
+/* target was the root directory */
+#define XFS_PPTR_OFLAG_ROOT    (1U << 1)
+
 /*
  * ioctl limits
  */
diff --git a/scrub/inodes.c b/scrub/inodes.c
index ccfb9e0..3fbcd1a 100644
--- a/scrub/inodes.c
+++ b/scrub/inodes.c
@@ -31,6 +31,7 @@ 
 #include "xfs_scrub.h"
 #include "common.h"
 #include "inodes.h"
+#include "parent.h"
 
 /*
  * Iterate a range of inodes.
@@ -293,3 +294,28 @@  xfs_open_handle(
 	return open_by_fshandle(handle, sizeof(*handle),
 			O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY);
 }
+
+/* Construct a description for an inode. */
+void
+xfs_scrub_ino_descr(
+	struct scrub_ctx	*ctx,
+	struct xfs_handle	*handle,
+	char			*buf,
+	size_t			buflen)
+{
+	uint64_t		ino;
+	xfs_agnumber_t		agno;
+	xfs_agino_t		agino;
+	int			ret;
+
+	ret = handle_to_path(handle, sizeof(struct xfs_handle), buf, buflen);
+	if (ret >= 0)
+		return;
+
+	ino = handle->ha_fid.fid_ino;
+	agno = ino / (1ULL << (ctx->inopblog + ctx->agblklog));
+	agino = ino % (1ULL << (ctx->inopblog + ctx->agblklog));
+	snprintf(buf, buflen, _("inode %"PRIu64" (%u/%u)"), ino, agno,
+			agino);
+}
+
diff --git a/scrub/inodes.h b/scrub/inodes.h
index 693cb05..e94de0a 100644
--- a/scrub/inodes.h
+++ b/scrub/inodes.h
@@ -28,5 +28,7 @@  bool xfs_scan_all_inodes(struct scrub_ctx *ctx, xfs_inode_iter_fn fn,
 		void *arg);
 
 int xfs_open_handle(struct xfs_handle *handle);
+void xfs_scrub_ino_descr(struct scrub_ctx *ctx, struct xfs_handle *handle,
+		char *buf, size_t buflen);
 
 #endif /* XFS_SCRUB_INODES_H_ */
diff --git a/scrub/phase5.c b/scrub/phase5.c
index 01038f7..ecaaaaa 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -245,16 +245,11 @@  xfs_scrub_connections(
 	void			*arg)
 {
 	bool			*pmoveon = arg;
-	char			descr[DESCR_BUFSZ];
+	char			descr[PATH_MAX];
 	bool			moveon = true;
-	xfs_agnumber_t		agno;
-	xfs_agino_t		agino;
 	int			fd = -1;
 
-	agno = bstat->bs_ino / (1ULL << (ctx->inopblog + ctx->agblklog));
-	agino = bstat->bs_ino % (1ULL << (ctx->inopblog + ctx->agblklog));
-	snprintf(descr, DESCR_BUFSZ, _("inode %"PRIu64" (%u/%u)"),
-			(uint64_t)bstat->bs_ino, agno, agino);
+	xfs_scrub_ino_descr(ctx, handle, descr, PATH_MAX);
 	background_sleep();
 
 	/* Warn about naming problems in xattrs. */