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 \
-       ll_decode_filter_fid.8
+       ll_decode_filter_fid.8 llapi_path2parent.3 llapi_fd2parent.3
 
 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 path2fid <path> ...
+.B lfs path2fid [--parents] <path> ...
 .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
-.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.
+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 }
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);
 
+/** 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,
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_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 {
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);
+/* 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);
index c42ef84..1c30e4f 100644 (file)
@@ -1654,6 +1654,8 @@ out_rmdir:
                 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;
index 75b1391..72a8caa 100644 (file)
@@ -2448,6 +2448,9 @@ ll_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
                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: {
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_linkea.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_getparent(struct file *file, struct getparent __user *arg);
+
 #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);
 }
+
+/**
+ * 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
-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
index 96a7081..2850a99 100644 (file)
@@ -9768,6 +9768,82 @@ test_154e()
 }
 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
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: 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, "
@@ -3362,33 +3362,75 @@ static int lfs_fid2path(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;
-       else if (argc > 2)
+       else if (optind < argc - 1)
                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) {
-                       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;
                        }
-                       goto out;
                }
-               printf("%s%s"DFID"\n", *sep != '\0' ? *path : "", sep,
-                      PFID(&fid));
-out:
-               path++;
        }
 
        return rc;
index 292acd4..735c00e 100644 (file)
@@ -4420,6 +4420,55 @@ int llapi_path2fid(const char *path, lustre_fid *fid)
        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;