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 mnt_dir[PATH_MAX]; /* Lustre mountpoint */
84 static int mnt_fd = -1;
85 static char *lustre_dir; /* Test directory inside Lustre */
87 /* Cleanup our test directory. */
88 static void cleanup(void)
93 rc = snprintf(cmd, sizeof(cmd), "rm -rf -- '%s'", mainpath);
94 ASSERTF(rc > 0 && rc < sizeof(cmd),
95 "invalid delete command for path '%s'", mainpath);
97 ASSERTF(rc != -1, "Cannot execute rm command");
98 ASSERTF(WEXITSTATUS(rc) == 0,
99 "rm command returned %d", WEXITSTATUS(rc));
102 /* Helper - call path2fid, fd2fid and fid2path against an existing
104 static void helper_fid2path(const char *filename, int fd)
109 char fidstr[FID_LEN + 1];
110 char path1[PATH_MAX];
111 char path2[PATH_MAX];
112 char path3[PATH_MAX];
119 rc = llapi_path2fid(filename, &fid);
120 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
121 filename, strerror(-rc));
124 snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
127 rc = llapi_fid2path(lustre_dir, fidstr, path1,
128 sizeof(path1), &recno1, &linkno1);
129 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
130 fidstr, strerror(-rc));
132 /* Same with braces */
133 snprintf(fidstr, sizeof(fidstr), DFID, PFID(&fid));
136 rc = llapi_fid2path(lustre_dir, fidstr, path2,
137 sizeof(path2), &recno2, &linkno2);
138 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
139 fidstr, strerror(-rc));
141 /* Make sure both calls to llapi_fid2path returned the same
143 ASSERTF(strcmp(path1, path2) == 0, "paths are different: '%s' / '%s'",
145 ASSERTF(recno1 == recno2, "recnos are different: %lld / %lld",
147 ASSERTF(linkno1 == linkno2, "linknos are different: %d / %d",
150 /* Use llapi_fid2path_at() */
153 rc = llapi_fid2path_at(mnt_fd, &fid, path2, sizeof(path2),
155 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
156 fidstr, strerror(-rc));
158 /* Make sure both calls to llapi_fid2path returned the same
160 ASSERTF(strcmp(path1, path2) == 0, "paths are different: '%s' / '%s'",
162 ASSERTF(recno1 == recno2, "recnos are different: %lld / %lld",
164 ASSERTF(linkno1 == linkno2, "linknos are different: %d / %d",
167 /* Try fd2fid and check that the result is still the same. */
169 rc = llapi_fd2fid(fd, &fid3);
170 ASSERTF(rc == 0, "llapi_fd2fid failed for '%s': %s",
171 mainpath, strerror(-rc));
173 ASSERTF(memcmp(&fid, &fid3, sizeof(fid)) == 0,
174 "fids are different");
177 /* Pass the result back to fid2path and ensure the fid stays
179 rc = snprintf(path3, sizeof(path3), "%s/%s", mnt_dir, path1);
180 ASSERTF((rc > 0 && rc < sizeof(path3)), "invalid name");
181 rc = llapi_path2fid(path3, &fid2);
182 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
183 path3, strerror(-rc));
184 ASSERTF(memcmp(&fid, &fid2, sizeof(fid)) == 0, "fids are different");
187 /* Test helper_fid2path */
188 static void test10(void)
194 /* Against Lustre root */
195 helper_fid2path(lustre_dir, -1);
197 /* Against a regular file */
198 fd = creat(mainpath, 0);
199 ASSERTF(fd >= 0, "creat failed for '%s': %s",
200 mainpath, strerror(errno));
201 helper_fid2path(mainpath, fd);
203 rc = unlink(mainpath);
204 ASSERTF(rc == 0, "unlink failed for '%s': %s",
205 mainpath, strerror(errno));
208 rc = mkfifo(mainpath, 0);
209 ASSERTF(rc == 0, "mkfifo failed for '%s': %s",
210 mainpath, strerror(errno));
211 helper_fid2path(mainpath, -1);
212 rc = unlink(mainpath);
213 ASSERTF(rc == 0, "unlink failed for '%s': %s",
214 mainpath, strerror(errno));
216 /* Against a directory */
217 rc = mkdir(mainpath, 0);
218 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
219 mainpath, strerror(errno));
220 helper_fid2path(mainpath, -1);
221 rc = rmdir(mainpath);
222 ASSERTF(rc == 0, "rmdir failed for '%s': %s",
223 mainpath, strerror(errno));
225 /* Against a char device. Use same as /dev/null in case things
227 rc = stat("/dev/null", &statbuf);
228 ASSERTF(rc == 0, "stat failed for /dev/null: %s", strerror(errno));
229 rc = mknod(mainpath, S_IFCHR, statbuf.st_rdev);
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));
237 /* Against a block device device. Reuse same dev. */
238 rc = mknod(mainpath, S_IFBLK, statbuf.st_rdev);
239 ASSERTF(rc == 0, "mknod failed for '%s': %s",
240 mainpath, strerror(errno));
241 helper_fid2path(mainpath, -1);
242 rc = unlink(mainpath);
243 ASSERTF(rc == 0, "unlink failed for '%s': %s",
244 mainpath, strerror(errno));
246 /* Against a socket. */
247 rc = mknod(mainpath, S_IFSOCK, (dev_t)0);
248 ASSERTF(rc == 0, "mknod failed for '%s': %s",
249 mainpath, strerror(errno));
250 helper_fid2path(mainpath, -1);
251 rc = unlink(mainpath);
252 ASSERTF(rc == 0, "unlink failed for '%s': %s",
253 mainpath, strerror(errno));
256 /* Test against deleted files. */
257 static void test11(void)
262 char fidstr[FID_LEN + 1];
267 /* Against a regular file */
268 fd = creat(mainpath, 0);
269 ASSERTF(fd >= 0, "creat failed for '%s': %s",
270 mainpath, strerror(errno));
273 rc = llapi_path2fid(mainpath, &fid);
274 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
275 mainpath, strerror(-rc));
277 rc = unlink(mainpath);
278 ASSERTF(rc == 0, "unlink failed for '%s': %s",
279 mainpath, strerror(errno));
281 snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
284 rc = llapi_fid2path(lustre_dir, fidstr, path,
285 sizeof(path), &recno, &linkno);
286 ASSERTF(rc == -ENOENT, "llapi_fid2path failed for fid %s: %s",
287 fidstr, strerror(-rc));
290 /* Test volatile file. */
291 static void test12(void)
299 /* Against a volatile file */
300 rc = mkdir(mainpath, 0);
301 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
302 mainpath, strerror(errno));
303 fd = llapi_create_volatile_idx(mainpath, -1, 0);
304 ASSERTF(fd >= 0, "creat failed for '%s': %s",
305 mainpath, strerror(errno));
307 rc = llapi_fd2fid(fd, &fid);
308 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
309 mainpath, strerror(-rc));
311 /* No many ways to test, except to open by fid. */
312 fd2 = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
313 ASSERTF(fd2 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
314 PFID(&fid), strerror(errno));
318 /* Check the file can still be opened, since fd2 is not
320 fd3 = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
321 ASSERTF(fd3 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
322 PFID(&fid), strerror(errno));
327 /* The volatile file is gone now. */
328 fd = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
329 ASSERTF(fd < 0, "llapi_open_by_fid for " DFID_NOBRACE ": %d",
333 /* Test with sub directories */
334 static void test20(void)
336 char testpath[PATH_MAX];
341 rc = snprintf(testpath, sizeof(testpath), "%s", mainpath);
342 ASSERTF((rc > 0 && rc < sizeof(testpath)),
343 "invalid name for testpath '%s'", mainpath);
345 rc = mkdir(testpath, S_IRWXU);
346 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
347 testpath, strerror(errno));
349 len = strlen(testpath);
351 /* Create subdirectories as long as we can. Each new subdir is
352 * "/x", so we need at least 3 characters left in testpath. */
353 while (len <= sizeof(testpath) - 3) {
354 strncat(testpath, "/x", sizeof(testpath) - 1);
358 rc = mkdir(testpath, S_IRWXU);
359 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
360 testpath, strerror(errno));
364 helper_fid2path(testpath, -1);
367 /* And test the last one. */
368 helper_fid2path(testpath, -1);
370 /* Make sure we have created enough directories. Even with a
371 * reasonably long mountpath, we should have created at least
373 ASSERTF(dir_created >= 2000, "dir_created=%d -- '%s'",
374 dir_created, testpath);
377 /* Test linkno from fid2path */
378 static void test30(void)
380 /* Note that since the links are stored in the extended
381 * attributes, only a few of these will fit (about 150 in this
382 * test). Still, create more than that to ensure the system
383 * doesn't break. See LU-5746. */
384 const int num_links = 1000;
386 char filename[PATH_MAX];
390 char buf2[PATH_MAX * 2];
392 char fidstr[FID_LEN + 1];
398 bool past_link_limit = false;
400 /* Create the containing directory. */
401 rc = mkdir(mainpath, 0);
402 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
403 mainpath, strerror(errno));
405 /* Initializes the link array. */
406 for (i = 0; i < num_links; i++) {
407 rc = snprintf(links[i].filename, sizeof(links[i].filename),
408 "%s/%s/link%04d", lustre_dir, maindir, i);
410 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
411 "invalid name for link");
413 links[i].seen = false;
416 /* Create the original file. */
417 fd = creat(links[0].filename, 0);
418 ASSERTF(fd >= 0, "create failed for '%s': %s",
419 links[0].filename, strerror(errno));
422 rc = llapi_path2fid(links[0].filename, &fid);
423 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
424 links[0].filename, strerror(-rc));
425 snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
427 /* Create the links */
428 for (i = 1; i < num_links; i++) {
429 rc = link(links[0].filename, links[i].filename);
430 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
431 links[0].filename, links[i].filename, strerror(errno));
434 /* Query the links, making sure we got all of them */
435 for (i = 0; i < num_links + 10; i++) {
442 rc = llapi_fid2path(links[0].filename, fidstr, buf,
443 sizeof(buf), &recno, &linkno);
444 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
445 fidstr, strerror(-rc));
447 snprintf(buf2, sizeof(buf2), "%s/%s", mnt_dir, buf);
449 if (past_link_limit == false) {
450 /* Find the name in the links that were created */
452 for (j = 0; j < num_links; j++) {
453 if (strcmp(buf2, links[j].filename) == 0) {
454 ASSERTF(links[j].seen == false,
455 "link '%s' already seen",
457 links[j].seen = true;
462 ASSERTF(found == true, "link '%s' not found", buf2);
465 /* The linkno hasn't changed. This
466 * means it is the last entry
468 past_link_limit = true;
471 "Was able to store %d links in the EA\n",
474 /* Also assume that some links were
475 * returned. It's hard to compute the
478 "not enough links were returned: %d",
482 /* Past the number of links stored in the EA,
483 * Lustre will simply return the original
485 ASSERTF(strcmp(buf2, links[0].filename) == 0,
486 "unexpected link for record %d: '%s' / '%s'",
487 i, buf2, links[0].filename);
493 /* Test llapi_fd2parent/llapi_path2parent on mainpath (whatever its
494 * type). mainpath must exist. */
495 static void help_test40(void)
497 struct lu_fid parent_fid;
502 /* Successful call */
503 memset(buf, 0x55, sizeof(buf));
504 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, PATH_MAX);
505 ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
506 mainpath, strerror(errno));
507 ASSERTF(strcmp(buf, maindir) == 0, "paths are different: '%s' / '%s'",
510 /* By construction, mainpath is just under lustre_dir, so we
511 * can check that the parent fid of mainpath is indeed the one
513 rc = llapi_path2fid(lustre_dir, &fid2);
514 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
515 lustre_dir, strerror(-rc));
516 ASSERTF(memcmp(&parent_fid, &fid2, sizeof(fid2)) == 0,
517 "fids are different");
520 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 0);
521 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
523 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 5);
524 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
526 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, strlen(maindir));
527 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
529 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf,
531 ASSERTF(rc == 0, "llapi_path2parent failed: %s", strerror(-rc));
534 static void test40(void)
539 /* Against a directory. */
540 rc = mkdir(mainpath, 0);
541 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
542 mainpath, strerror(errno));
547 /* Against a regular file */
548 fd = creat(mainpath, 0);
549 ASSERTF(fd >= 0, "creat failed for '%s': %s",
550 mainpath, strerror(errno));
554 /* Test LL_IOC_GETPARENT directly */
555 static void test41(void)
565 /* Against a regular file */
566 fd = creat(mainpath, 0);
567 ASSERTF(fd >= 0, "creat failed for '%s': %s",
568 mainpath, strerror(errno));
570 /* Ask a few times */
571 for (i = 0; i < 256; i++) {
572 memset(u.buf, i, sizeof(u.buf)); /* poison */
574 u.gp.gp_name_size = 100;
576 rc = ioctl(fd, LL_IOC_GETPARENT, &u.gp);
577 ASSERTF(rc == 0, "LL_IOC_GETPARENT failed: %s, rc=%d",
578 strerror(errno), rc);
579 ASSERTF(strcmp(u.gp.gp_name, maindir) == 0,
580 "strings are different: %zd, %zd",
581 strlen(u.gp.gp_name), strlen(maindir));
587 /* Test with linkno. Create sub directories, and put a link to the
588 * original file in them. */
589 static void test42(void)
592 const int num_links = 100;
594 char subdir[PATH_MAX];
595 struct lu_fid subdir_fid;
596 char filename[PATH_MAX];
599 char link0[PATH_MAX];
605 struct lu_fid parent_fid;
607 /* Create the containing directory. */
608 rc = mkdir(mainpath, 0);
609 ASSERTF(rc == 0, "mkdir failed: for '%s': %s",
610 mainpath, strerror(errno));
612 /* Initializes the link array. */
613 for (i = 0; i < num_links; i++) {
614 rc = snprintf(links[i].subdir, sizeof(links[i].subdir),
615 "%s/sub%04d", mainpath, i);
616 ASSERTF((rc > 0 && rc < sizeof(links[i].subdir)),
617 "invalid name for subdir");
619 rc = snprintf(links[i].filename, sizeof(links[i].filename),
621 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
622 "invalid name for link");
624 links[i].seen = false;
627 /* Create the subdirectories. */
628 for (i = 0; i < num_links; i++) {
629 rc = mkdir(links[i].subdir, S_IRWXU);
630 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
631 links[i].subdir, strerror(errno));
633 rc = llapi_path2fid(links[i].subdir, &links[i].subdir_fid);
634 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
635 links[i].subdir, strerror(-rc));
638 /* Create the original file. */
639 rc = snprintf(link0, sizeof(link0), "%s/%s",
640 links[0].subdir, links[0].filename);
641 ASSERTF((rc > 0 && rc < sizeof(link0)), "invalid name for file");
643 fd = creat(link0, 0);
644 ASSERTF(fd >= 0, "create failed for '%s': %s", link0, strerror(errno));
647 /* Create the links */
648 for (i = 1; i < num_links; i++) {
649 rc = snprintf(buf, sizeof(buf), "%s/%s",
650 links[i].subdir, links[i].filename);
651 ASSERTF((rc > 0 && rc < sizeof(buf)),
652 "invalid name for link %d", i);
654 rc = link(link0, buf);
655 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
656 link0, buf, strerror(errno));
659 /* Query the links, making sure we got all of them. Do it in
660 * reverse order, just because! */
661 for (linkno = num_links-1; linkno >= 0; linkno--) {
664 rc = llapi_path2parent(link0, linkno, &parent_fid, buf,
666 ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
667 link0, strerror(-rc));
669 /* Find the name in the links that were created */
671 for (i = 0; i < num_links; i++) {
672 if (memcmp(&parent_fid, &links[i].subdir_fid,
673 sizeof(parent_fid)) != 0)
676 ASSERTF(strcmp(links[i].filename, buf) == 0,
677 "name differ: '%s' / '%s'",
678 links[i].filename, buf);
679 ASSERTF(links[i].seen == false,
680 "link '%s' already seen", links[i].filename);
681 links[i].seen = true;
685 ASSERTF(found == true, "link '%s' not found", buf);
688 /* check non existent n+1 link */
689 rc = llapi_path2parent(link0, num_links, &parent_fid, buf, sizeof(buf));
690 ASSERTF(rc == -ENODATA, "llapi_path2parent error for '%s': %s",
691 link0, strerror(-rc));
694 static void usage(char *prog)
696 fprintf(stderr, "Usage: %s [-d lustre_dir]\n", prog);
700 static void process_args(int argc, char *argv[])
704 while ((c = getopt(argc, argv, "d:")) != -1) {
711 fprintf(stderr, "Unknown option '%c'\n", optopt);
717 int main(int argc, char *argv[])
722 process_args(argc, argv);
723 if (lustre_dir == NULL)
724 lustre_dir = "/mnt/lustre";
726 rc = llapi_search_mounts(lustre_dir, 0, mnt_dir, fsname);
728 fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
733 mnt_fd = open(mnt_dir, O_RDONLY|O_DIRECTORY);
734 ASSERTF(!(mnt_fd < 0), "cannot open '%s': %s\n", mnt_dir, strerror(errno));
736 /* Play nice with Lustre test scripts. Non-line buffered output
737 * stream under I/O redirection may appear incorrectly. */
738 setvbuf(stdout, NULL, _IOLBF, 0);
740 /* Create a test filename and reuse it. Remove possibly old files. */
741 rc = snprintf(mainpath, sizeof(mainpath), "%s/%s", lustre_dir, maindir);
742 ASSERTF((rc > 0 && rc < sizeof(mainpath)), "invalid name for mainpath");