Whamcloud - gitweb
LU-3613 llite: Add ioctl to get parent fids from link EA. 69/7069/17
authorHenri Doreau <henri.doreau@cea.fr>
Wed, 15 Oct 2014 07:56:04 +0000 (09:56 +0200)
committerOleg Drokin <oleg.drokin@intel.com>
Fri, 31 Oct 2014 15:35:06 +0000 (15:35 +0000)
Added LL_IOC_GETPARENT to retrieve the <parent_fid>/name(s) of a given
entry, based on its link EA. This saves multiple calls to
path2fid/fid2path.

Added llapi_path2parent() and llapi_fd2parent() wrappers.

Added '--parents' option to lfs path2fid to call this function.

New sanity test 154f to validate the behavior.

Signed-off-by: Thomas Leibovici <thomas.leibovici@cea.fr>
Signed-off-by: Henri Doreau <henri.doreau@cea.fr>
Change-Id: I10f9316da605050be8fc709fe88d87abe86e22a4
Reviewed-on: http://review.whamcloud.com/7069
Tested-by: Jenkins
Reviewed-by: Ned Bass <bass6@llnl.gov>
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: frank zago <fzago@cray.com>
Reviewed-by: John L. Hammond <john.hammond@intel.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
15 files changed:
lustre/doc/Makefile.am
lustre/doc/lfs.1
lustre/doc/llapi_fd2parent.3 [new file with mode: 0644]
lustre/doc/llapi_path2parent.3 [new file with mode: 0644]
lustre/include/lustre/lustre_idl.h
lustre/include/lustre/lustre_user.h
lustre/include/lustre/lustreapi.h
lustre/llite/dir.c
lustre/llite/file.c
lustre/llite/llite_internal.h
lustre/llite/llite_lib.c
lustre/tests/Makefile.am
lustre/tests/sanity.sh
lustre/utils/lfs.c
lustre/utils/liblustreapi.c

index 5829f30..cc37c46 100644 (file)
@@ -54,7 +54,7 @@ MANFILES = lustre.7 lfs.1 mount.lustre.8 lctl.8 \
        llapi_layout_stripe_count_get.3 llapi_layout_stripe_count_set.3 \
        llapi_layout_stripe_size_get.3  llapi_layout_stripe_size_set.3 \
        llapi_path2fid.3 llapi_group_lock.3 llapi_group_unlock.3 \
        llapi_layout_stripe_count_get.3 llapi_layout_stripe_count_set.3 \
        llapi_layout_stripe_size_get.3  llapi_layout_stripe_size_set.3 \
        llapi_path2fid.3 llapi_group_lock.3 llapi_group_unlock.3 \
-       ll_decode_filter_fid.8
+       ll_decode_filter_fid.8 llapi_path2parent.3 llapi_fd2parent.3
 
 SERVER_MANFILES = mkfs.lustre.8 tunefs.lustre.8
 
 
 SERVER_MANFILES = mkfs.lustre.8 tunefs.lustre.8
 
index 2face13..b2ffcd2 100644 (file)
@@ -44,7 +44,7 @@ lfs \- Lustre utility to create a file with specific striping pattern, find the
 .B lfs osts
 .RB [ path ]
 .br
 .B lfs osts
 .RB [ path ]
 .br
-.B lfs path2fid <path> ...
+.B lfs path2fid [--parents] <path> ...
 .br
 .B lfs pool_list <filesystem>[.<pool>] | <pathname>
 .br
 .br
 .B lfs pool_list <filesystem>[.<pool>] | <pathname>
 .br
@@ -216,9 +216,12 @@ hard links, then all of the pathnames for that file are printed, unless
 at 0, in no particular order).  If multiple fids are specified, but only a
 single pathname is needed for each file, use \fB--link 0\fR.
 .TP
 at 0, in no particular order).  If multiple fids are specified, but only a
 single pathname is needed for each file, use \fB--link 0\fR.
 .TP
-.B path2fid <path> ...
+.B path2fid [--parents] <path> ...
 Print out the FIDs for the specified \fBpath(s)\fR.  If multiple pathnames
 are given, then they will be printed one per line with the path as prefix.
 Print out the FIDs for the specified \fBpath(s)\fR.  If multiple pathnames
 are given, then they will be printed one per line with the path as prefix.
+The \fB--parents\fR switch makes it output the parent FID and name(s) of the
+given entries. If an entry has multiple links, these are displayed on a single
+line, tab-separated.
 .TP
 .B pool_list
 .RI { filesystem }[ .poolname "] | {" pathname }
 .TP
 .B pool_list
 .RI { filesystem }[ .poolname "] | {" pathname }
diff --git a/lustre/doc/llapi_fd2parent.3 b/lustre/doc/llapi_fd2parent.3
new file mode 100644 (file)
index 0000000..12210ce
--- /dev/null
@@ -0,0 +1 @@
+.so man3/llapi_path2parent.3
diff --git a/lustre/doc/llapi_path2parent.3 b/lustre/doc/llapi_path2parent.3
new file mode 100644 (file)
index 0000000..13a3d9b
--- /dev/null
@@ -0,0 +1,63 @@
+.TH llapi_path2parent 3 "2014 Oct 13" "Lustre User API"
+.SH NAME
+llapi_path2parent, llapi_fd2parent \- Retrieve <parent FID>/name(s) for an entry
+in Lustre.
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.PP
+.BI "int llapi_path2parent(const char *" path ", unsigned int " linkno ","
+.BI "                      lustre_fid *" parent_fid ", char *" name ","
+.BI "                      size_t " name_size );
+
+.BI "int llapi_fd2parent(int " fd ", unsigned int " linkno ","
+.BI "                    lustre_fid *" parent_fid ", char *" name ","
+.BI "                    size_t " name_size );
+.sp
+.fi
+.SH DESCRIPTION
+.PP
+.BR llapi_path2parent()
+for link number
+.I linkno
+stores the FID of the parent directory into
+.I parent_fid
+and the zero-terminated name of the entry into the buffer
+.I name
+which is expected to be of size
+.IR name_size .
+
+The function
+.B llapi_fd2parent()
+behaves similarly except that it operates on an open file descriptor
+instead of a path.
+.sp
+.SH RETURN VALUES
+.LP
+.B llapi_path2fid()
+and
+.B llapi_fd2fid()
+return 0 on success or a negative errno value on failure.
+.SH ERRORS
+.TP 15
+.SM -ENODATA
+linkno has reached the link count.
+.TP
+.SM -ENOTTY
+.I path
+does not reside on a Lustre filesystem.
+.TP
+.SM -ENOENT
+.I path
+does not exist.
+.TP
+.SM -EINVAL
+An invalid argument was specified.
+.TP
+.SM -EOVERFLOW
+The given buffer was too small.
+.TP
+.SM -EPERM
+The file cannot be open by user or CAP_DAC_READ_SEARCH is not granted.
+.SH "SEE ALSO"
+.BR liblustreapi (7)
index 307b0a2..9abdf54 100644 (file)
@@ -3914,6 +3914,14 @@ struct getinfo_fid2path {
 
 void lustre_swab_fid2path (struct getinfo_fid2path *gf);
 
 
 void lustre_swab_fid2path (struct getinfo_fid2path *gf);
 
+/** path2parent request/reply structures */
+struct getparent {
+       struct lu_fid   gp_fid;         /**< parent FID */
+       __u32           gp_linkno;      /**< hardlink number */
+       __u32           gp_name_size;   /**< size of the name field */
+       char            gp_name[0];     /**< zero-terminated link name */
+} __attribute__((packed));
+
 enum {
         LAYOUT_INTENT_ACCESS    = 0,
         LAYOUT_INTENT_READ      = 1,
 enum {
         LAYOUT_INTENT_ACCESS    = 0,
         LAYOUT_INTENT_READ      = 1,
index 3764a54..9f4063f 100644 (file)
@@ -260,6 +260,7 @@ struct ost_id {
 #define LL_IOC_LMV_SET_DEFAULT_STRIPE  _IOWR('f', 246, struct lmv_user_md)
 #define LL_IOC_MIGRATE                 _IOR('f', 247, int)
 #define LL_IOC_FID2MDTIDX              _IOWR('f', 248, struct lu_fid)
 #define LL_IOC_LMV_SET_DEFAULT_STRIPE  _IOWR('f', 246, struct lmv_user_md)
 #define LL_IOC_MIGRATE                 _IOR('f', 247, int)
 #define LL_IOC_FID2MDTIDX              _IOWR('f', 248, struct lu_fid)
+#define LL_IOC_GETPARENT               _IOWR('f', 249, struct getparent)
 
 /* Lease types for use as arg and return of LL_IOC_{GET,SET}_LEASE ioctl. */
 enum ll_lease_type {
 
 /* Lease types for use as arg and return of LL_IOC_{GET,SET}_LEASE ioctl. */
 enum ll_lease_type {
index 072df8f..b30f9e2 100644 (file)
@@ -276,6 +276,13 @@ extern int llapi_path2fid(const char *path, lustre_fid *fid);
 extern int llapi_get_mdt_index_by_fid(int fd, const lustre_fid *fid,
                                      int *mdt_index);
 extern int llapi_fd2fid(const int fd, lustre_fid *fid);
 extern int llapi_get_mdt_index_by_fid(int fd, const lustre_fid *fid,
                                      int *mdt_index);
 extern int llapi_fd2fid(const int fd, lustre_fid *fid);
+/* get FID of parent dir + the related name of entry in this parent dir */
+extern int llapi_path2parent(const char *path, unsigned int linkno,
+                            lustre_fid *parent_fid, char *name,
+                            size_t name_size);
+extern int llapi_fd2parent(int fd, unsigned int linkno,
+                          lustre_fid *parent_fid, char *name,
+                          size_t name_size);
 extern int llapi_chomp_string(char *buf);
 extern int llapi_open_by_fid(const char *dir, const lustre_fid *fid,
                             int open_flags);
 extern int llapi_chomp_string(char *buf);
 extern int llapi_open_by_fid(const char *dir, const lustre_fid *fid,
                             int open_flags);
index c42ef84..1c30e4f 100644 (file)
@@ -1654,6 +1654,8 @@ out_rmdir:
                 RETURN(rc);
        case OBD_IOC_FID2PATH:
                RETURN(ll_fid2path(inode, (void __user *)arg));
                 RETURN(rc);
        case OBD_IOC_FID2PATH:
                RETURN(ll_fid2path(inode, (void __user *)arg));
+       case LL_IOC_GETPARENT:
+               RETURN(ll_getparent(file, (void __user *)arg));
        case LL_IOC_FID2MDTIDX: {
                struct obd_export *exp = ll_i2mdexp(inode);
                struct lu_fid     fid;
        case LL_IOC_FID2MDTIDX: {
                struct obd_export *exp = ll_i2mdexp(inode);
                struct lu_fid     fid;
index 75b1391..72a8caa 100644 (file)
@@ -2448,6 +2448,9 @@ ll_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
                RETURN(0);
        }
 
                RETURN(0);
        }
+       case LL_IOC_GETPARENT:
+               RETURN(ll_getparent(file, (void __user *)arg));
+
        case OBD_IOC_FID2PATH:
                RETURN(ll_fid2path(inode, (void __user *)arg));
        case LL_IOC_DATA_VERSION: {
        case OBD_IOC_FID2PATH:
                RETURN(ll_fid2path(inode, (void __user *)arg));
        case LL_IOC_DATA_VERSION: {
index 739dae2..4f9b890 100644 (file)
@@ -40,6 +40,7 @@
 #include <lustre_ver.h>
 #include <lustre_disk.h>  /* for s2sbi */
 #include <lustre_eacl.h>
 #include <lustre_ver.h>
 #include <lustre_disk.h>  /* for s2sbi */
 #include <lustre_eacl.h>
+#include <lustre_linkea.h>
 
 /* for struct cl_lock_descr and struct cl_io */
 #include <cl_object.h>
 
 /* for struct cl_lock_descr and struct cl_io */
 #include <cl_object.h>
@@ -1677,4 +1678,6 @@ void ll_xattr_fini(void);
 int ll_page_sync_io(const struct lu_env *env, struct cl_io *io,
                    struct cl_page *page, enum cl_req_type crt);
 
 int ll_page_sync_io(const struct lu_env *env, struct cl_io *io,
                    struct cl_page *page, enum cl_req_type crt);
 
+int ll_getparent(struct file *file, struct getparent __user *arg);
+
 #endif /* LLITE_INTERNAL_H */
 #endif /* LLITE_INTERNAL_H */
index dc62d60..17e388f 100644 (file)
@@ -2777,3 +2777,131 @@ void ll_compute_rootsquash_state(struct ll_sb_info *sbi)
        }
        up_write(&squash->rsi_sem);
 }
        }
        up_write(&squash->rsi_sem);
 }
+
+/**
+ * Parse linkea content to extract information about a given hardlink
+ *
+ * \param[in]  ldata  - Initialized linkea data
+ * \param[in]  linkno - Link identifier
+ * \param[out] gpout  - Destination structure to fill with linkno,
+ *                      parent FID and entry name
+ * \param[in]  size   - Size of the gp_name buffer in gpout
+ *
+ * \retval 0 on success
+ * \retval Appropriate negative error code on failure
+ */
+static int ll_linkea_decode(struct linkea_data *ldata, unsigned int linkno,
+                           struct getparent *gpout, size_t name_size)
+{
+       unsigned int    idx;
+       struct lu_name  ln;
+       int             rc;
+       ENTRY;
+
+       rc = linkea_init(ldata);
+       if (rc < 0)
+               RETURN(rc);
+
+       if (linkno >= ldata->ld_leh->leh_reccount)
+               /* beyond last link */
+               RETURN(-ENODATA);
+
+       linkea_first_entry(ldata);
+       idx = 0;
+       while (ldata->ld_lee != NULL) {
+               linkea_entry_unpack(ldata->ld_lee, &ldata->ld_reclen, &ln,
+                                   &gpout->gp_fid);
+               if (idx == linkno)
+                       break;
+
+               linkea_next_entry(ldata);
+               idx++;
+       }
+
+       if (idx < linkno)
+               RETURN(-ENODATA);
+
+       if (ln.ln_namelen >= name_size)
+               RETURN(-EOVERFLOW);
+
+       gpout->gp_linkno = linkno;
+       strlcpy(gpout->gp_name, ln.ln_name, name_size);
+       RETURN(0);
+}
+
+/**
+ * Get parent FID and name of an identified link. Operation is performed for
+ * a given link number, letting the caller iterate over linkno to list one or
+ * all links of an entry.
+ *
+ * \param[in]     file - File descriptor against which to perform the operation
+ * \param[in,out] arg  - User-filled structure containing the linkno to operate
+ *                       on and the available size. It is eventually filled with
+ *                       the requested information or left untouched on error
+ *
+ * \retval - 0 on success
+ * \retval - Appropriate negative error code on failure
+ */
+int ll_getparent(struct file *file, struct getparent __user *arg)
+{
+       struct dentry           *dentry = file->f_dentry;
+       struct inode            *inode = file->f_dentry->d_inode;
+       struct linkea_data      *ldata;
+       struct lu_buf            buf = LU_BUF_NULL;
+       struct getparent        *gpout;
+       __u32                    linkno;
+       __u32                    name_size;
+       size_t                   out_size;
+       int                      rc;
+
+       ENTRY;
+
+       if (!cfs_capable(CFS_CAP_DAC_READ_SEARCH) &&
+           !(ll_i2sbi(inode)->ll_flags & LL_SBI_USER_FID2PATH))
+               RETURN(-EPERM);
+
+       if (get_user(name_size, &arg->gp_name_size))
+               RETURN(-EFAULT);
+
+       if (get_user(linkno, &arg->gp_linkno))
+               RETURN(-EFAULT);
+
+       if (name_size > PATH_MAX)
+               RETURN(-EINVAL);
+
+       OBD_ALLOC(ldata, sizeof(*ldata));
+       if (ldata == NULL)
+               RETURN(-ENOMEM);
+
+       rc = linkea_data_new(ldata, &buf);
+       if (rc < 0)
+               GOTO(ldata_free, rc);
+
+       out_size = sizeof(*gpout) + name_size;
+       OBD_ALLOC(gpout, out_size);
+       if (gpout == NULL)
+               GOTO(lb_free, rc = -ENOMEM);
+
+       if (copy_from_user(gpout, arg, sizeof(*gpout)))
+               GOTO(gp_free, rc = -EFAULT);
+
+       rc = ll_getxattr(dentry, XATTR_NAME_LINK, buf.lb_buf, buf.lb_len);
+       if (rc < 0)
+               GOTO(gp_free, rc);
+
+       rc = ll_linkea_decode(ldata, linkno, gpout, name_size);
+       if (rc < 0)
+               GOTO(gp_free, rc);
+
+       if (copy_to_user(arg, gpout, out_size))
+               GOTO(gp_free, rc = -EFAULT);
+
+gp_free:
+       OBD_FREE(gpout, out_size);
+lb_free:
+       lu_buf_free(&buf);
+ldata_free:
+       OBD_FREE(ldata, sizeof(*ldata));
+
+       RETURN(rc);
+}
index e75b2b6..c7284e1 100644 (file)
@@ -93,7 +93,7 @@ it_test_LDADD=$(LIBCFS)
 rwv_LDADD=$(LIBCFS)
 
 ll_dirstripe_verify_SOURCES= ll_dirstripe_verify.c
 rwv_LDADD=$(LIBCFS)
 
 ll_dirstripe_verify_SOURCES= ll_dirstripe_verify.c
-ll_dirstripe_verify_LDADD= -L$(top_builddir)/lustre/utils $(PTHREAD_LIBS) -llustreapi $(LIBCFS)
+ll_dirstripe_verify_LDADD= -L$(top_builddir)/lustre/utils $(PTHREAD_LIBS) $(LIBLUSTREAPI) $(LIBCFS)
 
 flocks_test_SOURCES=flocks_test.c
 flocks_test_LDADD=-lpthread
 
 flocks_test_SOURCES=flocks_test.c
 flocks_test_LDADD=-lpthread
index 96a7081..2850a99 100644 (file)
@@ -9768,6 +9768,82 @@ test_154e()
 }
 run_test 154e ".lustre is not returned by readdir"
 
 }
 run_test 154e ".lustre is not returned by readdir"
 
+test_154f() {
+       # create parent directory on a single MDT to avoid cross-MDT hardlinks
+       test_mkdir -p -c1 $DIR/$tdir/d
+       # test dirs inherit from its stripe
+       mkdir -p $DIR/$tdir/d/foo1 || error "mkdir error"
+       mkdir -p $DIR/$tdir/d/foo2 || error "mkdir error"
+       cp /etc/hosts $DIR/$tdir/d/foo1/$tfile
+       ln $DIR/$tdir/d/foo1/$tfile $DIR/$tdir/d/foo2/link
+       touch $DIR/f
+
+       # get fid of parents
+       local FID0=$($LFS path2fid $DIR/$tdir/d)
+       local FID1=$($LFS path2fid $DIR/$tdir/d/foo1)
+       local FID2=$($LFS path2fid $DIR/$tdir/d/foo2)
+       local FID3=$($LFS path2fid $DIR)
+
+       # check that path2fid --parents returns expected <parent_fid>/name
+       # 1) test for a directory (single parent)
+       local parent=$($LFS path2fid --parents $DIR/$tdir/d/foo1)
+       [ "$parent" == "$FID0/foo1" ] ||
+               error "expected parent: $FID0/foo1, got: $parent"
+
+       # 2) test for a file with nlink > 1 (multiple parents)
+       parent=$($LFS path2fid --parents $DIR/$tdir/d/foo1/$tfile)
+       echo "$parent" | grep -F "$FID1/$tfile" ||
+               error "$FID1/$tfile not returned in parent list"
+       echo "$parent" | grep -F "$FID2/link" ||
+               error "$FID2/link not returned in parent list"
+
+       # 3) get parent by fid
+       local file_fid=$($LFS path2fid $DIR/$tdir/d/foo1/$tfile)
+       parent=$($LFS path2fid --parents $MOUNT/.lustre/fid/$file_fid)
+       echo "$parent" | grep -F "$FID1/$tfile" ||
+               error "$FID1/$tfile not returned in parent list (by fid)"
+       echo "$parent" | grep -F "$FID2/link" ||
+               error "$FID2/link not returned in parent list (by fid)"
+
+       # 4) test for entry in root directory
+       parent=$($LFS path2fid --parents $DIR/f)
+       echo "$parent" | grep -F "$FID3/f" ||
+               error "$FID3/f not returned in parent list"
+
+       # 5) test it on root directory
+       [ -z "$($LFS path2fid --parents $MOUNT 2>/dev/null)" ] ||
+               error "$MOUNT should not have parents"
+
+       # enable xattr caching and check that linkea is correctly updated
+       local save="$TMP/$TESTSUITE-$TESTNAME.parameters"
+       save_lustre_params client "llite.*.xattr_cache" > $save
+       lctl set_param llite.*.xattr_cache 1
+
+       # 6.1) linkea update on rename
+       mv $DIR/$tdir/d/foo1/$tfile $DIR/$tdir/d/foo2/$tfile.moved
+
+       # get parents by fid
+       parent=$($LFS path2fid --parents $MOUNT/.lustre/fid/$file_fid)
+       # foo1 should no longer be returned in parent list
+       echo "$parent" | grep -F "$FID1" &&
+               error "$FID1 should no longer be in parent list"
+       # the new path should appear
+       echo "$parent" | grep -F "$FID2/$tfile.moved" ||
+               error "$FID2/$tfile.moved is not in parent list"
+
+       # 6.2) linkea update on unlink
+       rm -f $DIR/$tdir/d/foo2/link
+       parent=$($LFS path2fid --parents $MOUNT/.lustre/fid/$file_fid)
+       # foo2/link should no longer be returned in parent list
+       echo "$parent" | grep -F "$FID2/link" &&
+               error "$FID2/link should no longer be in parent list"
+       true
+
+       rm -f $DIR/f
+       restore_lustre_params < $save
+}
+run_test 154f "get parent fids by reading link ea"
+
 test_155_small_load() {
     local temp=$TMP/$tfile
     local file=$DIR/$tfile
 test_155_small_load() {
     local temp=$TMP/$tfile
     local file=$DIR/$tfile
index 0943573..5fba86f 100644 (file)
@@ -298,7 +298,7 @@ command_t cmdlist[] = {
         "usage: fid2path [--link <linkno>] <fsname|rootpath> <fid> ..."
                /* [ --rec <recno> ] */ },
        {"path2fid", lfs_path2fid, 0, "Display the fid(s) for a given path(s).\n"
         "usage: fid2path [--link <linkno>] <fsname|rootpath> <fid> ..."
                /* [ --rec <recno> ] */ },
        {"path2fid", lfs_path2fid, 0, "Display the fid(s) for a given path(s).\n"
-        "usage: path2fid <path> ..."},
+        "usage: path2fid [--parents] <path> ..."},
        {"data_version", lfs_data_version, 0, "Display file data version for "
         "a given path.\n" "usage: data_version -[n|r|w] <path>"},
        {"hsm_state", lfs_hsm_state, 0, "Display the HSM information (states, "
        {"data_version", lfs_data_version, 0, "Display file data version for "
         "a given path.\n" "usage: data_version -[n|r|w] <path>"},
        {"hsm_state", lfs_hsm_state, 0, "Display the HSM information (states, "
@@ -3362,33 +3362,75 @@ static int lfs_fid2path(int argc, char **argv)
 
 static int lfs_path2fid(int argc, char **argv)
 {
 
 static int lfs_path2fid(int argc, char **argv)
 {
-       char **path;
-       const char *sep = "";
-       lustre_fid fid;
-       int rc = 0;
+       struct option     long_opts[] = {
+               {"parents", no_argument, 0, 'p'},
+               {0, 0, 0, 0}
+       };
+       char            **path;
+       const char        short_opts[] = "p";
+       const char       *sep = "";
+       lustre_fid        fid;
+       int               rc = 0;
+       bool              show_parents = false;
 
 
-       if (argc < 2)
+       optind = 0;
+       while ((rc = getopt_long(argc, argv, short_opts,
+                                long_opts, NULL)) != -1) {
+               switch (rc) {
+               case 'p':
+                       show_parents = true;
+                       break;
+               default:
+                       fprintf(stderr, "error: %s: option '%s' unrecognized\n",
+                               argv[0], argv[optind - 1]);
+                       return CMD_HELP;
+               }
+       }
+
+       if (optind > argc - 1)
                return CMD_HELP;
                return CMD_HELP;
-       else if (argc > 2)
+       else if (optind < argc - 1)
                sep = ": ";
 
                sep = ": ";
 
-       path = argv + 1;
-       while (*path != NULL) {
-               int err = llapi_path2fid(*path, &fid);
+       rc = 0;
+       for (path = argv + optind; *path != NULL; path++) {
+               int err = 0;
+               if (!show_parents) {
+                       err = llapi_path2fid(*path, &fid);
+                       if (!err)
+                               printf("%s%s"DFID"\n",
+                                      *sep != '\0' ? *path : "", sep,
+                                      PFID(&fid));
+               } else {
+                       char            name[NAME_MAX + 1];
+                       unsigned int    linkno = 0;
+
+                       while ((err = llapi_path2parent(*path, linkno, &fid,
+                                               name, sizeof(name))) == 0) {
+                               if (*sep != '\0' && linkno == 0)
+                                       printf("%s%s", *path, sep);
+
+                               printf("%s"DFID"/%s", linkno != 0 ? "\t" : "",
+                                      PFID(&fid), name);
+                               linkno++;
+                       }
+
+                       /* err == -ENODATA is end-of-loop */
+                       if (linkno > 0 && err == -ENODATA) {
+                               printf("\n");
+                               err = 0;
+                       }
+               }
 
                if (err) {
 
                if (err) {
-                       fprintf(stderr, "%s: can't get fid for %s: %s\n",
-                               argv[0], *path, strerror(-err));
+                       fprintf(stderr, "%s: can't get %sfid for %s: %s\n",
+                               argv[0], show_parents ? "parent " : "", *path,
+                               strerror(-err));
                        if (rc == 0) {
                                rc = err;
                                errno = -err;
                        }
                        if (rc == 0) {
                                rc = err;
                                errno = -err;
                        }
-                       goto out;
                }
                }
-               printf("%s%s"DFID"\n", *sep != '\0' ? *path : "", sep,
-                      PFID(&fid));
-out:
-               path++;
        }
 
        return rc;
        }
 
        return rc;
index 292acd4..735c00e 100644 (file)
@@ -4420,6 +4420,55 @@ int llapi_path2fid(const char *path, lustre_fid *fid)
        return rc;
 }
 
        return rc;
 }
 
+int llapi_fd2parent(int fd, unsigned int linkno, lustre_fid *parent_fid,
+                   char *name, size_t name_size)
+{
+       struct getparent        *gp;
+       int                      rc;
+
+       gp = malloc(sizeof(*gp) + name_size);
+       if (gp == NULL)
+               return -ENOMEM;
+
+       memset(gp, 0, sizeof(*gp) + name_size);
+       gp->gp_linkno = linkno;
+       gp->gp_name_size = name_size;
+
+       rc = ioctl(fd, LL_IOC_GETPARENT, gp);
+       if (rc < 0) {
+               rc = -errno;
+               goto err_free;
+       }
+
+       if (gp->gp_name_size > name_size) {
+               rc = -EOVERFLOW;
+               goto err_free;
+       }
+
+       *parent_fid = gp->gp_fid;
+       strncpy(name, gp->gp_name, name_size);
+       name[name_size - 1] = '\0';
+
+err_free:
+       free(gp);
+       return rc;
+}
+
+int llapi_path2parent(const char *path, unsigned int linkno,
+                     lustre_fid *parent_fid, char *name, size_t name_size)
+{
+       int     fd;
+       int     rc;
+
+       fd = open(path, O_RDONLY | O_NONBLOCK | O_NOFOLLOW);
+       if (fd < 0)
+               return -errno;
+
+       rc = llapi_fd2parent(fd, linkno, parent_fid, name, name_size);
+       close(fd);
+       return rc;
+}
+
 int llapi_get_connect_flags(const char *mnt, __u64 *flags)
 {
         DIR *root;
 int llapi_get_connect_flags(const char *mnt, __u64 *flags)
 {
         DIR *root;