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.
27 * A few portions are extracted from llapi_layout_test.c
29 * The purpose of this test is to test the llapi fid related function
30 * (fid2path, path2fid, ...)
32 * The program will exit as soon a non zero error code is returned.
41 #include <sys/ioctl.h>
44 #include <lustre/lustreapi.h>
45 #include <lustre/lustre_idl.h>
47 #define ERROR(fmt, ...) \
48 fprintf(stderr, "%s: %s:%d: %s: " fmt "\n", \
49 program_invocation_short_name, __FILE__, __LINE__, \
50 __func__, ## __VA_ARGS__);
52 #define DIE(fmt, ...) \
54 ERROR(fmt, ## __VA_ARGS__); \
58 #define ASSERTF(cond, fmt, ...) \
61 DIE("assertion '%s' failed: "fmt, \
62 #cond, ## __VA_ARGS__); \
65 #define PERFORM(testfn) \
68 fprintf(stderr, "Starting test " #testfn " at %lld\n", \
69 (unsigned long long)time(NULL)); \
71 fprintf(stderr, "Finishing test " #testfn " at %lld\n", \
72 (unsigned long long)time(NULL)); \
76 /* Name of file/directory. Will be set once and will not change. */
77 static char mainpath[PATH_MAX];
78 static const char *maindir = "llapi_fid_test_name_9585766";
80 static char fsmountdir[PATH_MAX]; /* Lustre mountpoint */
81 static char *lustre_dir; /* Test directory inside Lustre */
83 /* Cleanup our test directory. */
84 static void cleanup(void)
89 rc = snprintf(cmd, sizeof(cmd), "rm -rf -- '%s'", mainpath);
90 ASSERTF(rc > 0 && rc < sizeof(cmd),
91 "invalid delete command for path '%s'", mainpath);
93 ASSERTF(rc != -1, "Cannot execute rm command");
94 ASSERTF(WEXITSTATUS(rc) == 0,
95 "rm command returned %d", WEXITSTATUS(rc));
98 /* Helper - call path2fid, fd2fid and fid2path against an existing
100 static void helper_fid2path(const char *filename, int fd)
105 char fidstr[FID_LEN + 1];
106 char path1[PATH_MAX];
107 char path2[PATH_MAX];
108 char path3[PATH_MAX];
115 rc = llapi_path2fid(filename, &fid);
116 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
117 filename, strerror(-rc));
120 snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
123 rc = llapi_fid2path(lustre_dir, fidstr, path1,
124 sizeof(path1), &recno1, &linkno1);
125 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
126 fidstr, strerror(-rc));
128 /* Same with braces */
129 snprintf(fidstr, sizeof(fidstr), DFID, PFID(&fid));
132 rc = llapi_fid2path(lustre_dir, fidstr, path2,
133 sizeof(path2), &recno2, &linkno2);
134 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
135 fidstr, strerror(-rc));
137 /* Make sure both calls to llapi_fid2path returned the same
139 ASSERTF(strcmp(path1, path2) == 0, "paths are different: '%s' / '%s'",
141 ASSERTF(recno1 == recno2, "recnos are different: %lld / %lld",
143 ASSERTF(linkno1 == linkno2, "linknos are different: %d / %d",
146 /* Try fd2fid and check that the result is still the same. */
148 rc = llapi_fd2fid(fd, &fid3);
149 ASSERTF(rc == 0, "llapi_fd2fid failed for '%s': %s",
150 mainpath, strerror(-rc));
152 ASSERTF(memcmp(&fid, &fid3, sizeof(fid)) == 0,
153 "fids are different");
156 /* Pass the result back to fid2path and ensure the fid stays
158 rc = snprintf(path3, sizeof(path3), "%s/%s", fsmountdir, path1);
159 ASSERTF((rc > 0 && rc < sizeof(path3)), "invalid name");
160 rc = llapi_path2fid(path3, &fid2);
161 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
162 path3, strerror(-rc));
163 ASSERTF(memcmp(&fid, &fid2, sizeof(fid)) == 0, "fids are different");
166 /* Test helper_fid2path */
167 static void test10(void)
173 /* Against Lustre root */
174 helper_fid2path(lustre_dir, -1);
176 /* Against a regular file */
177 fd = creat(mainpath, 0);
178 ASSERTF(fd >= 0, "creat failed for '%s': %s",
179 mainpath, strerror(errno));
180 helper_fid2path(mainpath, fd);
182 rc = unlink(mainpath);
183 ASSERTF(rc == 0, "unlink failed for '%s': %s",
184 mainpath, strerror(errno));
187 rc = mkfifo(mainpath, 0);
188 ASSERTF(rc == 0, "mkfifo failed for '%s': %s",
189 mainpath, strerror(errno));
190 helper_fid2path(mainpath, -1);
191 rc = unlink(mainpath);
192 ASSERTF(rc == 0, "unlink failed for '%s': %s",
193 mainpath, strerror(errno));
195 /* Against a directory */
196 rc = mkdir(mainpath, 0);
197 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
198 mainpath, strerror(errno));
199 helper_fid2path(mainpath, -1);
200 rc = rmdir(mainpath);
201 ASSERTF(rc == 0, "rmdir failed for '%s': %s",
202 mainpath, strerror(errno));
204 /* Against a char device. Use same as /dev/null in case things
206 rc = stat("/dev/null", &statbuf);
207 ASSERTF(rc == 0, "stat failed for /dev/null: %s", strerror(errno));
208 rc = mknod(mainpath, S_IFCHR, statbuf.st_rdev);
209 ASSERTF(rc == 0, "mknod 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 block device device. Reuse same dev. */
217 rc = mknod(mainpath, S_IFBLK, statbuf.st_rdev);
218 ASSERTF(rc == 0, "mknod failed for '%s': %s",
219 mainpath, strerror(errno));
220 helper_fid2path(mainpath, -1);
221 rc = unlink(mainpath);
222 ASSERTF(rc == 0, "unlink failed for '%s': %s",
223 mainpath, strerror(errno));
225 /* Against a socket. */
226 rc = mknod(mainpath, S_IFSOCK, (dev_t)0);
227 ASSERTF(rc == 0, "mknod failed for '%s': %s",
228 mainpath, strerror(errno));
229 helper_fid2path(mainpath, -1);
230 rc = unlink(mainpath);
231 ASSERTF(rc == 0, "unlink failed for '%s': %s",
232 mainpath, strerror(errno));
235 /* Test against deleted files. */
236 static void test11(void)
241 char fidstr[FID_LEN + 1];
246 /* Against a regular file */
247 fd = creat(mainpath, 0);
248 ASSERTF(fd >= 0, "creat failed for '%s': %s",
249 mainpath, strerror(errno));
252 rc = llapi_path2fid(mainpath, &fid);
253 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
254 mainpath, strerror(-rc));
256 rc = unlink(mainpath);
257 ASSERTF(rc == 0, "unlink failed for '%s': %s",
258 mainpath, strerror(errno));
260 snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
263 rc = llapi_fid2path(lustre_dir, fidstr, path,
264 sizeof(path), &recno, &linkno);
265 ASSERTF(rc == -ENOENT, "llapi_fid2path failed for fid %s: %s",
266 fidstr, strerror(-rc));
269 /* Test volatile file. */
270 static void test12(void)
278 /* Against a volatile file */
279 rc = mkdir(mainpath, 0);
280 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
281 mainpath, strerror(errno));
282 fd = llapi_create_volatile_idx(mainpath, -1, 0);
283 ASSERTF(fd >= 0, "creat failed for '%s': %s",
284 mainpath, strerror(errno));
286 rc = llapi_fd2fid(fd, &fid);
287 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
288 mainpath, strerror(-rc));
290 /* No many ways to test, except to open by fid. */
291 fd2 = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
292 ASSERTF(fd2 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
293 PFID(&fid), strerror(errno));
297 /* Check the file can still be opened, since fd2 is not
299 fd3 = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
300 ASSERTF(fd3 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
301 PFID(&fid), strerror(errno));
306 /* The volatile file is gone now. */
307 fd = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
308 ASSERTF(fd < 0, "llapi_open_by_fid for " DFID_NOBRACE ": %d",
312 /* Test with sub directories */
313 static void test20(void)
315 char testpath[PATH_MAX];
320 rc = snprintf(testpath, sizeof(testpath), "%s", mainpath);
321 ASSERTF((rc > 0 && rc < sizeof(testpath)),
322 "invalid name for testpath '%s'", mainpath);
324 rc = mkdir(testpath, S_IRWXU);
325 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
326 testpath, strerror(errno));
328 len = strlen(testpath);
330 /* Create subdirectories as long as we can. Each new subdir is
331 * "/x", so we need at least 3 characters left in testpath. */
332 while (len <= sizeof(testpath) - 3) {
333 strncat(testpath, "/x", 2);
337 rc = mkdir(testpath, S_IRWXU);
338 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
339 testpath, strerror(errno));
343 helper_fid2path(testpath, -1);
346 /* And test the last one. */
347 helper_fid2path(testpath, -1);
349 /* Make sure we have created enough directories. Even with a
350 * reasonably long mountpath, we should have created at least
352 ASSERTF(dir_created >= 2000, "dir_created=%d -- '%s'",
353 dir_created, testpath);
356 /* Test linkno from fid2path */
357 static void test30(void)
359 /* Note that since the links are stored in the extended
360 * attributes, only a few of these will fit (about 150 in this
361 * test). Still, create more than that to ensure the system
362 * doesn't break. See LU-5746. */
363 const int num_links = 1000;
365 char filename[PATH_MAX];
371 char fidstr[FID_LEN + 1];
377 bool past_link_limit = false;
379 /* Create the containing directory. */
380 rc = mkdir(mainpath, 0);
381 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
382 mainpath, strerror(errno));
384 /* Initializes the link array. */
385 for (i = 0; i < num_links; i++) {
386 rc = snprintf(links[i].filename, sizeof(links[i].filename),
387 "%s/%s/link%04d", lustre_dir, maindir, i);
389 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
390 "invalid name for link");
392 links[i].seen = false;
395 /* Create the original file. */
396 fd = creat(links[0].filename, 0);
397 ASSERTF(fd >= 0, "create failed for '%s': %s",
398 links[0].filename, strerror(errno));
401 rc = llapi_path2fid(links[0].filename, &fid);
402 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
403 links[0].filename, strerror(-rc));
404 snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
406 /* Create the links */
407 for (i = 1; i < num_links; i++) {
408 rc = link(links[0].filename, links[i].filename);
409 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
410 links[0].filename, links[i].filename, strerror(errno));
413 /* Query the links, making sure we got all of them */
414 for (i = 0; i < num_links + 10; i++) {
421 rc = llapi_fid2path(links[0].filename, fidstr, buf,
422 sizeof(buf), &recno, &linkno);
423 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
424 fidstr, strerror(-rc));
426 snprintf(buf2, sizeof(buf2), "%s/%s", fsmountdir, buf);
428 if (past_link_limit == false) {
429 /* Find the name in the links that were created */
431 for (j = 0; j < num_links; j++) {
432 if (strcmp(buf2, links[j].filename) == 0) {
433 ASSERTF(links[j].seen == false,
434 "link '%s' already seen",
436 links[j].seen = true;
441 ASSERTF(found == true, "link '%s' not found", buf2);
444 /* The linkno hasn't changed. This
445 * means it is the last entry
447 past_link_limit = true;
450 "Was able to store %d links in the EA\n",
453 /* Also assume that some links were
454 * returned. It's hard to compute the
457 "not enough links were returned: %d",
461 /* Past the number of links stored in the EA,
462 * Lustre will simply return the original
464 ASSERTF(strcmp(buf2, links[0].filename) == 0,
465 "unexpected link for record %d: '%s' / '%s'",
466 i, buf2, links[0].filename);
472 /* Test llapi_fd2parent/llapi_path2parent on mainpath (whatever its
473 * type). mainpath must exist. */
474 static void help_test40(void)
476 lustre_fid parent_fid;
481 /* Successful call */
482 memset(buf, 0x55, sizeof(buf));
483 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, PATH_MAX);
484 ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
485 mainpath, strerror(errno));
486 ASSERTF(strcmp(buf, maindir) == 0, "paths are different: '%s' / '%s'",
489 /* By construction, mainpath is just under lustre_dir, so we
490 * can check that the parent fid of mainpath is indeed the one
492 rc = llapi_path2fid(lustre_dir, &fid2);
493 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
494 lustre_dir, strerror(-rc));
495 ASSERTF(memcmp(&parent_fid, &fid2, sizeof(fid2)) == 0,
496 "fids are different");
499 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 0);
500 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
502 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 5);
503 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
505 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, strlen(maindir));
506 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
508 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf,
510 ASSERTF(rc == 0, "llapi_path2parent failed: %s", strerror(-rc));
513 static void test40(void)
518 /* Against a directory. */
519 rc = mkdir(mainpath, 0);
520 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
521 mainpath, strerror(errno));
526 /* Against a regular file */
527 fd = creat(mainpath, 0);
528 ASSERTF(fd >= 0, "creat failed for '%s': %s",
529 mainpath, strerror(errno));
533 /* Test LL_IOC_GETPARENT directly */
534 static void test41(void)
544 /* Against a regular file */
545 fd = creat(mainpath, 0);
546 ASSERTF(fd >= 0, "creat failed for '%s': %s",
547 mainpath, strerror(errno));
549 /* Ask a few times */
550 for (i = 0; i < 256; i++) {
551 memset(u.buf, i, sizeof(u.buf)); /* poison */
553 u.gp.gp_name_size = 100;
555 rc = ioctl(fd, LL_IOC_GETPARENT, &u.gp);
556 ASSERTF(rc == 0, "LL_IOC_GETPARENT failed: %s, rc=%d",
557 strerror(errno), rc);
558 ASSERTF(strcmp(u.gp.gp_name, maindir) == 0,
559 "strings are different: %zd, %zd",
560 strlen(u.gp.gp_name), strlen(maindir));
566 /* Test with linkno. Create sub directories, and put a link to the
567 * original file in them. */
568 static void test42(void)
571 const int num_links = 100;
573 char subdir[PATH_MAX];
574 lustre_fid subdir_fid;
575 char filename[PATH_MAX];
578 char link0[PATH_MAX];
584 lustre_fid parent_fid;
586 /* Create the containing directory. */
587 rc = mkdir(mainpath, 0);
588 ASSERTF(rc == 0, "mkdir failed: for '%s': %s",
589 mainpath, strerror(errno));
591 /* Initializes the link array. */
592 for (i = 0; i < num_links; i++) {
593 rc = snprintf(links[i].subdir, sizeof(links[i].subdir),
594 "%s/sub%04d", mainpath, i);
595 ASSERTF((rc > 0 && rc < sizeof(links[i].subdir)),
596 "invalid name for subdir");
598 rc = snprintf(links[i].filename, sizeof(links[i].filename),
600 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
601 "invalid name for link");
603 links[i].seen = false;
606 /* Create the subdirectories. */
607 for (i = 0; i < num_links; i++) {
608 rc = mkdir(links[i].subdir, S_IRWXU);
609 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
610 links[i].subdir, strerror(errno));
612 rc = llapi_path2fid(links[i].subdir, &links[i].subdir_fid);
613 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
614 links[i].subdir, strerror(-rc));
617 /* Create the original file. */
618 rc = snprintf(link0, sizeof(link0), "%s/%s",
619 links[0].subdir, links[0].filename);
620 ASSERTF((rc > 0 && rc < sizeof(link0)), "invalid name for file");
622 fd = creat(link0, 0);
623 ASSERTF(fd >= 0, "create failed for '%s': %s", link0, strerror(errno));
626 /* Create the links */
627 for (i = 1; i < num_links; i++) {
628 rc = snprintf(buf, sizeof(buf), "%s/%s",
629 links[i].subdir, links[i].filename);
630 ASSERTF((rc > 0 && rc < sizeof(buf)),
631 "invalid name for link %d", i);
633 rc = link(link0, buf);
634 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
635 link0, buf, strerror(errno));
638 /* Query the links, making sure we got all of them. Do it in
639 * reverse order, just because! */
640 for (linkno = num_links-1; linkno >= 0; linkno--) {
643 rc = llapi_path2parent(link0, linkno, &parent_fid, buf,
645 ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
646 link0, strerror(-rc));
648 /* Find the name in the links that were created */
650 for (i = 0; i < num_links; i++) {
651 if (memcmp(&parent_fid, &links[i].subdir_fid,
652 sizeof(parent_fid)) != 0)
655 ASSERTF(strcmp(links[i].filename, buf) == 0,
656 "name differ: '%s' / '%s'",
657 links[i].filename, buf);
658 ASSERTF(links[i].seen == false,
659 "link '%s' already seen", links[i].filename);
660 links[i].seen = true;
664 ASSERTF(found == true, "link '%s' not found", buf);
667 /* check non existent n+1 link */
668 rc = llapi_path2parent(link0, num_links, &parent_fid, buf, sizeof(buf));
669 ASSERTF(rc == -ENODATA, "llapi_path2parent error for '%s': %s",
670 link0, strerror(-rc));
673 static void usage(char *prog)
675 fprintf(stderr, "Usage: %s [-d lustre_dir]\n", prog);
679 static void process_args(int argc, char *argv[])
683 while ((c = getopt(argc, argv, "d:")) != -1) {
690 fprintf(stderr, "Unknown option '%c'\n", optopt);
696 int main(int argc, char *argv[])
701 process_args(argc, argv);
702 if (lustre_dir == NULL)
703 lustre_dir = "/mnt/lustre";
705 rc = llapi_search_mounts(lustre_dir, 0, fsmountdir, fsname);
707 fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
712 /* Play nice with Lustre test scripts. Non-line buffered output
713 * stream under I/O redirection may appear incorrectly. */
714 setvbuf(stdout, NULL, _IOLBF, 0);
716 /* Create a test filename and reuse it. Remove possibly old files. */
717 rc = snprintf(mainpath, sizeof(mainpath), "%s/%s", lustre_dir, maindir);
718 ASSERTF((rc > 0 && rc < sizeof(mainpath)), "invalid name for mainpath");