Whamcloud - gitweb
LU-4239 tests: test FID related APIs 45/12545/11
authorFrank Zago <fzago@cray.com>
Wed, 15 Oct 2014 19:19:44 +0000 (14:19 -0500)
committerOleg Drokin <oleg.drokin@intel.com>
Wed, 18 Mar 2015 11:02:37 +0000 (11:02 +0000)
This adds a few stress tests to the user lustre API, related to FIDs.

Change-Id: I34144a8f4c446e55c6630d31cae6a133d61eb304
Signed-off-by: Frank Zago <fzago@cray.com>
Test-Parameters: alwaysuploadlogs envdefinitions=ONLY=154g testlist=sanity
Reviewed-on: http://review.whamcloud.com/12545
Tested-by: Jenkins
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: John L. Hammond <john.hammond@intel.com>
Reviewed-by: Jian Yu <jian.yu@intel.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
lustre/tests/Makefile.am
lustre/tests/llapi_fid_test.c [new file with mode: 0644]
lustre/tests/sanity.sh

index 4c4cc9f..6afc47a 100644 (file)
@@ -77,7 +77,7 @@ noinst_PROGRAMS += mmap_sanity writemany reads flocks_test flock_deadlock
 noinst_PROGRAMS += write_time_limit rwv lgetxattr_size_check checkfiemap
 noinst_PROGRAMS += listxattr_size_check check_fhandle_syscalls badarea_io
 noinst_PROGRAMS += llapi_layout_test orphan_linkea_check llapi_hsm_test
-noinst_PROGRAMS += group_lock_test
+noinst_PROGRAMS += group_lock_test llapi_fid_test
 
 bin_PROGRAMS = mcreate munlink
 testdir = $(libdir)/lustre/tests
@@ -94,6 +94,7 @@ multiop_LDADD=$(LIBLUSTREAPI) $(PTHREAD_LIBS) $(LIBCFS)
 llapi_layout_test_LDADD=$(LIBLUSTREAPI)
 llapi_hsm_test_LDADD=$(LIBLUSTREAPI)
 group_lock_test_LDADD=$(LIBLUSTREAPI)
+llapi_fid_test_LDADD=$(LIBLUSTREAPI)
 it_test_LDADD=$(LIBCFS)
 rwv_LDADD=$(LIBCFS)
 
diff --git a/lustre/tests/llapi_fid_test.c b/lustre/tests/llapi_fid_test.c
new file mode 100644 (file)
index 0000000..6d08f61
--- /dev/null
@@ -0,0 +1,733 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+
+/*
+ * Copyright 2014 Cray Inc, all rights reserved.
+ * Author: Frank Zago.
+ *
+ * A few portions are extracted from llapi_layout_test.c
+ *
+ * The purpose of this test is to test the llapi fid related function
+ * (fid2path, path2fid, ...)
+ *
+ * The program will exit as soon a non zero error code is returned.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <poll.h>
+#include <time.h>
+
+#include <lustre/lustreapi.h>
+#include <lustre/lustre_idl.h>
+
+#define ERROR(fmt, ...)                                                        \
+       fprintf(stderr, "%s: %s:%d: %s: " fmt "\n",                     \
+               program_invocation_short_name, __FILE__, __LINE__,      \
+               __func__, ## __VA_ARGS__);
+
+#define DIE(fmt, ...)                  \
+       do {                           \
+               ERROR(fmt, ## __VA_ARGS__);     \
+               exit(EXIT_FAILURE);             \
+       } while (0)
+
+#define ASSERTF(cond, fmt, ...)                                                \
+       do {                                                            \
+               if (!(cond))                                            \
+                       DIE("assertion '%s' failed: "fmt,               \
+                           #cond, ## __VA_ARGS__);                     \
+       } while (0)
+
+#define PERFORM(testfn) \
+       do {                                                            \
+               cleanup();                                              \
+               fprintf(stderr, "Starting test " #testfn " at %lld\n",  \
+                       (unsigned long long)time(NULL));                \
+               testfn();                                               \
+               fprintf(stderr, "Finishing test " #testfn " at %lld\n", \
+                      (unsigned long long)time(NULL));                 \
+               cleanup();                                              \
+       } while (0)
+
+/* Name of file/directory. Will be set once and will not change. */
+static char mainpath[PATH_MAX];
+static const char *maindir = "llapi_fid_test_name_9585766";
+
+static char fsmountdir[PATH_MAX];      /* Lustre mountpoint */
+static char *lustre_dir;               /* Test directory inside Lustre */
+
+/* Cleanup our test directory. */
+static void cleanup(void)
+{
+       char cmd[PATH_MAX];
+       int rc;
+
+       rc = snprintf(cmd, sizeof(cmd), "rm -rf -- '%s'", mainpath);
+       ASSERTF(rc > 0 && rc < sizeof(cmd),
+               "invalid delete command for path '%s'", mainpath);
+       system(cmd);
+}
+
+/* Helper - call path2fid, fd2fid and fid2path against an existing
+ * file/directory */
+static void helper_fid2path(const char *filename, int fd)
+{
+       lustre_fid fid;
+       lustre_fid fid2;
+       lustre_fid fid3;
+       char fidstr[FID_LEN];
+       char path1[PATH_MAX];
+       char path2[PATH_MAX];
+       char path3[PATH_MAX];
+       long long recno1;
+       long long recno2;
+       int linkno1;
+       int linkno2;
+       int rc;
+
+       rc = llapi_path2fid(filename, &fid);
+       ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
+               filename, strerror(-rc));
+
+       /* Without braces */
+       snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
+       recno1 = -1;
+       linkno1 = 0;
+       rc = llapi_fid2path(lustre_dir, fidstr, path1,
+                           sizeof(path1), &recno1, &linkno1);
+       ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
+               fidstr, strerror(-rc));
+
+       /* Same with braces */
+       snprintf(fidstr, sizeof(fidstr), DFID, PFID(&fid));
+       recno2 = -1;
+       linkno2 = 0;
+       rc = llapi_fid2path(lustre_dir, fidstr, path2,
+                           sizeof(path2), &recno2, &linkno2);
+       ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
+               fidstr, strerror(-rc));
+
+       /* Make sure both calls to llapi_fid2path returned the same
+        * data. */
+       ASSERTF(strcmp(path1, path2) == 0, "paths are different: '%s' / '%s'",
+               path1, path2);
+       ASSERTF(recno1 == recno2, "recnos are different: %lld / %lld",
+               recno1, recno2);
+       ASSERTF(linkno1 == linkno2, "linknos are different: %d / %d",
+               linkno1, linkno2);
+
+       /* Try fd2fid and check that the result is still the same. */
+       if (fd != -1) {
+               rc = llapi_fd2fid(fd, &fid3);
+               ASSERTF(rc == 0, "llapi_fd2fid failed for '%s': %s",
+                       mainpath, strerror(-rc));
+
+               ASSERTF(memcmp(&fid, &fid3, sizeof(fid)) == 0,
+                       "fids are different");
+       }
+
+       /* Pass the result back to fid2path and ensure the fid stays
+        * the same. */
+       rc = snprintf(path3, sizeof(path3), "%s/%s", fsmountdir, path1);
+       ASSERTF((rc > 0 && rc < sizeof(path3)), "invalid name");
+       rc = llapi_path2fid(path3, &fid2);
+       ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
+               path3, strerror(-rc));
+       ASSERTF(memcmp(&fid, &fid2, sizeof(fid)) == 0, "fids are different");
+}
+
+/* Test helper_fid2path */
+static void test10(void)
+{
+       int rc;
+       int fd;
+       struct stat statbuf;
+
+       /* Against Lustre root */
+       helper_fid2path(lustre_dir, -1);
+
+       /* Against a regular file */
+       fd = creat(mainpath, 0);
+       ASSERTF(fd >= 0, "creat failed for '%s': %s",
+               mainpath, strerror(errno));
+       helper_fid2path(mainpath, fd);
+       close(fd);
+       rc = unlink(mainpath);
+       ASSERTF(rc == 0, "unlink failed for '%s': %s",
+               mainpath, strerror(errno));
+
+       /* Against a pipe */
+       rc = mkfifo(mainpath, 0);
+       ASSERTF(rc == 0, "mkfifo failed for '%s': %s",
+               mainpath, strerror(errno));
+       helper_fid2path(mainpath, -1);
+       rc = unlink(mainpath);
+       ASSERTF(rc == 0, "unlink failed for '%s': %s",
+               mainpath, strerror(errno));
+
+       /* Against a directory */
+       rc = mkdir(mainpath, 0);
+       ASSERTF(rc == 0, "mkdir failed for '%s': %s",
+               mainpath, strerror(errno));
+       helper_fid2path(mainpath, -1);
+       rc = rmdir(mainpath);
+       ASSERTF(rc == 0, "rmdir failed for '%s': %s",
+               mainpath, strerror(errno));
+
+       /* Against a char device. Use same as /dev/null in case things
+        * go wrong. */
+       rc = stat("/dev/null", &statbuf);
+       ASSERTF(rc == 0, "stat failed for /dev/null: %s", strerror(errno));
+       rc = mknod(mainpath, S_IFCHR, statbuf.st_rdev);
+       ASSERTF(rc == 0, "mknod failed for '%s': %s",
+               mainpath, strerror(errno));
+       helper_fid2path(mainpath, -1);
+       rc = unlink(mainpath);
+       ASSERTF(rc == 0, "unlink failed for '%s': %s",
+               mainpath, strerror(errno));
+
+       /* Against a block device device. Reuse same dev. */
+       rc = mknod(mainpath, S_IFBLK, statbuf.st_rdev);
+       ASSERTF(rc == 0, "mknod failed for '%s': %s",
+               mainpath, strerror(errno));
+       helper_fid2path(mainpath, -1);
+       rc = unlink(mainpath);
+       ASSERTF(rc == 0, "unlink failed for '%s': %s",
+               mainpath, strerror(errno));
+
+       /* Against a socket. */
+       rc = mknod(mainpath, S_IFSOCK, (dev_t)0);
+       ASSERTF(rc == 0, "mknod failed for '%s': %s",
+               mainpath, strerror(errno));
+       helper_fid2path(mainpath, -1);
+       rc = unlink(mainpath);
+       ASSERTF(rc == 0, "unlink failed for '%s': %s",
+               mainpath, strerror(errno));
+}
+
+/* Test against deleted files. */
+static void test11(void)
+{
+       int rc;
+       int fd;
+       lustre_fid fid;
+       char fidstr[FID_LEN];
+       char path[PATH_MAX];
+       long long recno;
+       int linkno;
+
+       /* Against a regular file */
+       fd = creat(mainpath, 0);
+       ASSERTF(fd >= 0, "creat failed for '%s': %s",
+               mainpath, strerror(errno));
+       close(fd);
+
+       rc = llapi_path2fid(mainpath, &fid);
+       ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
+               mainpath, strerror(-rc));
+
+       rc = unlink(mainpath);
+       ASSERTF(rc == 0, "unlink failed for '%s': %s",
+               mainpath, strerror(errno));
+
+       snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
+       recno = -1;
+       linkno = 0;
+       rc = llapi_fid2path(lustre_dir, fidstr, path,
+                           sizeof(path), &recno, &linkno);
+       ASSERTF(rc == -ENOENT, "llapi_fid2path failed for fid %s: %s",
+               fidstr, strerror(-rc));
+}
+
+/* Test volatile file. */
+static void test12(void)
+{
+       int rc;
+       int fd;
+       int fd2;
+       int fd3;
+       lustre_fid fid;
+
+       /* Against a volatile file */
+       rc = mkdir(mainpath, 0);
+       ASSERTF(rc == 0, "mkdir failed for '%s': %s",
+               mainpath, strerror(errno));
+       fd = llapi_create_volatile_idx(mainpath, -1, 0600);
+       ASSERTF(fd >= 0, "creat failed for '%s': %s",
+               mainpath, strerror(errno));
+
+       rc = llapi_fd2fid(fd, &fid);
+       ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
+               mainpath, strerror(-rc));
+
+       /* No many ways to test, except to open by fid. */
+       fd2 = llapi_open_by_fid(mainpath, &fid, 0600);
+       ASSERTF(fd2 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
+               PFID(&fid), strerror(errno));
+
+       close(fd);
+
+       /* Check the file can still be opened, since fd2 is not
+        * closed. */
+       fd3 = llapi_open_by_fid(mainpath, &fid, 0600);
+       ASSERTF(fd3 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
+               PFID(&fid), strerror(errno));
+
+       close(fd2);
+       close(fd3);
+
+       /* The volatile file is gone now. */
+       fd = llapi_open_by_fid(mainpath, &fid, 0600);
+       ASSERTF(fd < 0, "llapi_open_by_fid for " DFID_NOBRACE ": %d",
+               PFID(&fid), fd);
+}
+
+/* Test with sub directories */
+static void test20(void)
+{
+       char testpath[PATH_MAX];
+       size_t len;
+       int dir_created = 0;
+       int rc;
+
+       rc = snprintf(testpath, sizeof(testpath), "%s", mainpath);
+       ASSERTF((rc > 0 && rc < sizeof(testpath)),
+               "invalid name for testpath '%s'", mainpath);
+
+       rc = mkdir(testpath, S_IRWXU);
+       ASSERTF(rc == 0, "mkdir failed for '%s': %s",
+               testpath, strerror(errno));
+
+       len = strlen(testpath);
+
+       /* Create subdirectories as long as we can. Each new subdir is
+        * "/x", so we need at least 3 characters left in testpath. */
+       while (len <= sizeof(testpath) - 3) {
+               strncat(testpath, "/x", 2);
+
+               len += 2;
+
+               rc = mkdir(testpath, S_IRWXU);
+               ASSERTF(rc == 0, "mkdir failed for '%s': %s",
+                       testpath, strerror(errno));
+
+               dir_created++;
+
+               helper_fid2path(testpath, -1);
+       }
+
+       /* And test the last one. */
+       helper_fid2path(testpath, -1);
+
+       /* Make sure we have created enough directories. Even with a
+        * reasonably long mountpath, we should have created at least
+        * 2000. */
+       ASSERTF(dir_created >= 2000, "dir_created=%d -- '%s'",
+               dir_created, testpath);
+}
+
+/* Test linkno from fid2path */
+static void test30(void)
+{
+       /* Note that since the links are stored in the extended
+        * attributes, only a few of these will fit (about 150 in this
+        * test). Still, create more than that to ensure the system
+        * doesn't break. See LU-5746. */
+       const int num_links = 1000;
+       struct {
+               char filename[PATH_MAX];
+               bool seen;
+       } links[num_links];
+       char buf[PATH_MAX];
+       char buf2[PATH_MAX];
+       lustre_fid fid;
+       char fidstr[FID_LEN];
+       int rc;
+       int i;
+       int j;
+       int fd;
+       int linkno;
+       bool past_link_limit = false;
+
+       /* Create the containing directory. */
+       rc = mkdir(mainpath, 0);
+       ASSERTF(rc == 0, "mkdir failed for '%s': %s",
+               mainpath, strerror(errno));
+
+       /* Initializes the link array. */
+       for (i = 0; i < num_links; i++) {
+               rc = snprintf(links[i].filename, sizeof(links[i].filename),
+                             "%s/%s/link%04d", lustre_dir, maindir, i);
+
+               ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
+                       "invalid name for link");
+
+               links[i].seen = false;
+       }
+
+       /* Create the original file. */
+       fd = creat(links[0].filename, 0);
+       ASSERTF(fd >= 0, "create failed for '%s': %s",
+               links[0].filename, strerror(errno));
+       close(fd);
+
+       rc = llapi_path2fid(links[0].filename, &fid);
+       ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
+               links[0].filename, strerror(-rc));
+       snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
+
+       /* Create the links */
+       for (i = 1; i < num_links; i++) {
+               rc = link(links[0].filename, links[i].filename);
+               ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
+                       links[0].filename, links[i].filename, strerror(errno));
+       }
+
+       /* Query the links, making sure we got all of them */
+       for (i = 0; i < num_links + 10; i++) {
+               long long recno;
+               bool found;
+
+               /* Without braces */
+               recno = -1;
+               linkno = i;
+               rc = llapi_fid2path(links[0].filename, fidstr, buf,
+                                   sizeof(buf), &recno, &linkno);
+               ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
+                       fidstr, strerror(-rc));
+
+               snprintf(buf2, sizeof(buf2), "%s/%s", fsmountdir, buf);
+
+               if (past_link_limit == false) {
+                       /* Find the name in the links that were created */
+                       found = false;
+                       for (j = 0; j < num_links; j++) {
+                               if (strcmp(buf2, links[j].filename) == 0) {
+                                       ASSERTF(links[j].seen == false,
+                                               "link '%s' already seen",
+                                               links[j].filename);
+                                       links[j].seen = true;
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       ASSERTF(found == true, "link '%s' not found", buf2);
+
+                       if (linkno == i) {
+                               /* The linkno hasn't changed. This
+                                * means it is the last entry
+                                * stored. */
+                               past_link_limit = true;
+
+                               fprintf(stderr,
+                                       "Was able to store %d links in the EA\n",
+                                       i);
+
+                               /* Also assume that some links were
+                                * returned. It's hard to compute the
+                                * exact value. */
+                               ASSERTF(i > 50,
+                                       "not enough links were returned: %d",
+                                       i);
+                       }
+               } else {
+                       /* Past the number of links stored in the EA,
+                        * Lustre will simply return the original
+                        * file. */
+                       ASSERTF(strcmp(buf2, links[0].filename) == 0,
+                                      "unexpected link for record %d: '%s' / '%s'",
+                                      i, buf2, links[0].filename);
+               }
+
+       }
+}
+
+/* Test llapi_fd2parent/llapi_path2parent on mainpath (whatever its
+ * type). mainpath must exist. */
+static void help_test40(void)
+{
+       lustre_fid parent_fid;
+       lustre_fid fid2;
+       char buf[PATH_MAX];
+       int rc;
+
+       /* Successful call */
+       memset(buf, 0x55, sizeof(buf));
+       rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, PATH_MAX);
+       ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
+               mainpath, strerror(errno));
+       ASSERTF(strcmp(buf, maindir) == 0, "paths are different: '%s' / '%s'",
+               buf, maindir);
+
+       /* By construction, mainpath is just under lustre_dir, so we
+        * can check that the parent fid of mainpath is indeed the one
+        * of lustre_dir. */
+       rc = llapi_path2fid(lustre_dir, &fid2);
+       ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
+               lustre_dir, strerror(-rc));
+       ASSERTF(memcmp(&parent_fid, &fid2, sizeof(fid2)) == 0,
+               "fids are different");
+
+       /* Name too short */
+       rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 0);
+       ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
+
+       rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 5);
+       ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
+
+       rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, strlen(maindir));
+       ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
+
+       rc = llapi_path2parent(mainpath, 0, &parent_fid, buf,
+                              strlen(maindir)+1);
+       ASSERTF(rc == 0, "llapi_path2parent failed: %s", strerror(-rc));
+}
+
+static void test40(void)
+{
+       int fd;
+       int rc;
+
+       /* Against a directory. */
+       rc = mkdir(mainpath, 0);
+       ASSERTF(rc == 0, "mkdir failed for '%s': %s",
+               mainpath, strerror(errno));
+       help_test40();
+
+       cleanup();
+
+       /* Against a regular file */
+       fd = creat(mainpath, 0);
+       ASSERTF(fd >= 0, "creat failed for '%s': %s",
+               mainpath, strerror(errno));
+       close(fd);
+}
+
+/* Test LL_IOC_GETPARENT directly */
+static void test41(void)
+{
+       int rc;
+       int fd;
+       int i;
+       union {
+               struct getparent gp;
+               char buf[1024];
+       } u;
+
+       /* Against a regular file */
+       fd = creat(mainpath, 0);
+       ASSERTF(fd >= 0, "creat failed for '%s': %s",
+               mainpath, strerror(errno));
+
+       /* Ask a few times */
+       for (i = 0; i < 256; i++) {
+               memset(u.buf, i, sizeof(u.buf)); /* poison */
+               u.gp.gp_linkno = 0;
+               u.gp.gp_name_size = 100;
+
+               rc = ioctl(fd, LL_IOC_GETPARENT, &u.gp);
+               ASSERTF(rc == 0, "LL_IOC_GETPARENT failed: %s, rc=%d",
+                       strerror(errno), rc);
+               ASSERTF(strcmp(u.gp.gp_name, maindir) == 0,
+                       "strings are different: %zd, %zd",
+                       strlen(u.gp.gp_name), strlen(maindir));
+       }
+
+       close(fd);
+}
+
+/* Test with linkno. Create sub directories, and put a link to the
+ * original file in them. */
+static void test42(void)
+{
+
+       const int num_links = 100;
+       struct {
+               char subdir[PATH_MAX];
+               lustre_fid subdir_fid;
+               char filename[PATH_MAX];
+               bool seen;
+       } links[num_links];
+       char link0[PATH_MAX];
+       char buf[PATH_MAX];
+       int rc;
+       int i;
+       int fd;
+       int linkno;
+       lustre_fid parent_fid;
+
+       /* Create the containing directory. */
+       rc = mkdir(mainpath, 0);
+       ASSERTF(rc == 0, "mkdir failed: for '%s': %s",
+               mainpath, strerror(errno));
+
+       /* Initializes the link array. */
+       for (i = 0; i < num_links; i++) {
+               rc = snprintf(links[i].subdir, sizeof(links[i].subdir),
+                             "%s/sub%04d", mainpath, i);
+               ASSERTF((rc > 0 && rc < sizeof(links[i].subdir)),
+                       "invalid name for subdir");
+
+               rc = snprintf(links[i].filename, sizeof(links[i].filename),
+                             "link%04d", i);
+               ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
+                       "invalid name for link");
+
+               links[i].seen = false;
+       }
+
+       /* Create the subdirectories. */
+       for (i = 0; i < num_links; i++) {
+               rc = mkdir(links[i].subdir, S_IRWXU);
+               ASSERTF(rc == 0, "mkdir failed for '%s': %s",
+                       links[i].subdir, strerror(errno));
+
+               rc = llapi_path2fid(links[i].subdir, &links[i].subdir_fid);
+               ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
+                       links[i].subdir, strerror(-rc));
+       }
+
+       /* Create the original file. */
+       rc = snprintf(link0, sizeof(link0), "%s/%s",
+                     links[0].subdir, links[0].filename);
+       ASSERTF((rc > 0 && rc < sizeof(link0)), "invalid name for file");
+
+       fd = creat(link0, 0);
+       ASSERTF(fd >= 0, "create failed for '%s': %s", link0, strerror(errno));
+       close(fd);
+
+       /* Create the links */
+       for (i = 1; i < num_links; i++) {
+               rc = snprintf(buf, sizeof(buf), "%s/%s",
+                             links[i].subdir, links[i].filename);
+               ASSERTF((rc > 0 && rc < sizeof(buf)),
+                       "invalid name for link %d", i);
+
+               rc = link(link0, buf);
+               ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
+                       link0, buf, strerror(errno));
+       }
+
+       /* Query the links, making sure we got all of them. Do it in
+        * reverse order, just because! */
+       for (linkno = num_links-1; linkno >= 0; linkno--) {
+               bool found;
+
+               rc = llapi_path2parent(link0, linkno, &parent_fid, buf,
+                                      sizeof(buf));
+               ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
+                       link0, strerror(-rc));
+
+               /* Find the name in the links that were created */
+               found = false;
+               for (i = 0; i < num_links; i++) {
+                       if (memcmp(&parent_fid, &links[i].subdir_fid,
+                                  sizeof(parent_fid)) != 0)
+                               continue;
+
+                       ASSERTF(strcmp(links[i].filename, buf) == 0,
+                               "name differ: '%s' / '%s'",
+                               links[i].filename, buf);
+                       ASSERTF(links[i].seen == false,
+                               "link '%s' already seen", links[i].filename);
+                       links[i].seen = true;
+                       found = true;
+                       break;
+               }
+               ASSERTF(found == true, "link '%s' not found", buf);
+       }
+
+       /* check non existent n+1 link */
+       rc = llapi_path2parent(link0, num_links, &parent_fid, buf, sizeof(buf));
+       ASSERTF(rc == -ENODATA, "llapi_path2parent error for '%s': %s",
+               link0, strerror(-rc));
+}
+
+static void usage(char *prog)
+{
+       fprintf(stderr, "Usage: %s [-d lustre_dir]\n", prog);
+       exit(EXIT_FAILURE);
+}
+
+static void process_args(int argc, char *argv[])
+{
+       int c;
+
+       while ((c = getopt(argc, argv, "d:")) != -1) {
+               switch (c) {
+               case 'd':
+                       lustre_dir = optarg;
+                       break;
+               case '?':
+               default:
+                       fprintf(stderr, "Unknown option '%c'\n", optopt);
+                       usage(argv[0]);
+               }
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       char fsname[8];
+       int rc;
+
+       process_args(argc, argv);
+       if (lustre_dir == NULL)
+               lustre_dir = "/mnt/lustre";
+
+       rc = llapi_search_mounts(lustre_dir, 0, fsmountdir, fsname);
+       if (rc != 0) {
+               fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
+                       lustre_dir);
+               return EXIT_FAILURE;
+       }
+
+       /* Play nice with Lustre test scripts. Non-line buffered output
+        * stream under I/O redirection may appear incorrectly. */
+       setvbuf(stdout, NULL, _IOLBF, 0);
+
+       /* Create a test filename and reuse it. Remove possibly old files. */
+       rc = snprintf(mainpath, sizeof(mainpath), "%s/%s", lustre_dir, maindir);
+       ASSERTF((rc > 0 && rc < sizeof(mainpath)), "invalid name for mainpath");
+       cleanup();
+
+       atexit(cleanup);
+
+       PERFORM(test10);
+       PERFORM(test11);
+       PERFORM(test12);
+       PERFORM(test20);
+       PERFORM(test30);
+       PERFORM(test40);
+       PERFORM(test41);
+       PERFORM(test42);
+
+       return EXIT_SUCCESS;
+}
index a45f185..9ac5f84 100644 (file)
@@ -9913,6 +9913,16 @@ test_154f() {
 }
 run_test 154f "get parent fids by reading link ea"
 
+test_154g()
+{
+       [[ $(lustre_version_code $SINGLEMDS) -ge $(version_code 2.6.92) ]] ||
+               { skip "Need MDS version at least 2.6.92"; return 0; }
+
+       mkdir -p $DIR/$tdir
+       llapi_fid_test -d $DIR/$tdir
+}
+run_test 154g "various llapi FID tests"
+
 test_155_small_load() {
     local temp=$TMP/$tfile
     local file=$DIR/$tfile