* [RFCRAP DONOTMERGE PATCH 0/2] xfs: parent pointers
@ 2017-12-08 3:54 Darrick J. Wong
2017-12-08 4:02 ` [RFCRAP DONOTMERGE PATCH 1/2] xfs: sketchy implementation of " Darrick J. Wong
2017-12-08 4:03 ` [RFCRAP DONOTMERGE PATCH 2/2] xfsprogs: implement the upper half " Darrick J. Wong
0 siblings, 2 replies; 4+ messages in thread
From: Darrick J. Wong @ 2017-12-08 3:54 UTC (permalink / raw)
To: Allison Henderson; +Cc: xfs
Hi all,
So I've been working in my spare time (ha ha) to build a prototype
parent pointer ioctl backend so that I can play around with the
userspace side of things -- namely (cough) having xfs_scrub be able to
resolve an (ino, gen) tuple back to a user-friendly file path.
The first ugly patch attacks the dentry cache to use d_parent to provide
one of the parents of some file. Obviously, this is totally deficient
as we can only report one parent and the file has to have been opened
via path at some previous point in time, and I bet the locking is
totally wrong. It's too ugly to live but it /does/ function as a stub.
The second patch implements two iterators atop the parent pointer ioctl
we've been tossing around on the mailing list -- one to return parents
of an open file/file handle, and a second one to return all the paths of
an open file/file handle, and plunges it into (part of) xfs_scrub and
the xfs_io parent command. Note that I'm expecting xfs_scrub/xfs_repair
to take over parent pointer checking and fixing, so I removed the
checker function from xfs_io.
This is _not_ a substitute for Allison's parent pointer patches, as
they provide the meat of the real feature... but seeing how much
bikeshedding ioctls invite, we might as well start the reconciliation
process now.
--D
^ permalink raw reply [flat|nested] 4+ messages in thread
* [RFCRAP DONOTMERGE PATCH 1/2] xfs: sketchy implementation of parent pointers
2017-12-08 3:54 [RFCRAP DONOTMERGE PATCH 0/2] xfs: parent pointers Darrick J. Wong
@ 2017-12-08 4:02 ` Darrick J. Wong
2017-12-08 7:52 ` Amir Goldstein
2017-12-08 4:03 ` [RFCRAP DONOTMERGE PATCH 2/2] xfsprogs: implement the upper half " Darrick J. Wong
1 sibling, 1 reply; 4+ messages in thread
From: Darrick J. Wong @ 2017-12-08 4:02 UTC (permalink / raw)
To: Allison Henderson; +Cc: xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
This is a, um, "sample" implementation of parent pointers that abuses
struct dentry to return one parent pointer of a file ... if the dentry
cache has been connected. This exists only to enable testing of the
userspace xfs_scrub bits and is probably too ugly to live. Wait for the
real implementation.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/Makefile | 1
fs/xfs/libxfs/xfs_fs.h | 57 +++++++++++++
fs/xfs/xfs_ioctl.c | 49 +++++++++++
fs/xfs/xfs_parent.c | 214 ++++++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_parent.h | 29 +++++++
5 files changed, 350 insertions(+)
create mode 100644 fs/xfs/xfs_parent.c
create mode 100644 fs/xfs/xfs_parent.h
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index e1768e7..9f4de14 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -99,6 +99,7 @@ xfs-y += xfs_aops.o \
xfs_message.o \
xfs_mount.o \
xfs_mru_cache.o \
+ xfs_parent.o \
xfs_reflink.o \
xfs_stats.o \
xfs_super.o \
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index fc4386a..f0ff964 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -545,6 +545,62 @@ struct xfs_scrub_metadata {
XFS_SCRUB_OFLAG_WARNING)
#define XFS_SCRUB_FLAGS_ALL (XFS_SCRUB_FLAGS_IN | XFS_SCRUB_FLAGS_OUT)
+/* Parent Pointers */
+#define XFS_PPTR_NAME_MAX 256
+
+struct xfs_pptr {
+ /* Parent inode number. */
+ __u64 pp_ino;
+ __u64 pp_reserved[2];
+
+ /* Parent generation number. */
+ __u32 pp_gen;
+
+ /* Name in the parent. */
+ __u32 pp_namelen;
+ __u8 pp_name[XFS_PPTR_NAME_MAX];
+};
+
+/* return parents of the handle, not the open fd */
+#define XFS_PPTR_IFLAG_HANDLE (1U << 0)
+
+#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)
+
+struct xfs_pptr_info {
+ /* i: (optional) file handle. */
+ struct xfs_handle pi_handle;
+
+ /* i/o: xattr lookup cursor, if necessary */
+ struct xfs_attrlist_cursor pi_cursor;
+
+ /* i: input flags */
+ __u32 pi_iflags;
+
+ /* o: output flags */
+ __u32 pi_oflags;
+
+ /* i: number of pointers we have space for. */
+ __u32 pi_ptrs_size;
+
+ /* o: number of pointers actually returned. */
+ __u32 pi_ptrs_used;
+
+ /* must be zero */
+ __u64 pi_reserved[6];
+
+ /* o: pointer info, must come last */
+ struct xfs_pptr pi_ptrs[0];
+};
+
+#define XFS_PPTR_INFO_SIZEOF(nr_ptrs) (sizeof(struct xfs_pptr_info) + \
+ ((nr_ptrs) * sizeof(struct xfs_pptr)))
+
/*
* ioctl limits
*/
@@ -589,6 +645,7 @@ struct xfs_scrub_metadata {
#define XFS_IOC_FREE_EOFBLOCKS _IOR ('X', 58, struct xfs_fs_eofblocks)
/* XFS_IOC_GETFSMAP ------ hoisted 59 */
#define XFS_IOC_SCRUB_METADATA _IOWR('X', 60, struct xfs_scrub_metadata)
+#define XFS_IOC_GET_PPTR _IOWR('X', 61, struct xfs_pptr_info)
/*
* ioctl commands that replace IRIX syssgi()'s
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 20dc65f..916b060 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -45,6 +45,7 @@
#include <linux/fsmap.h>
#include "xfs_fsmap.h"
#include "scrub/xfs_scrub.h"
+#include "xfs_parent.h"
#include <linux/capability.h>
#include <linux/cred.h>
@@ -1730,6 +1731,51 @@ xfs_ioc_scrub_metadata(
return 0;
}
+struct getpptr_info {
+ struct xfs_pptr_info __user *data;
+ unsigned int idx;
+};
+
+STATIC int
+xfs_getpptr_format(
+ struct xfs_pptr *pp,
+ void *arg)
+{
+ struct getpptr_info *info = arg;
+
+ if (copy_to_user(&info->data->pi_ptrs[info->idx++], pp,
+ sizeof(struct xfs_pptr)))
+ return -EFAULT;
+ return 0;
+}
+
+STATIC int
+xfs_ioc_get_parent_pointers(
+ struct file *filp,
+ void __user *arg)
+{
+ struct getpptr_info info = { NULL };
+ struct xfs_pptr_info pi;
+ int error;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&pi, arg, sizeof(pi)))
+ return -EFAULT;
+
+ info.data = arg;
+ error = xfs_parent_get_pointers(filp, &pi, xfs_getpptr_format, &info);
+ if (error)
+ return error;
+
+ pi.pi_ptrs_used = info.idx;
+ if (copy_to_user(arg, &pi, sizeof(pi)))
+ return -EFAULT;
+
+ return 0;
+}
+
int
xfs_ioc_swapext(
xfs_swapext_t *sxp)
@@ -1914,6 +1960,9 @@ xfs_file_ioctl(
case XFS_IOC_SCRUB_METADATA:
return xfs_ioc_scrub_metadata(ip, arg);
+ case XFS_IOC_GET_PPTR:
+ return xfs_ioc_get_parent_pointers(filp, arg);
+
case XFS_IOC_FD_TO_HANDLE:
case XFS_IOC_PATH_TO_HANDLE:
case XFS_IOC_PATH_TO_FSHANDLE: {
diff --git a/fs/xfs/xfs_parent.c b/fs/xfs/xfs_parent.c
new file mode 100644
index 0000000..91b07e0
--- /dev/null
+++ b/fs/xfs/xfs_parent.c
@@ -0,0 +1,214 @@
+/*
+ * 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 "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_btree.h"
+#include "xfs_bit.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_inode.h"
+#include "xfs_icache.h"
+#include "xfs_itable.h"
+#include "xfs_export.h"
+#include "xfs_parent.h"
+
+struct xfs_pptr_buf {
+ uint32_t idx;
+ struct xfs_pptr recs[0];
+};
+
+#define XFS_PARENT_ITER_ABORT (1)
+
+/* Package up a pptr structure and emit it to the caller. */
+static int
+xfs_parent_emit(
+ struct xfs_pptr_info *pi,
+ struct xfs_pptr_buf *pb,
+ uint64_t ino,
+ uint32_t gen,
+ uint8_t namelen,
+ const char *name)
+{
+ struct xfs_pptr *pptr;
+
+ if (pb->idx >= pi->pi_ptrs_size)
+ return XFS_PARENT_ITER_ABORT;
+
+ pptr = &pb->recs[pb->idx++];
+ pptr->pp_ino = ino;
+ pptr->pp_gen = gen;
+ pptr->pp_namelen = namelen;
+ memcpy(pptr->pp_name, name, namelen);
+
+ return 0;
+}
+
+/*
+ * No need to do permission checks on the various pathname components
+ * as the handle operations are privileged.
+ */
+STATIC int
+xfs_parent_handle_acceptable(
+ void *context,
+ struct dentry *dentry)
+{
+ return 1;
+}
+
+/* Get parent info for an inode by walking the parent dentry. */
+static int
+xfs_parent_get_dparent(
+ struct file *filp,
+ struct xfs_pptr_info *pi,
+ struct xfs_pptr_buf *pb)
+{
+ struct xfs_inode *ip;
+ struct inode *dir_inode;
+ struct dentry *dentry;
+ struct dentry *dparent;
+ unsigned int ilock;
+ int error = 0;
+
+ pi->pi_oflags |= XFS_PPTR_OFLAG_PARTIAL;
+
+ /* Any nonzero byte in the cursor means we've already retrieved one. */
+ if (memchr_inv(&pi->pi_cursor, 0, sizeof(pi->pi_cursor)))
+ return 0;
+ memset(&pi->pi_cursor, 0xFF, sizeof(pi->pi_cursor));
+
+ if (pi->pi_iflags & XFS_PPTR_IFLAG_HANDLE) {
+ struct xfs_handle *handle = &pi->pi_handle;
+ struct xfs_fid64 fid = { 0 };
+
+ /* Extract the desired file from the handle info. */
+ if (sizeof(handle->ha_fid) - sizeof(handle->ha_fid.fid_len) !=
+ handle->ha_fid.fid_len)
+ return -EINVAL;
+
+ fid.ino = handle->ha_fid.fid_ino;
+ fid.gen = handle->ha_fid.fid_gen;
+
+ dentry = exportfs_decode_fh(filp->f_path.mnt,
+ (struct fid *)&fid, 3,
+ FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG,
+ xfs_parent_handle_acceptable, NULL);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+ } else {
+ /* Or just use the dentry of the open file. */
+ dentry = dget(file_dentry(filp));
+ }
+
+ /* Lock XFS inode... */
+ ip = XFS_I(d_inode(dentry));
+ ilock = XFS_IOLOCK_SHARED | XFS_MMAPLOCK_SHARED | XFS_ILOCK_SHARED;
+ xfs_ilock(ip, ilock);
+
+ /* Bail out early for the root dir. */
+ if (ip->i_ino == ip->i_mount->m_sb.sb_rootino) {
+ pi->pi_oflags |= XFS_PPTR_OFLAG_ROOT;
+ goto out_dentry;
+ }
+
+ /* Filter out the unconnected inodes. */
+ if (d_unlinked(dentry) || (dentry->d_flags & DCACHE_DISCONNECTED))
+ goto out_dentry;
+ if (IS_ROOT(dentry)) {
+ pi->pi_oflags |= XFS_PPTR_OFLAG_ROOT;
+ goto out_dentry;
+ }
+
+ /* Otherwise look up the parent... */
+ dparent = dget_parent(dentry);
+ if (IS_ERR(dparent)) {
+ error = PTR_ERR(dparent);
+ goto out_dentry;
+ }
+
+ dir_inode = d_inode(dparent);
+ if (!dir_inode)
+ goto out_dparent;
+
+ /* ...and emit a record. */
+ error = xfs_parent_emit(pi, pb, dir_inode->i_ino,
+ dir_inode->i_generation,
+ ACCESS_ONCE(dentry->d_name.len),
+ ACCESS_ONCE(dentry->d_name.name));
+ if (error == XFS_PARENT_ITER_ABORT)
+ error = 0;
+
+out_dparent:
+ dput(dparent);
+out_dentry:
+ xfs_iunlock(ip, ilock);
+ dput(dentry);
+ return error;
+}
+
+/* Walk all the parent pointers of a file. */
+int
+xfs_parent_get_pointers(
+ struct file *filp,
+ struct xfs_pptr_info *pi,
+ xfs_parent_format_t formatter,
+ void *arg)
+{
+ struct xfs_pptr_buf *pb;
+ size_t sz;
+ unsigned int i;
+ int error;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (pi->pi_iflags & ~XFS_PPTR_ALL_IFLAGS)
+ return -EINVAL;
+ if (memchr_inv(pi->pi_reserved, 0, sizeof(pi->pi_reserved)))
+ return -EINVAL;
+
+ /*
+ * Allocate temporary buffer to hold pptr records while we have
+ * the inode locked.
+ */
+ sz = sizeof(struct xfs_pptr_buf) +
+ (pi->pi_ptrs_size * sizeof(struct xfs_pptr));
+ if (sz > PAGE_SIZE * 4)
+ return -ENOMEM;
+ pb = kmem_zalloc_large(sz, KM_SLEEP | KM_MAYFAIL);
+ if (!pb)
+ return -ENOMEM;
+
+ /* Record parent pointer information in buffer. */
+ error = xfs_parent_get_dparent(filp, pi, pb);
+ if (error)
+ goto out;
+
+ /* Format records to userspace. */
+ for (i = 0; error == 0 && i < pb->idx; i++)
+ error = formatter(&pb->recs[i], arg);
+
+out:
+ kmem_free(pb);
+ return error;
+}
diff --git a/fs/xfs/xfs_parent.h b/fs/xfs/xfs_parent.h
new file mode 100644
index 0000000..23ee505
--- /dev/null
+++ b/fs/xfs/xfs_parent.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+#ifndef __XFS_PARENT_H__
+#define __XFS_PARENT_H__
+
+/* pptr to userspace formatter - copy to user & advance pointer */
+typedef int (*xfs_parent_format_t)(struct xfs_pptr *, void *);
+
+int xfs_parent_get_pointers(struct file *filp, struct xfs_pptr_info *pi,
+ xfs_parent_format_t formatter, void *priv);
+
+#endif /* __XFS_PARENT_H__ */
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [RFCRAP DONOTMERGE PATCH 2/2] xfsprogs: implement the upper half of parent pointers
2017-12-08 3:54 [RFCRAP DONOTMERGE PATCH 0/2] xfs: parent pointers Darrick J. Wong
2017-12-08 4:02 ` [RFCRAP DONOTMERGE PATCH 1/2] xfs: sketchy implementation of " Darrick J. Wong
@ 2017-12-08 4:03 ` Darrick J. Wong
1 sibling, 0 replies; 4+ messages in thread
From: Darrick J. Wong @ 2017-12-08 4:03 UTC (permalink / raw)
To: Allison Henderson; +Cc: xfs
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).
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fsr/Makefile | 5 -
include/handle.h | 1
include/parent.h | 20 ++
include/path.h | 19 ++
io/parent.c | 459 +++++++++++++---------------------------------------
libfrog/paths.c | 135 +++++++++++++++
libhandle/Makefile | 2
libhandle/handle.c | 7 -
libhandle/parent.c | 324 +++++++++++++++++++++++++++++++++++++
libxfs/xfs_fs.h | 57 ++++++
scrub/inodes.c | 25 +++
scrub/inodes.h | 2
scrub/phase5.c | 9 -
13 files changed, 708 insertions(+), 357 deletions(-)
create mode 100644 libhandle/parent.c
diff --git a/fsr/Makefile b/fsr/Makefile
index d3521b2..e5baf02 100644
--- a/fsr/Makefile
+++ b/fsr/Makefile
@@ -7,7 +7,10 @@ include $(TOPDIR)/include/builddefs
LTCOMMAND = xfs_fsr
CFILES = xfs_fsr.c
-LLDLIBS = $(LIBHANDLE)
+
+LLDLIBS += $(LIBHANDLE) $(LIBFROG) $(LIBPTHREAD)
+LTDEPENDENCIES += $(LIBHANDLE) $(LIBFROG)
+LLDFLAGS = -static
ifeq ($(HAVE_GETMNTENT),yes)
LCFLAGS += -DHAVE_GETMNTENT
diff --git a/include/handle.h b/include/handle.h
index 49f1441..cae0e99 100644
--- a/include/handle.h
+++ b/include/handle.h
@@ -51,6 +51,7 @@ extern int fssetdm_by_handle (void *__hanp, size_t __hlen,
struct fsdmidata *__fsdmi);
void fshandle_destroy(void);
+int handle_to_fsfd(void *hanp, char **path);
#ifdef __cplusplus
}
diff --git a/include/parent.h b/include/parent.h
index f338f96..9757fb6 100644
--- a/include/parent.h
+++ b/include/parent.h
@@ -16,7 +16,25 @@
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __PARENT_H__
-#define __PARENT_H__
+#define __PARENT_H__
+
+struct path_list;
+
+typedef int (*walk_pptr_fn)(struct xfs_pptr_info *pi, struct xfs_pptr *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);
typedef struct parent {
__u64 p_ino;
diff --git a/include/path.h b/include/path.h
index 1d3a902..9b0c8a3 100644
--- a/include/path.h
+++ b/include/path.h
@@ -69,4 +69,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 1968516..3adbc0d 100644
--- a/io/parent.c
+++ b/io/parent.c
@@ -21,363 +21,108 @@
#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)
+static int
+pptr_print(
+ struct xfs_pptr_info *pi,
+ struct xfs_pptr *pptr,
+ void *arg)
{
- int sts;
- char fullpath[PATH_MAX];
- struct stat statbuf;
- char *str;
-
- sprintf(fullpath, _("%s%s"), mntpt, parent->p_name);
+ char buf[XFS_PPTR_NAME_MAX + 1];
- 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);
+ if (pi->pi_oflags & XFS_PPTR_OFLAG_ROOT) {
+ printf(_("Root directory.\n"));
+ return 0;
}
- /* 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);
- }
- }
- }
+ memcpy(buf, pptr->pp_name, pptr->pp_namelen);
+ buf[pptr->pp_namelen] = 0;
+ printf(_("pp_ino = %llu\n"), (unsigned long long)pptr->pp_ino);
+ printf(_("pp_gen = %u\n"), (unsigned int)pptr->pp_gen);
+ printf(_("pp_name = \"%s\"\n"), buf);
+ return 0;
}
-static void
-check_parents(parent_t *parentbuf, size_t *parentbuf_size,
- jdm_fshandle_t *fshandlep, xfs_bstat_t *statp)
+int
+print_parents(
+ struct xfs_handle *handle)
{
- 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);
-
+ int ret;
- 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++;
- }
+ if (handle)
+ ret = handle_walk_pptrs(handle, sizeof(*handle), pptr_print,
+ NULL);
+ else
+ ret = fd_walk_pptrs(file->fd, pptr_print, NULL);
+ if (ret)
+ perror(file->name);
- entryp = parentbuf;
- for (i = 0; i < count; i++) {
- check_parent_entry(statp, entryp);
- entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
- }
+ return 0;
}
static int
-do_bulkstat(parent_t *parentbuf, size_t *parentbuf_size, xfs_bstat_t *bstatbuf,
- int fsfd, jdm_fshandle_t *fshandlep)
+path_print(
+ const char *mntpt,
+ struct path_list *path,
+ 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[PATH_MAX];
+ size_t len = PATH_MAX;
+ int ret;
- if (stat(mntpt, &mntstat)) {
- fprintf(stderr, _("can't stat mount point \"%s\": %s\n"),
- mntpt, strerror(errno));
- return 1;
+ ret = snprintf(buf, len, "%s", mntpt);
+ if (ret != strlen(mntpt)) {
+ errno = ENOMEM;
+ return -1;
}
- 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);
- }
+ ret = path_list_to_string(path, buf + ret, len - ret);
+ if (ret < 0)
+ return ret;
- /* print dotted progress */
- if ((inodes_checked % 100) == 0 && verbose_flag == 1) {
- printf("."); fflush(stdout);
- }
- inodes_checked++;
+ printf("%s\n", buf);
- check_parents(parentbuf, parentbuf_size, fshandlep, p);
- }
-
- }/*while*/
-
- fprintf(stderr, _("syssgi bulkstat failed: %s\n"), strerror(errno));
- return 1;
+ return 0;
}
-static int
-parent_check(void)
+int
+print_paths(
+ 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;
- }
-
- if (do_bulkstat(parentbuf, &parentbuf_size, bstatbuf, fsfd, fshandlep) != 0)
- err_status++;
-
- if (err_status > 0)
- fprintf(stderr, _("num errors: %d\n"), err_status);
- else
- printf(_("succeeded checking %llu inodes\n"),
- (unsigned long long) inodes_checked);
+ int ret;
-out:
- free(bstatbuf);
- free(parentbuf);
- free(fshandlep);
- return err_status;
-}
-
-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, parent->p_name);
+ if (handle)
+ ret = handle_walk_ppaths(handle, sizeof(*handle), path_print,
+ NULL);
else
- printf(_("p_name = \"%s\"\n"), parent->p_name);
-}
-
-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);
- }
+ ret = fd_walk_ppaths(file->fd, path_print, NULL);
+ if (ret)
+ perror(file->name);
- 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;
- }
-
- 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);
-
- if (count == 0) {
- /* no links for inode - something wrong here */
- fprintf(stderr, _("%s: inode-path is missing\n"), progname);
- goto error;
- }
-
- entryp = parentbuf;
- for (i = 0; i < count; i++) {
- print_parent_entry(entryp, fullpath);
- entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
- }
-
- retval = 0;
-error:
- free(handlep);
- free(parentbuf);
- return retval;
+ 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;
@@ -391,33 +136,59 @@ 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(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;
}
@@ -428,9 +199,9 @@ parent_help(void)
"\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"));
}
@@ -441,9 +212,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 62b4eda..62a0af3 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;
@@ -604,3 +605,137 @@ 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..6d0f50e
--- /dev/null
+++ b/libhandle/parent.c
@@ -0,0 +1,324 @@
+/*
+ * 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_pptr *p;
+ unsigned int i;
+ ssize_t ret = -1;
+
+ pi = xfs_pptr_alloc(4);
+ if (!pi)
+ return -1;
+
+ p = pi->pi_ptrs;
+ if (handle) {
+ memcpy(&pi->pi_handle, handle, sizeof(struct xfs_handle));
+ pi->pi_iflags = XFS_PPTR_IFLAG_HANDLE;
+ }
+
+ ret = ioctl(fd, XFS_IOC_GET_PPTR, pi);
+ while (!ret) {
+ if (pi->pi_oflags & XFS_PPTR_OFLAG_ROOT) {
+ ret = fn(pi, NULL, arg);
+ break;
+ }
+ if (pi->pi_ptrs_used == 0)
+ break;
+ for (i = 0, p = pi->pi_ptrs; i < pi->pi_ptrs_used; i++, p++) {
+ ret = fn(pi, p, arg);
+ if (ret)
+ goto out_pi;
+ }
+ ret = ioctl(fd, XFS_IOC_GET_PPTR, 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_pptr *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_oflags & XFS_PPTR_OFLAG_ROOT)
+ return wpi->fn(wpi->mntpt, wpi->path, wpi->arg);
+
+ for (i = 0, p = pi->pi_ptrs; i < pi->pi_ptrs_used; i++, p++) {
+ ret = path_component_change(wpli->pc, p->pp_name,
+ p->pp_namelen);
+ if (ret)
+ break;
+ wpli->newhandle.ha_fid.fid_ino = p->pp_ino;
+ wpli->newhandle.ha_fid.fid_gen = p->pp_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 319b2a6..f555360 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -566,6 +566,62 @@ struct xfs_scrub_metadata {
XFS_SCRUB_OFLAG_WARNING)
#define XFS_SCRUB_FLAGS_ALL (XFS_SCRUB_FLAGS_IN | XFS_SCRUB_FLAGS_OUT)
+/* Parent Pointers */
+#define XFS_PPTR_NAME_MAX 256
+
+struct xfs_pptr {
+ /* Parent inode number. */
+ __u64 pp_ino;
+ __u64 pp_reserved[2];
+
+ /* Parent generation number. */
+ __u32 pp_gen;
+
+ /* Name in the parent. */
+ __u32 pp_namelen;
+ __u8 pp_name[XFS_PPTR_NAME_MAX];
+};
+
+/* return parents of the handle, not the open fd */
+#define XFS_PPTR_IFLAG_HANDLE (1U << 0)
+
+#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)
+
+struct xfs_pptr_info {
+ /* i: (optional) file handle. */
+ struct xfs_handle pi_handle;
+
+ /* i/o: xattr lookup cursor, if necessary */
+ struct xfs_attrlist_cursor pi_cursor;
+
+ /* i: input flags */
+ __u32 pi_iflags;
+
+ /* o: output flags */
+ __u32 pi_oflags;
+
+ /* i: number of pointers we have space for. */
+ __u32 pi_ptrs_size;
+
+ /* o: number of pointers actually returned. */
+ __u32 pi_ptrs_used;
+
+ /* must be zero */
+ __u64 pi_reserved[6];
+
+ /* o: pointer info, must come last */
+ struct xfs_pptr pi_ptrs[];
+};
+
+#define XFS_PPTR_INFO_SIZEOF(nr_ptrs) (sizeof(struct xfs_pptr_info) + \
+ ((nr_ptrs) * sizeof(struct xfs_pptr)))
+
/*
* ioctl limits
*/
@@ -609,6 +665,7 @@ struct xfs_scrub_metadata {
#define XFS_IOC_FREE_EOFBLOCKS _IOR ('X', 58, struct xfs_fs_eofblocks)
/* XFS_IOC_GETFSMAP ------ hoisted 59 */
#define XFS_IOC_SCRUB_METADATA _IOWR('X', 60, struct xfs_scrub_metadata)
+#define XFS_IOC_GET_PPTR _IOWR('X', 61, struct xfs_pptr_info)
/*
* ioctl commands that replace IRIX syssgi()'s
diff --git a/scrub/inodes.c b/scrub/inodes.c
index c880c36..076ea6f 100644
--- a/scrub/inodes.c
+++ b/scrub/inodes.c
@@ -33,6 +33,7 @@
#include "xfs_scrub.h"
#include "common.h"
#include "inodes.h"
+#include "parent.h"
/*
* Iterate a range of inodes.
@@ -282,3 +283,27 @@ 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 c398677..912985e 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 72b7cda..ba2e412 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -253,16 +253,11 @@ xfs_scrub_connections(
void *arg)
{
bool *pmoveon = arg;
- char descr[DESCR_BUFSZ];
+ char descr[PATH_MAX];
bool moveon;
- 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. */
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [RFCRAP DONOTMERGE PATCH 1/2] xfs: sketchy implementation of parent pointers
2017-12-08 4:02 ` [RFCRAP DONOTMERGE PATCH 1/2] xfs: sketchy implementation of " Darrick J. Wong
@ 2017-12-08 7:52 ` Amir Goldstein
0 siblings, 0 replies; 4+ messages in thread
From: Amir Goldstein @ 2017-12-08 7:52 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: Allison Henderson, xfs
On Fri, Dec 8, 2017 at 6:02 AM, Darrick J. Wong <darrick.wong@oracle.com> wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> This is a, um, "sample" implementation of parent pointers that abuses
> struct dentry to return one parent pointer of a file ... if the dentry
> cache has been connected.
Note that your sample implementation will return parent pointer of a
directory even if it wasn't already in dcache.
> This exists only to enable testing of the
> userspace xfs_scrub bits and is probably too ugly to live. Wait for the
> real implementation.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
[...]
> +/*
> + * No need to do permission checks on the various pathname components
> + * as the handle operations are privileged.
> + */
> +STATIC int
> +xfs_parent_handle_acceptable(
> + void *context,
> + struct dentry *dentry)
> +{
> + return 1;
For the sake of fun, assuming that you want to gear up your sample to return
all parents, the acceptable callback is an iterator on all aliases of
inode in dcache.
So if you pass in your parent iterator context to this iterator, you
can emit all parents
from this callback. Just return 0 to keep iterating. You won't get a dentry back
from exportfs_decode_fh(), but you won't need it.
> +}
> +
> +/* Get parent info for an inode by walking the parent dentry. */
> +static int
> +xfs_parent_get_dparent(
> + struct file *filp,
> + struct xfs_pptr_info *pi,
> + struct xfs_pptr_buf *pb)
> +{
> + struct xfs_inode *ip;
> + struct inode *dir_inode;
> + struct dentry *dentry;
> + struct dentry *dparent;
> + unsigned int ilock;
> + int error = 0;
> +
> + pi->pi_oflags |= XFS_PPTR_OFLAG_PARTIAL;
> +
> + /* Any nonzero byte in the cursor means we've already retrieved one. */
> + if (memchr_inv(&pi->pi_cursor, 0, sizeof(pi->pi_cursor)))
> + return 0;
> + memset(&pi->pi_cursor, 0xFF, sizeof(pi->pi_cursor));
> +
> + if (pi->pi_iflags & XFS_PPTR_IFLAG_HANDLE) {
> + struct xfs_handle *handle = &pi->pi_handle;
> + struct xfs_fid64 fid = { 0 };
> +
> + /* Extract the desired file from the handle info. */
> + if (sizeof(handle->ha_fid) - sizeof(handle->ha_fid.fid_len) !=
> + handle->ha_fid.fid_len)
> + return -EINVAL;
> +
> + fid.ino = handle->ha_fid.fid_ino;
> + fid.gen = handle->ha_fid.fid_gen;
> +
> + dentry = exportfs_decode_fh(filp->f_path.mnt,
> + (struct fid *)&fid, 3,
> + FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG,
> + xfs_parent_handle_acceptable, NULL);
> + if (IS_ERR(dentry))
> + return PTR_ERR(dentry);
> + } else {
> + /* Or just use the dentry of the open file. */
> + dentry = dget(file_dentry(filp));
> + }
> +
> + /* Lock XFS inode... */
> + ip = XFS_I(d_inode(dentry));
> + ilock = XFS_IOLOCK_SHARED | XFS_MMAPLOCK_SHARED | XFS_ILOCK_SHARED;
> + xfs_ilock(ip, ilock);
> +
> + /* Bail out early for the root dir. */
> + if (ip->i_ino == ip->i_mount->m_sb.sb_rootino) {
Is it interesting for the API to filter path that are under the root of
the mount where the ioctl was issued from (i.e. bind mount case)?
Probably not. This API should probably be bind mount blind, but
userspace should know to expect that it may can get a real fs path,
that is not really resolvable in its mount namespace.
> + pi->pi_oflags |= XFS_PPTR_OFLAG_ROOT;
> + goto out_dentry;
> + }
> +
> + /* Filter out the unconnected inodes. */
> + if (d_unlinked(dentry) || (dentry->d_flags & DCACHE_DISCONNECTED))
> + goto out_dentry;
> + if (IS_ROOT(dentry)) {
> + pi->pi_oflags |= XFS_PPTR_OFLAG_ROOT;
This is a bit confusing... IS_ROOT(dentry) does not mean that you found
the fs root, it really means disconnected of first order (parent == self),
while DCACHE_DISCONNECTED means not connected up to fs root.
You want to check if dentry == dentry->d_sb->s_root.
> + goto out_dentry;
> + }
> +
> + /* Otherwise look up the parent... */
> + dparent = dget_parent(dentry);
> + if (IS_ERR(dparent)) {
> + error = PTR_ERR(dparent);
> + goto out_dentry;
> + }
> +
> + dir_inode = d_inode(dparent);
> + if (!dir_inode)
> + goto out_dparent;
> +
> + /* ...and emit a record. */
> + error = xfs_parent_emit(pi, pb, dir_inode->i_ino,
> + dir_inode->i_generation,
> + ACCESS_ONCE(dentry->d_name.len),
> + ACCESS_ONCE(dentry->d_name.name));
Just in case some bits of this patch are going to be used in final
implementation, you need to copy d_name either under
dentry->d_lock or use take_dentry_name_snapshot().
Cheers,
Amir.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2017-12-08 7:52 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-12-08 3:54 [RFCRAP DONOTMERGE PATCH 0/2] xfs: parent pointers Darrick J. Wong
2017-12-08 4:02 ` [RFCRAP DONOTMERGE PATCH 1/2] xfs: sketchy implementation of " Darrick J. Wong
2017-12-08 7:52 ` Amir Goldstein
2017-12-08 4:03 ` [RFCRAP DONOTMERGE PATCH 2/2] xfsprogs: implement the upper half " Darrick J. Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox