4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.gnu.org/licenses/gpl-2.0.html
24 * Copyright 2014 Cray Inc, all rights reserved.
26 * Copyright (c) 2016, Intel Corporation.
30 * A few portions are extracted from llapi_layout_test.c
32 * The purpose of this test is to test the llapi fid related function
33 * (fid2path, path2fid, ...)
35 * The program will exit as soon a non zero error code is returned.
44 #include <sys/ioctl.h>
47 #include <lustre/lustreapi.h>
48 #include <linux/lustre/lustre_idl.h>
50 #define ERROR(fmt, ...) \
51 fprintf(stderr, "%s: %s:%d: %s: " fmt "\n", \
52 program_invocation_short_name, __FILE__, __LINE__, \
53 __func__, ## __VA_ARGS__);
55 #define DIE(fmt, ...) \
57 ERROR(fmt, ## __VA_ARGS__); \
61 #define ASSERTF(cond, fmt, ...) \
64 DIE("assertion '%s' failed: "fmt, \
65 #cond, ## __VA_ARGS__); \
68 #define PERFORM(testfn) \
71 fprintf(stderr, "Starting test " #testfn " at %lld\n", \
72 (unsigned long long)time(NULL)); \
74 fprintf(stderr, "Finishing test " #testfn " at %lld\n", \
75 (unsigned long long)time(NULL)); \
79 /* Name of file/directory. Will be set once and will not change. */
80 static char mainpath[PATH_MAX];
81 static const char *maindir = "llapi_fid_test_name_9585766";
83 static char fsmountdir[PATH_MAX]; /* Lustre mountpoint */
84 static char *lustre_dir; /* Test directory inside Lustre */
86 /* Cleanup our test directory. */
87 static void cleanup(void)
92 rc = snprintf(cmd, sizeof(cmd), "rm -rf -- '%s'", mainpath);
93 ASSERTF(rc > 0 && rc < sizeof(cmd),
94 "invalid delete command for path '%s'", mainpath);
96 ASSERTF(rc != -1, "Cannot execute rm command");
97 ASSERTF(WEXITSTATUS(rc) == 0,
98 "rm command returned %d", WEXITSTATUS(rc));
101 /* Helper - call path2fid, fd2fid and fid2path against an existing
103 static void helper_fid2path(const char *filename, int fd)
108 char fidstr[FID_LEN + 1];
109 char path1[PATH_MAX];
110 char path2[PATH_MAX];
111 char path3[PATH_MAX];
118 rc = llapi_path2fid(filename, &fid);
119 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
120 filename, strerror(-rc));
123 snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
126 rc = llapi_fid2path(lustre_dir, fidstr, path1,
127 sizeof(path1), &recno1, &linkno1);
128 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
129 fidstr, strerror(-rc));
131 /* Same with braces */
132 snprintf(fidstr, sizeof(fidstr), DFID, PFID(&fid));
135 rc = llapi_fid2path(lustre_dir, fidstr, path2,
136 sizeof(path2), &recno2, &linkno2);
137 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
138 fidstr, strerror(-rc));
140 /* Make sure both calls to llapi_fid2path returned the same
142 ASSERTF(strcmp(path1, path2) == 0, "paths are different: '%s' / '%s'",
144 ASSERTF(recno1 == recno2, "recnos are different: %lld / %lld",
146 ASSERTF(linkno1 == linkno2, "linknos are different: %d / %d",
149 /* Try fd2fid and check that the result is still the same. */
151 rc = llapi_fd2fid(fd, &fid3);
152 ASSERTF(rc == 0, "llapi_fd2fid failed for '%s': %s",
153 mainpath, strerror(-rc));
155 ASSERTF(memcmp(&fid, &fid3, sizeof(fid)) == 0,
156 "fids are different");
159 /* Pass the result back to fid2path and ensure the fid stays
161 rc = snprintf(path3, sizeof(path3), "%s/%s", fsmountdir, path1);
162 ASSERTF((rc > 0 && rc < sizeof(path3)), "invalid name");
163 rc = llapi_path2fid(path3, &fid2);
164 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
165 path3, strerror(-rc));
166 ASSERTF(memcmp(&fid, &fid2, sizeof(fid)) == 0, "fids are different");
169 /* Test helper_fid2path */
170 static void test10(void)
176 /* Against Lustre root */
177 helper_fid2path(lustre_dir, -1);
179 /* Against a regular file */
180 fd = creat(mainpath, 0);
181 ASSERTF(fd >= 0, "creat failed for '%s': %s",
182 mainpath, strerror(errno));
183 helper_fid2path(mainpath, fd);
185 rc = unlink(mainpath);
186 ASSERTF(rc == 0, "unlink failed for '%s': %s",
187 mainpath, strerror(errno));
190 rc = mkfifo(mainpath, 0);
191 ASSERTF(rc == 0, "mkfifo failed for '%s': %s",
192 mainpath, strerror(errno));
193 helper_fid2path(mainpath, -1);
194 rc = unlink(mainpath);
195 ASSERTF(rc == 0, "unlink failed for '%s': %s",
196 mainpath, strerror(errno));
198 /* Against a directory */
199 rc = mkdir(mainpath, 0);
200 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
201 mainpath, strerror(errno));
202 helper_fid2path(mainpath, -1);
203 rc = rmdir(mainpath);
204 ASSERTF(rc == 0, "rmdir failed for '%s': %s",
205 mainpath, strerror(errno));
207 /* Against a char device. Use same as /dev/null in case things
209 rc = stat("/dev/null", &statbuf);
210 ASSERTF(rc == 0, "stat failed for /dev/null: %s", strerror(errno));
211 rc = mknod(mainpath, S_IFCHR, statbuf.st_rdev);
212 ASSERTF(rc == 0, "mknod failed for '%s': %s",
213 mainpath, strerror(errno));
214 helper_fid2path(mainpath, -1);
215 rc = unlink(mainpath);
216 ASSERTF(rc == 0, "unlink failed for '%s': %s",
217 mainpath, strerror(errno));
219 /* Against a block device device. Reuse same dev. */
220 rc = mknod(mainpath, S_IFBLK, statbuf.st_rdev);
221 ASSERTF(rc == 0, "mknod failed for '%s': %s",
222 mainpath, strerror(errno));
223 helper_fid2path(mainpath, -1);
224 rc = unlink(mainpath);
225 ASSERTF(rc == 0, "unlink failed for '%s': %s",
226 mainpath, strerror(errno));
228 /* Against a socket. */
229 rc = mknod(mainpath, S_IFSOCK, (dev_t)0);
230 ASSERTF(rc == 0, "mknod failed for '%s': %s",
231 mainpath, strerror(errno));
232 helper_fid2path(mainpath, -1);
233 rc = unlink(mainpath);
234 ASSERTF(rc == 0, "unlink failed for '%s': %s",
235 mainpath, strerror(errno));
238 /* Test against deleted files. */
239 static void test11(void)
244 char fidstr[FID_LEN + 1];
249 /* Against a regular file */
250 fd = creat(mainpath, 0);
251 ASSERTF(fd >= 0, "creat failed for '%s': %s",
252 mainpath, strerror(errno));
255 rc = llapi_path2fid(mainpath, &fid);
256 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
257 mainpath, strerror(-rc));
259 rc = unlink(mainpath);
260 ASSERTF(rc == 0, "unlink failed for '%s': %s",
261 mainpath, strerror(errno));
263 snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
266 rc = llapi_fid2path(lustre_dir, fidstr, path,
267 sizeof(path), &recno, &linkno);
268 ASSERTF(rc == -ENOENT, "llapi_fid2path failed for fid %s: %s",
269 fidstr, strerror(-rc));
272 /* Test volatile file. */
273 static void test12(void)
281 /* Against a volatile file */
282 rc = mkdir(mainpath, 0);
283 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
284 mainpath, strerror(errno));
285 fd = llapi_create_volatile_idx(mainpath, -1, 0);
286 ASSERTF(fd >= 0, "creat failed for '%s': %s",
287 mainpath, strerror(errno));
289 rc = llapi_fd2fid(fd, &fid);
290 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
291 mainpath, strerror(-rc));
293 /* No many ways to test, except to open by fid. */
294 fd2 = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
295 ASSERTF(fd2 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
296 PFID(&fid), strerror(errno));
300 /* Check the file can still be opened, since fd2 is not
302 fd3 = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
303 ASSERTF(fd3 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
304 PFID(&fid), strerror(errno));
309 /* The volatile file is gone now. */
310 fd = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
311 ASSERTF(fd < 0, "llapi_open_by_fid for " DFID_NOBRACE ": %d",
315 /* Test with sub directories */
316 static void test20(void)
318 char testpath[PATH_MAX];
323 rc = snprintf(testpath, sizeof(testpath), "%s", mainpath);
324 ASSERTF((rc > 0 && rc < sizeof(testpath)),
325 "invalid name for testpath '%s'", mainpath);
327 rc = mkdir(testpath, S_IRWXU);
328 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
329 testpath, strerror(errno));
331 len = strlen(testpath);
333 /* Create subdirectories as long as we can. Each new subdir is
334 * "/x", so we need at least 3 characters left in testpath. */
335 while (len <= sizeof(testpath) - 3) {
336 strncat(testpath, "/x", sizeof(testpath) - 1);
340 rc = mkdir(testpath, S_IRWXU);
341 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
342 testpath, strerror(errno));
346 helper_fid2path(testpath, -1);
349 /* And test the last one. */
350 helper_fid2path(testpath, -1);
352 /* Make sure we have created enough directories. Even with a
353 * reasonably long mountpath, we should have created at least
355 ASSERTF(dir_created >= 2000, "dir_created=%d -- '%s'",
356 dir_created, testpath);
359 /* Test linkno from fid2path */
360 static void test30(void)
362 /* Note that since the links are stored in the extended
363 * attributes, only a few of these will fit (about 150 in this
364 * test). Still, create more than that to ensure the system
365 * doesn't break. See LU-5746. */
366 const int num_links = 1000;
368 char filename[PATH_MAX];
372 char buf2[PATH_MAX * 2];
374 char fidstr[FID_LEN + 1];
380 bool past_link_limit = false;
382 /* Create the containing directory. */
383 rc = mkdir(mainpath, 0);
384 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
385 mainpath, strerror(errno));
387 /* Initializes the link array. */
388 for (i = 0; i < num_links; i++) {
389 rc = snprintf(links[i].filename, sizeof(links[i].filename),
390 "%s/%s/link%04d", lustre_dir, maindir, i);
392 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
393 "invalid name for link");
395 links[i].seen = false;
398 /* Create the original file. */
399 fd = creat(links[0].filename, 0);
400 ASSERTF(fd >= 0, "create failed for '%s': %s",
401 links[0].filename, strerror(errno));
404 rc = llapi_path2fid(links[0].filename, &fid);
405 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
406 links[0].filename, strerror(-rc));
407 snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
409 /* Create the links */
410 for (i = 1; i < num_links; i++) {
411 rc = link(links[0].filename, links[i].filename);
412 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
413 links[0].filename, links[i].filename, strerror(errno));
416 /* Query the links, making sure we got all of them */
417 for (i = 0; i < num_links + 10; i++) {
424 rc = llapi_fid2path(links[0].filename, fidstr, buf,
425 sizeof(buf), &recno, &linkno);
426 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
427 fidstr, strerror(-rc));
429 snprintf(buf2, sizeof(buf2), "%s/%s", fsmountdir, buf);
431 if (past_link_limit == false) {
432 /* Find the name in the links that were created */
434 for (j = 0; j < num_links; j++) {
435 if (strcmp(buf2, links[j].filename) == 0) {
436 ASSERTF(links[j].seen == false,
437 "link '%s' already seen",
439 links[j].seen = true;
444 ASSERTF(found == true, "link '%s' not found", buf2);
447 /* The linkno hasn't changed. This
448 * means it is the last entry
450 past_link_limit = true;
453 "Was able to store %d links in the EA\n",
456 /* Also assume that some links were
457 * returned. It's hard to compute the
460 "not enough links were returned: %d",
464 /* Past the number of links stored in the EA,
465 * Lustre will simply return the original
467 ASSERTF(strcmp(buf2, links[0].filename) == 0,
468 "unexpected link for record %d: '%s' / '%s'",
469 i, buf2, links[0].filename);
475 /* Test llapi_fd2parent/llapi_path2parent on mainpath (whatever its
476 * type). mainpath must exist. */
477 static void help_test40(void)
479 struct lu_fid parent_fid;
484 /* Successful call */
485 memset(buf, 0x55, sizeof(buf));
486 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, PATH_MAX);
487 ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
488 mainpath, strerror(errno));
489 ASSERTF(strcmp(buf, maindir) == 0, "paths are different: '%s' / '%s'",
492 /* By construction, mainpath is just under lustre_dir, so we
493 * can check that the parent fid of mainpath is indeed the one
495 rc = llapi_path2fid(lustre_dir, &fid2);
496 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
497 lustre_dir, strerror(-rc));
498 ASSERTF(memcmp(&parent_fid, &fid2, sizeof(fid2)) == 0,
499 "fids are different");
502 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 0);
503 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
505 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 5);
506 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
508 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, strlen(maindir));
509 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
511 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf,
513 ASSERTF(rc == 0, "llapi_path2parent failed: %s", strerror(-rc));
516 static void test40(void)
521 /* Against a directory. */
522 rc = mkdir(mainpath, 0);
523 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
524 mainpath, strerror(errno));
529 /* Against a regular file */
530 fd = creat(mainpath, 0);
531 ASSERTF(fd >= 0, "creat failed for '%s': %s",
532 mainpath, strerror(errno));
536 /* Test LL_IOC_GETPARENT directly */
537 static void test41(void)
547 /* Against a regular file */
548 fd = creat(mainpath, 0);
549 ASSERTF(fd >= 0, "creat failed for '%s': %s",
550 mainpath, strerror(errno));
552 /* Ask a few times */
553 for (i = 0; i < 256; i++) {
554 memset(u.buf, i, sizeof(u.buf)); /* poison */
556 u.gp.gp_name_size = 100;
558 rc = ioctl(fd, LL_IOC_GETPARENT, &u.gp);
559 ASSERTF(rc == 0, "LL_IOC_GETPARENT failed: %s, rc=%d",
560 strerror(errno), rc);
561 ASSERTF(strcmp(u.gp.gp_name, maindir) == 0,
562 "strings are different: %zd, %zd",
563 strlen(u.gp.gp_name), strlen(maindir));
569 /* Test with linkno. Create sub directories, and put a link to the
570 * original file in them. */
571 static void test42(void)
574 const int num_links = 100;
576 char subdir[PATH_MAX];
577 struct lu_fid subdir_fid;
578 char filename[PATH_MAX];
581 char link0[PATH_MAX];
587 struct lu_fid parent_fid;
589 /* Create the containing directory. */
590 rc = mkdir(mainpath, 0);
591 ASSERTF(rc == 0, "mkdir failed: for '%s': %s",
592 mainpath, strerror(errno));
594 /* Initializes the link array. */
595 for (i = 0; i < num_links; i++) {
596 rc = snprintf(links[i].subdir, sizeof(links[i].subdir),
597 "%s/sub%04d", mainpath, i);
598 ASSERTF((rc > 0 && rc < sizeof(links[i].subdir)),
599 "invalid name for subdir");
601 rc = snprintf(links[i].filename, sizeof(links[i].filename),
603 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
604 "invalid name for link");
606 links[i].seen = false;
609 /* Create the subdirectories. */
610 for (i = 0; i < num_links; i++) {
611 rc = mkdir(links[i].subdir, S_IRWXU);
612 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
613 links[i].subdir, strerror(errno));
615 rc = llapi_path2fid(links[i].subdir, &links[i].subdir_fid);
616 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
617 links[i].subdir, strerror(-rc));
620 /* Create the original file. */
621 rc = snprintf(link0, sizeof(link0), "%s/%s",
622 links[0].subdir, links[0].filename);
623 ASSERTF((rc > 0 && rc < sizeof(link0)), "invalid name for file");
625 fd = creat(link0, 0);
626 ASSERTF(fd >= 0, "create failed for '%s': %s", link0, strerror(errno));
629 /* Create the links */
630 for (i = 1; i < num_links; i++) {
631 rc = snprintf(buf, sizeof(buf), "%s/%s",
632 links[i].subdir, links[i].filename);
633 ASSERTF((rc > 0 && rc < sizeof(buf)),
634 "invalid name for link %d", i);
636 rc = link(link0, buf);
637 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
638 link0, buf, strerror(errno));
641 /* Query the links, making sure we got all of them. Do it in
642 * reverse order, just because! */
643 for (linkno = num_links-1; linkno >= 0; linkno--) {
646 rc = llapi_path2parent(link0, linkno, &parent_fid, buf,
648 ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
649 link0, strerror(-rc));
651 /* Find the name in the links that were created */
653 for (i = 0; i < num_links; i++) {
654 if (memcmp(&parent_fid, &links[i].subdir_fid,
655 sizeof(parent_fid)) != 0)
658 ASSERTF(strcmp(links[i].filename, buf) == 0,
659 "name differ: '%s' / '%s'",
660 links[i].filename, buf);
661 ASSERTF(links[i].seen == false,
662 "link '%s' already seen", links[i].filename);
663 links[i].seen = true;
667 ASSERTF(found == true, "link '%s' not found", buf);
670 /* check non existent n+1 link */
671 rc = llapi_path2parent(link0, num_links, &parent_fid, buf, sizeof(buf));
672 ASSERTF(rc == -ENODATA, "llapi_path2parent error for '%s': %s",
673 link0, strerror(-rc));
676 static void usage(char *prog)
678 fprintf(stderr, "Usage: %s [-d lustre_dir]\n", prog);
682 static void process_args(int argc, char *argv[])
686 while ((c = getopt(argc, argv, "d:")) != -1) {
693 fprintf(stderr, "Unknown option '%c'\n", optopt);
699 int main(int argc, char *argv[])
704 process_args(argc, argv);
705 if (lustre_dir == NULL)
706 lustre_dir = "/mnt/lustre";
708 rc = llapi_search_mounts(lustre_dir, 0, fsmountdir, fsname);
710 fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
715 /* Play nice with Lustre test scripts. Non-line buffered output
716 * stream under I/O redirection may appear incorrectly. */
717 setvbuf(stdout, NULL, _IOLBF, 0);
719 /* Create a test filename and reuse it. Remove possibly old files. */
720 rc = snprintf(mainpath, sizeof(mainpath), "%s/%s", lustre_dir, maindir);
721 ASSERTF((rc > 0 && rc < sizeof(mainpath)), "invalid name for mainpath");