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
.B lfs osts
.RB [ path ]
.br
-.B lfs path2fid <path> ...
+.B lfs path2fid [--parents] <path> ...
.br
.B lfs pool_list <filesystem>[.<pool>] | <pathname>
.br
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 }
--- /dev/null
+.so man3/llapi_path2parent.3
--- /dev/null
+.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)
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,
#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 {
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);
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;
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: {
#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>
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 */
}
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);
+}
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
}
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
"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, "
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;
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;