From b6e3612d845cf506e977f49aa85ddd1e709fd194 Mon Sep 17 00:00:00 2001 From: Henri Doreau Date: Wed, 15 Oct 2014 09:56:04 +0200 Subject: [PATCH] LU-3613 llite: Add ioctl to get parent fids from link EA. Added LL_IOC_GETPARENT to retrieve the /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 Signed-off-by: Henri Doreau Change-Id: I10f9316da605050be8fc709fe88d87abe86e22a4 Reviewed-on: http://review.whamcloud.com/7069 Tested-by: Jenkins Reviewed-by: Ned Bass Tested-by: Maloo Reviewed-by: frank zago Reviewed-by: John L. Hammond Reviewed-by: Oleg Drokin --- lustre/doc/Makefile.am | 2 +- lustre/doc/lfs.1 | 7 +- lustre/doc/llapi_fd2parent.3 | 1 + lustre/doc/llapi_path2parent.3 | 63 ++++++++++++++++++ lustre/include/lustre/lustre_idl.h | 8 +++ lustre/include/lustre/lustre_user.h | 1 + lustre/include/lustre/lustreapi.h | 7 ++ lustre/llite/dir.c | 2 + lustre/llite/file.c | 3 + lustre/llite/llite_internal.h | 3 + lustre/llite/llite_lib.c | 128 ++++++++++++++++++++++++++++++++++++ lustre/tests/Makefile.am | 2 +- lustre/tests/sanity.sh | 76 +++++++++++++++++++++ lustre/utils/lfs.c | 76 ++++++++++++++++----- lustre/utils/liblustreapi.c | 49 ++++++++++++++ 15 files changed, 407 insertions(+), 21 deletions(-) create mode 100644 lustre/doc/llapi_fd2parent.3 create mode 100644 lustre/doc/llapi_path2parent.3 diff --git a/lustre/doc/Makefile.am b/lustre/doc/Makefile.am index 5829f30..cc37c46 100644 --- a/lustre/doc/Makefile.am +++ b/lustre/doc/Makefile.am @@ -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 diff --git a/lustre/doc/lfs.1 b/lustre/doc/lfs.1 index 2face13e..b2ffcd2 100644 --- a/lustre/doc/lfs.1 +++ b/lustre/doc/lfs.1 @@ -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 ... +.B lfs path2fid [--parents] ... .br .B lfs pool_list [.] | .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 ... +.B path2fid [--parents] ... 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 index 0000000..12210ce --- /dev/null +++ b/lustre/doc/llapi_fd2parent.3 @@ -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 index 0000000..13a3d9b --- /dev/null +++ b/lustre/doc/llapi_path2parent.3 @@ -0,0 +1,63 @@ +.TH llapi_path2parent 3 "2014 Oct 13" "Lustre User API" +.SH NAME +llapi_path2parent, llapi_fd2parent \- Retrieve /name(s) for an entry +in Lustre. +.SH SYNOPSIS +.nf +.B #include +.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) diff --git a/lustre/include/lustre/lustre_idl.h b/lustre/include/lustre/lustre_idl.h index 307b0a2..9abdf54 100644 --- a/lustre/include/lustre/lustre_idl.h +++ b/lustre/include/lustre/lustre_idl.h @@ -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, diff --git a/lustre/include/lustre/lustre_user.h b/lustre/include/lustre/lustre_user.h index 3764a54..9f4063f 100644 --- a/lustre/include/lustre/lustre_user.h +++ b/lustre/include/lustre/lustre_user.h @@ -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 { diff --git a/lustre/include/lustre/lustreapi.h b/lustre/include/lustre/lustreapi.h index 072df8f..b30f9e2 100644 --- a/lustre/include/lustre/lustreapi.h +++ b/lustre/include/lustre/lustreapi.h @@ -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); diff --git a/lustre/llite/dir.c b/lustre/llite/dir.c index c42ef84..1c30e4f 100644 --- a/lustre/llite/dir.c +++ b/lustre/llite/dir.c @@ -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; diff --git a/lustre/llite/file.c b/lustre/llite/file.c index 75b1391..72a8caa 100644 --- a/lustre/llite/file.c +++ b/lustre/llite/file.c @@ -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: { diff --git a/lustre/llite/llite_internal.h b/lustre/llite/llite_internal.h index 739dae2..4f9b890 100644 --- a/lustre/llite/llite_internal.h +++ b/lustre/llite/llite_internal.h @@ -40,6 +40,7 @@ #include #include /* for s2sbi */ #include +#include /* for struct cl_lock_descr and struct cl_io */ #include @@ -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 */ diff --git a/lustre/llite/llite_lib.c b/lustre/llite/llite_lib.c index dc62d60..17e388f8 100644 --- a/lustre/llite/llite_lib.c +++ b/lustre/llite/llite_lib.c @@ -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); +} diff --git a/lustre/tests/Makefile.am b/lustre/tests/Makefile.am index e75b2b6..c7284e1 100644 --- a/lustre/tests/Makefile.am +++ b/lustre/tests/Makefile.am @@ -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 diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index 96a7081..2850a99 100644 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -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 /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 diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index 0943573..5fba86f 100644 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -298,7 +298,7 @@ command_t cmdlist[] = { "usage: fid2path [--link ] ..." /* [ --rec ] */ }, {"path2fid", lfs_path2fid, 0, "Display the fid(s) for a given path(s).\n" - "usage: path2fid ..."}, + "usage: path2fid [--parents] ..."}, {"data_version", lfs_data_version, 0, "Display file data version for " "a given path.\n" "usage: data_version -[n|r|w] "}, {"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; diff --git a/lustre/utils/liblustreapi.c b/lustre/utils/liblustreapi.c index 292acd4..735c00e 100644 --- a/lustre/utils/liblustreapi.c +++ b/lustre/utils/liblustreapi.c @@ -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; -- 1.8.3.1