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.sun.com/software/products/lustre/docs/GPLv2.pdf
20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21 * CA 95054 USA or visit www.sun.com if you need additional information or
28 * Copyright 2014 Cray Inc, all rights reserved.
31 * A few portions are extracted from llapi_layout_test.c
33 * The purpose of this test is to test the llapi fid related function
34 * (fid2path, path2fid, ...)
36 * The program will exit as soon a non zero error code is returned.
45 #include <sys/ioctl.h>
48 #include <lustre/lustreapi.h>
49 #include <lustre/lustre_idl.h>
51 #define ERROR(fmt, ...) \
52 fprintf(stderr, "%s: %s:%d: %s: " fmt "\n", \
53 program_invocation_short_name, __FILE__, __LINE__, \
54 __func__, ## __VA_ARGS__);
56 #define DIE(fmt, ...) \
58 ERROR(fmt, ## __VA_ARGS__); \
62 #define ASSERTF(cond, fmt, ...) \
65 DIE("assertion '%s' failed: "fmt, \
66 #cond, ## __VA_ARGS__); \
69 #define PERFORM(testfn) \
72 fprintf(stderr, "Starting test " #testfn " at %lld\n", \
73 (unsigned long long)time(NULL)); \
75 fprintf(stderr, "Finishing test " #testfn " at %lld\n", \
76 (unsigned long long)time(NULL)); \
80 /* Name of file/directory. Will be set once and will not change. */
81 static char mainpath[PATH_MAX];
82 static const char *maindir = "llapi_fid_test_name_9585766";
84 static char fsmountdir[PATH_MAX]; /* Lustre mountpoint */
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 /* Try fd2fid and check that the result is still the same. */
152 rc = llapi_fd2fid(fd, &fid3);
153 ASSERTF(rc == 0, "llapi_fd2fid failed for '%s': %s",
154 mainpath, strerror(-rc));
156 ASSERTF(memcmp(&fid, &fid3, sizeof(fid)) == 0,
157 "fids are different");
160 /* Pass the result back to fid2path and ensure the fid stays
162 rc = snprintf(path3, sizeof(path3), "%s/%s", fsmountdir, path1);
163 ASSERTF((rc > 0 && rc < sizeof(path3)), "invalid name");
164 rc = llapi_path2fid(path3, &fid2);
165 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
166 path3, strerror(-rc));
167 ASSERTF(memcmp(&fid, &fid2, sizeof(fid)) == 0, "fids are different");
170 /* Test helper_fid2path */
171 static void test10(void)
177 /* Against Lustre root */
178 helper_fid2path(lustre_dir, -1);
180 /* Against a regular file */
181 fd = creat(mainpath, 0);
182 ASSERTF(fd >= 0, "creat failed for '%s': %s",
183 mainpath, strerror(errno));
184 helper_fid2path(mainpath, fd);
186 rc = unlink(mainpath);
187 ASSERTF(rc == 0, "unlink failed for '%s': %s",
188 mainpath, strerror(errno));
191 rc = mkfifo(mainpath, 0);
192 ASSERTF(rc == 0, "mkfifo failed for '%s': %s",
193 mainpath, strerror(errno));
194 helper_fid2path(mainpath, -1);
195 rc = unlink(mainpath);
196 ASSERTF(rc == 0, "unlink failed for '%s': %s",
197 mainpath, strerror(errno));
199 /* Against a directory */
200 rc = mkdir(mainpath, 0);
201 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
202 mainpath, strerror(errno));
203 helper_fid2path(mainpath, -1);
204 rc = rmdir(mainpath);
205 ASSERTF(rc == 0, "rmdir failed for '%s': %s",
206 mainpath, strerror(errno));
208 /* Against a char device. Use same as /dev/null in case things
210 rc = stat("/dev/null", &statbuf);
211 ASSERTF(rc == 0, "stat failed for /dev/null: %s", strerror(errno));
212 rc = mknod(mainpath, S_IFCHR, statbuf.st_rdev);
213 ASSERTF(rc == 0, "mknod failed for '%s': %s",
214 mainpath, strerror(errno));
215 helper_fid2path(mainpath, -1);
216 rc = unlink(mainpath);
217 ASSERTF(rc == 0, "unlink failed for '%s': %s",
218 mainpath, strerror(errno));
220 /* Against a block device device. Reuse same dev. */
221 rc = mknod(mainpath, S_IFBLK, statbuf.st_rdev);
222 ASSERTF(rc == 0, "mknod failed for '%s': %s",
223 mainpath, strerror(errno));
224 helper_fid2path(mainpath, -1);
225 rc = unlink(mainpath);
226 ASSERTF(rc == 0, "unlink failed for '%s': %s",
227 mainpath, strerror(errno));
229 /* Against a socket. */
230 rc = mknod(mainpath, S_IFSOCK, (dev_t)0);
231 ASSERTF(rc == 0, "mknod failed for '%s': %s",
232 mainpath, strerror(errno));
233 helper_fid2path(mainpath, -1);
234 rc = unlink(mainpath);
235 ASSERTF(rc == 0, "unlink failed for '%s': %s",
236 mainpath, strerror(errno));
239 /* Test against deleted files. */
240 static void test11(void)
245 char fidstr[FID_LEN + 1];
250 /* Against a regular file */
251 fd = creat(mainpath, 0);
252 ASSERTF(fd >= 0, "creat failed for '%s': %s",
253 mainpath, strerror(errno));
256 rc = llapi_path2fid(mainpath, &fid);
257 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
258 mainpath, strerror(-rc));
260 rc = unlink(mainpath);
261 ASSERTF(rc == 0, "unlink failed for '%s': %s",
262 mainpath, strerror(errno));
264 snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
267 rc = llapi_fid2path(lustre_dir, fidstr, path,
268 sizeof(path), &recno, &linkno);
269 ASSERTF(rc == -ENOENT, "llapi_fid2path failed for fid %s: %s",
270 fidstr, strerror(-rc));
273 /* Test volatile file. */
274 static void test12(void)
282 /* Against a volatile file */
283 rc = mkdir(mainpath, 0);
284 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
285 mainpath, strerror(errno));
286 fd = llapi_create_volatile_idx(mainpath, -1, 0600);
287 ASSERTF(fd >= 0, "creat failed for '%s': %s",
288 mainpath, strerror(errno));
290 rc = llapi_fd2fid(fd, &fid);
291 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
292 mainpath, strerror(-rc));
294 /* No many ways to test, except to open by fid. */
295 fd2 = llapi_open_by_fid(mainpath, &fid, 0600);
296 ASSERTF(fd2 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
297 PFID(&fid), strerror(errno));
301 /* Check the file can still be opened, since fd2 is not
303 fd3 = llapi_open_by_fid(mainpath, &fid, 0600);
304 ASSERTF(fd3 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
305 PFID(&fid), strerror(errno));
310 /* The volatile file is gone now. */
311 fd = llapi_open_by_fid(mainpath, &fid, 0600);
312 ASSERTF(fd < 0, "llapi_open_by_fid for " DFID_NOBRACE ": %d",
316 /* Test with sub directories */
317 static void test20(void)
319 char testpath[PATH_MAX];
324 rc = snprintf(testpath, sizeof(testpath), "%s", mainpath);
325 ASSERTF((rc > 0 && rc < sizeof(testpath)),
326 "invalid name for testpath '%s'", mainpath);
328 rc = mkdir(testpath, S_IRWXU);
329 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
330 testpath, strerror(errno));
332 len = strlen(testpath);
334 /* Create subdirectories as long as we can. Each new subdir is
335 * "/x", so we need at least 3 characters left in testpath. */
336 while (len <= sizeof(testpath) - 3) {
337 strncat(testpath, "/x", 2);
341 rc = mkdir(testpath, S_IRWXU);
342 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
343 testpath, strerror(errno));
347 helper_fid2path(testpath, -1);
350 /* And test the last one. */
351 helper_fid2path(testpath, -1);
353 /* Make sure we have created enough directories. Even with a
354 * reasonably long mountpath, we should have created at least
356 ASSERTF(dir_created >= 2000, "dir_created=%d -- '%s'",
357 dir_created, testpath);
360 /* Test linkno from fid2path */
361 static void test30(void)
363 /* Note that since the links are stored in the extended
364 * attributes, only a few of these will fit (about 150 in this
365 * test). Still, create more than that to ensure the system
366 * doesn't break. See LU-5746. */
367 const int num_links = 1000;
369 char filename[PATH_MAX];
375 char fidstr[FID_LEN + 1];
381 bool past_link_limit = false;
383 /* Create the containing directory. */
384 rc = mkdir(mainpath, 0);
385 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
386 mainpath, strerror(errno));
388 /* Initializes the link array. */
389 for (i = 0; i < num_links; i++) {
390 rc = snprintf(links[i].filename, sizeof(links[i].filename),
391 "%s/%s/link%04d", lustre_dir, maindir, i);
393 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
394 "invalid name for link");
396 links[i].seen = false;
399 /* Create the original file. */
400 fd = creat(links[0].filename, 0);
401 ASSERTF(fd >= 0, "create failed for '%s': %s",
402 links[0].filename, strerror(errno));
405 rc = llapi_path2fid(links[0].filename, &fid);
406 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
407 links[0].filename, strerror(-rc));
408 snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
410 /* Create the links */
411 for (i = 1; i < num_links; i++) {
412 rc = link(links[0].filename, links[i].filename);
413 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
414 links[0].filename, links[i].filename, strerror(errno));
417 /* Query the links, making sure we got all of them */
418 for (i = 0; i < num_links + 10; i++) {
425 rc = llapi_fid2path(links[0].filename, fidstr, buf,
426 sizeof(buf), &recno, &linkno);
427 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
428 fidstr, strerror(-rc));
430 snprintf(buf2, sizeof(buf2), "%s/%s", fsmountdir, buf);
432 if (past_link_limit == false) {
433 /* Find the name in the links that were created */
435 for (j = 0; j < num_links; j++) {
436 if (strcmp(buf2, links[j].filename) == 0) {
437 ASSERTF(links[j].seen == false,
438 "link '%s' already seen",
440 links[j].seen = true;
445 ASSERTF(found == true, "link '%s' not found", buf2);
448 /* The linkno hasn't changed. This
449 * means it is the last entry
451 past_link_limit = true;
454 "Was able to store %d links in the EA\n",
457 /* Also assume that some links were
458 * returned. It's hard to compute the
461 "not enough links were returned: %d",
465 /* Past the number of links stored in the EA,
466 * Lustre will simply return the original
468 ASSERTF(strcmp(buf2, links[0].filename) == 0,
469 "unexpected link for record %d: '%s' / '%s'",
470 i, buf2, links[0].filename);
476 /* Test llapi_fd2parent/llapi_path2parent on mainpath (whatever its
477 * type). mainpath must exist. */
478 static void help_test40(void)
480 lustre_fid parent_fid;
485 /* Successful call */
486 memset(buf, 0x55, sizeof(buf));
487 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, PATH_MAX);
488 ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
489 mainpath, strerror(errno));
490 ASSERTF(strcmp(buf, maindir) == 0, "paths are different: '%s' / '%s'",
493 /* By construction, mainpath is just under lustre_dir, so we
494 * can check that the parent fid of mainpath is indeed the one
496 rc = llapi_path2fid(lustre_dir, &fid2);
497 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
498 lustre_dir, strerror(-rc));
499 ASSERTF(memcmp(&parent_fid, &fid2, sizeof(fid2)) == 0,
500 "fids are different");
503 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 0);
504 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
506 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 5);
507 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
509 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, strlen(maindir));
510 ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
512 rc = llapi_path2parent(mainpath, 0, &parent_fid, buf,
514 ASSERTF(rc == 0, "llapi_path2parent failed: %s", strerror(-rc));
517 static void test40(void)
522 /* Against a directory. */
523 rc = mkdir(mainpath, 0);
524 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
525 mainpath, strerror(errno));
530 /* Against a regular file */
531 fd = creat(mainpath, 0);
532 ASSERTF(fd >= 0, "creat failed for '%s': %s",
533 mainpath, strerror(errno));
537 /* Test LL_IOC_GETPARENT directly */
538 static void test41(void)
548 /* Against a regular file */
549 fd = creat(mainpath, 0);
550 ASSERTF(fd >= 0, "creat failed for '%s': %s",
551 mainpath, strerror(errno));
553 /* Ask a few times */
554 for (i = 0; i < 256; i++) {
555 memset(u.buf, i, sizeof(u.buf)); /* poison */
557 u.gp.gp_name_size = 100;
559 rc = ioctl(fd, LL_IOC_GETPARENT, &u.gp);
560 ASSERTF(rc == 0, "LL_IOC_GETPARENT failed: %s, rc=%d",
561 strerror(errno), rc);
562 ASSERTF(strcmp(u.gp.gp_name, maindir) == 0,
563 "strings are different: %zd, %zd",
564 strlen(u.gp.gp_name), strlen(maindir));
570 /* Test with linkno. Create sub directories, and put a link to the
571 * original file in them. */
572 static void test42(void)
575 const int num_links = 100;
577 char subdir[PATH_MAX];
578 lustre_fid subdir_fid;
579 char filename[PATH_MAX];
582 char link0[PATH_MAX];
588 lustre_fid parent_fid;
590 /* Create the containing directory. */
591 rc = mkdir(mainpath, 0);
592 ASSERTF(rc == 0, "mkdir failed: for '%s': %s",
593 mainpath, strerror(errno));
595 /* Initializes the link array. */
596 for (i = 0; i < num_links; i++) {
597 rc = snprintf(links[i].subdir, sizeof(links[i].subdir),
598 "%s/sub%04d", mainpath, i);
599 ASSERTF((rc > 0 && rc < sizeof(links[i].subdir)),
600 "invalid name for subdir");
602 rc = snprintf(links[i].filename, sizeof(links[i].filename),
604 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
605 "invalid name for link");
607 links[i].seen = false;
610 /* Create the subdirectories. */
611 for (i = 0; i < num_links; i++) {
612 rc = mkdir(links[i].subdir, S_IRWXU);
613 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
614 links[i].subdir, strerror(errno));
616 rc = llapi_path2fid(links[i].subdir, &links[i].subdir_fid);
617 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
618 links[i].subdir, strerror(-rc));
621 /* Create the original file. */
622 rc = snprintf(link0, sizeof(link0), "%s/%s",
623 links[0].subdir, links[0].filename);
624 ASSERTF((rc > 0 && rc < sizeof(link0)), "invalid name for file");
626 fd = creat(link0, 0);
627 ASSERTF(fd >= 0, "create failed for '%s': %s", link0, strerror(errno));
630 /* Create the links */
631 for (i = 1; i < num_links; i++) {
632 rc = snprintf(buf, sizeof(buf), "%s/%s",
633 links[i].subdir, links[i].filename);
634 ASSERTF((rc > 0 && rc < sizeof(buf)),
635 "invalid name for link %d", i);
637 rc = link(link0, buf);
638 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
639 link0, buf, strerror(errno));
642 /* Query the links, making sure we got all of them. Do it in
643 * reverse order, just because! */
644 for (linkno = num_links-1; linkno >= 0; linkno--) {
647 rc = llapi_path2parent(link0, linkno, &parent_fid, buf,
649 ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
650 link0, strerror(-rc));
652 /* Find the name in the links that were created */
654 for (i = 0; i < num_links; i++) {
655 if (memcmp(&parent_fid, &links[i].subdir_fid,
656 sizeof(parent_fid)) != 0)
659 ASSERTF(strcmp(links[i].filename, buf) == 0,
660 "name differ: '%s' / '%s'",
661 links[i].filename, buf);
662 ASSERTF(links[i].seen == false,
663 "link '%s' already seen", links[i].filename);
664 links[i].seen = true;
668 ASSERTF(found == true, "link '%s' not found", buf);
671 /* check non existent n+1 link */
672 rc = llapi_path2parent(link0, num_links, &parent_fid, buf, sizeof(buf));
673 ASSERTF(rc == -ENODATA, "llapi_path2parent error for '%s': %s",
674 link0, strerror(-rc));
677 static void usage(char *prog)
679 fprintf(stderr, "Usage: %s [-d lustre_dir]\n", prog);
683 static void process_args(int argc, char *argv[])
687 while ((c = getopt(argc, argv, "d:")) != -1) {
694 fprintf(stderr, "Unknown option '%c'\n", optopt);
700 int main(int argc, char *argv[])
705 process_args(argc, argv);
706 if (lustre_dir == NULL)
707 lustre_dir = "/mnt/lustre";
709 rc = llapi_search_mounts(lustre_dir, 0, fsmountdir, fsname);
711 fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
716 /* Play nice with Lustre test scripts. Non-line buffered output
717 * stream under I/O redirection may appear incorrectly. */
718 setvbuf(stdout, NULL, _IOLBF, 0);
720 /* Create a test filename and reuse it. Remove possibly old files. */
721 rc = snprintf(mainpath, sizeof(mainpath), "%s/%s", lustre_dir, maindir);
722 ASSERTF((rc > 0 && rc < sizeof(mainpath)), "invalid name for mainpath");