Whamcloud - gitweb
ba8cd1ea8369568977c5845146e554e9301724cb
[fs/lustre-release.git] / lustre / tests / llapi_fid_test.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
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.
9  *
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).
15  *
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
19  *
20  * GPL HEADER END
21  */
22
23 /*
24  * Copyright 2014 Cray Inc, all rights reserved.
25  * Author: Frank Zago.
26  *
27  * A few portions are extracted from llapi_layout_test.c
28  *
29  * The purpose of this test is to test the llapi fid related function
30  * (fid2path, path2fid, ...)
31  *
32  * The program will exit as soon a non zero error code is returned.
33  */
34
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <getopt.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <poll.h>
41 #include <sys/ioctl.h>
42 #include <time.h>
43
44 #include <lustre/lustreapi.h>
45 #include <linux/lustre/lustre_idl.h>
46
47 #define ERROR(fmt, ...)                                                 \
48         fprintf(stderr, "%s: %s:%d: %s: " fmt "\n",                     \
49                 program_invocation_short_name, __FILE__, __LINE__,      \
50                 __func__, ## __VA_ARGS__);
51
52 #define DIE(fmt, ...)                  \
53         do {                           \
54                 ERROR(fmt, ## __VA_ARGS__);     \
55                 exit(EXIT_FAILURE);             \
56         } while (0)
57
58 #define ASSERTF(cond, fmt, ...)                                         \
59         do {                                                            \
60                 if (!(cond))                                            \
61                         DIE("assertion '%s' failed: "fmt,               \
62                             #cond, ## __VA_ARGS__);                     \
63         } while (0)
64
65 #define PERFORM(testfn) \
66         do {                                                            \
67                 cleanup();                                              \
68                 fprintf(stderr, "Starting test " #testfn " at %lld\n",  \
69                         (unsigned long long)time(NULL));                \
70                 testfn();                                               \
71                 fprintf(stderr, "Finishing test " #testfn " at %lld\n", \
72                        (unsigned long long)time(NULL));                 \
73                 cleanup();                                              \
74         } while (0)
75
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";
79
80 static char fsmountdir[PATH_MAX];       /* Lustre mountpoint */
81 static char *lustre_dir;                /* Test directory inside Lustre */
82
83 /* Cleanup our test directory. */
84 static void cleanup(void)
85 {
86         char cmd[PATH_MAX];
87         int rc;
88
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);
92         rc = system(cmd);
93         ASSERTF(rc != -1, "Cannot execute rm command");
94         ASSERTF(WEXITSTATUS(rc) == 0,
95                 "rm command returned %d", WEXITSTATUS(rc));
96 }
97
98 /* Helper - call path2fid, fd2fid and fid2path against an existing
99  * file/directory */
100 static void helper_fid2path(const char *filename, int fd)
101 {
102         lustre_fid fid;
103         lustre_fid fid2;
104         lustre_fid fid3;
105         char fidstr[FID_LEN + 1];
106         char path1[PATH_MAX];
107         char path2[PATH_MAX];
108         char path3[PATH_MAX];
109         long long recno1;
110         long long recno2;
111         int linkno1;
112         int linkno2;
113         int rc;
114
115         rc = llapi_path2fid(filename, &fid);
116         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
117                 filename, strerror(-rc));
118
119         /* Without braces */
120         snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
121         recno1 = -1;
122         linkno1 = 0;
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));
127
128         /* Same with braces */
129         snprintf(fidstr, sizeof(fidstr), DFID, PFID(&fid));
130         recno2 = -1;
131         linkno2 = 0;
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));
136
137         /* Make sure both calls to llapi_fid2path returned the same
138          * data. */
139         ASSERTF(strcmp(path1, path2) == 0, "paths are different: '%s' / '%s'",
140                 path1, path2);
141         ASSERTF(recno1 == recno2, "recnos are different: %lld / %lld",
142                 recno1, recno2);
143         ASSERTF(linkno1 == linkno2, "linknos are different: %d / %d",
144                 linkno1, linkno2);
145
146         /* Try fd2fid and check that the result is still the same. */
147         if (fd != -1) {
148                 rc = llapi_fd2fid(fd, &fid3);
149                 ASSERTF(rc == 0, "llapi_fd2fid failed for '%s': %s",
150                         mainpath, strerror(-rc));
151
152                 ASSERTF(memcmp(&fid, &fid3, sizeof(fid)) == 0,
153                         "fids are different");
154         }
155
156         /* Pass the result back to fid2path and ensure the fid stays
157          * the same. */
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");
164 }
165
166 /* Test helper_fid2path */
167 static void test10(void)
168 {
169         int rc;
170         int fd;
171         struct stat statbuf;
172
173         /* Against Lustre root */
174         helper_fid2path(lustre_dir, -1);
175
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);
181         close(fd);
182         rc = unlink(mainpath);
183         ASSERTF(rc == 0, "unlink failed for '%s': %s",
184                 mainpath, strerror(errno));
185
186         /* Against a pipe */
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));
194
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));
203
204         /* Against a char device. Use same as /dev/null in case things
205          * go wrong. */
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));
215
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));
224
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));
233 }
234
235 /* Test against deleted files. */
236 static void test11(void)
237 {
238         int rc;
239         int fd;
240         lustre_fid fid;
241         char fidstr[FID_LEN + 1];
242         char path[PATH_MAX];
243         long long recno;
244         int linkno;
245
246         /* Against a regular file */
247         fd = creat(mainpath, 0);
248         ASSERTF(fd >= 0, "creat failed for '%s': %s",
249                 mainpath, strerror(errno));
250         close(fd);
251
252         rc = llapi_path2fid(mainpath, &fid);
253         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
254                 mainpath, strerror(-rc));
255
256         rc = unlink(mainpath);
257         ASSERTF(rc == 0, "unlink failed for '%s': %s",
258                 mainpath, strerror(errno));
259
260         snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
261         recno = -1;
262         linkno = 0;
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));
267 }
268
269 /* Test volatile file. */
270 static void test12(void)
271 {
272         int rc;
273         int fd;
274         int fd2;
275         int fd3;
276         lustre_fid fid;
277
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));
285
286         rc = llapi_fd2fid(fd, &fid);
287         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
288                 mainpath, strerror(-rc));
289
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));
294
295         close(fd);
296
297         /* Check the file can still be opened, since fd2 is not
298          * closed. */
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));
302
303         close(fd2);
304         close(fd3);
305
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",
309                 PFID(&fid), fd);
310 }
311
312 /* Test with sub directories */
313 static void test20(void)
314 {
315         char testpath[PATH_MAX];
316         size_t len;
317         int dir_created = 0;
318         int rc;
319
320         rc = snprintf(testpath, sizeof(testpath), "%s", mainpath);
321         ASSERTF((rc > 0 && rc < sizeof(testpath)),
322                 "invalid name for testpath '%s'", mainpath);
323
324         rc = mkdir(testpath, S_IRWXU);
325         ASSERTF(rc == 0, "mkdir failed for '%s': %s",
326                 testpath, strerror(errno));
327
328         len = strlen(testpath);
329
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);
334
335                 len += 2;
336
337                 rc = mkdir(testpath, S_IRWXU);
338                 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
339                         testpath, strerror(errno));
340
341                 dir_created++;
342
343                 helper_fid2path(testpath, -1);
344         }
345
346         /* And test the last one. */
347         helper_fid2path(testpath, -1);
348
349         /* Make sure we have created enough directories. Even with a
350          * reasonably long mountpath, we should have created at least
351          * 2000. */
352         ASSERTF(dir_created >= 2000, "dir_created=%d -- '%s'",
353                 dir_created, testpath);
354 }
355
356 /* Test linkno from fid2path */
357 static void test30(void)
358 {
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;
364         struct {
365                 char filename[PATH_MAX];
366                 bool seen;
367         } links[num_links];
368         char buf[PATH_MAX];
369         char buf2[PATH_MAX];
370         lustre_fid fid;
371         char fidstr[FID_LEN + 1];
372         int rc;
373         int i;
374         int j;
375         int fd;
376         int linkno;
377         bool past_link_limit = false;
378
379         /* Create the containing directory. */
380         rc = mkdir(mainpath, 0);
381         ASSERTF(rc == 0, "mkdir failed for '%s': %s",
382                 mainpath, strerror(errno));
383
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);
388
389                 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
390                         "invalid name for link");
391
392                 links[i].seen = false;
393         }
394
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));
399         close(fd);
400
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));
405
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));
411         }
412
413         /* Query the links, making sure we got all of them */
414         for (i = 0; i < num_links + 10; i++) {
415                 long long recno;
416                 bool found;
417
418                 /* Without braces */
419                 recno = -1;
420                 linkno = 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));
425
426                 snprintf(buf2, sizeof(buf2), "%s/%s", fsmountdir, buf);
427
428                 if (past_link_limit == false) {
429                         /* Find the name in the links that were created */
430                         found = false;
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",
435                                                 links[j].filename);
436                                         links[j].seen = true;
437                                         found = true;
438                                         break;
439                                 }
440                         }
441                         ASSERTF(found == true, "link '%s' not found", buf2);
442
443                         if (linkno == i) {
444                                 /* The linkno hasn't changed. This
445                                  * means it is the last entry
446                                  * stored. */
447                                 past_link_limit = true;
448
449                                 fprintf(stderr,
450                                         "Was able to store %d links in the EA\n",
451                                         i);
452
453                                 /* Also assume that some links were
454                                  * returned. It's hard to compute the
455                                  * exact value. */
456                                 ASSERTF(i > 50,
457                                         "not enough links were returned: %d",
458                                         i);
459                         }
460                 } else {
461                         /* Past the number of links stored in the EA,
462                          * Lustre will simply return the original
463                          * file. */
464                         ASSERTF(strcmp(buf2, links[0].filename) == 0,
465                                        "unexpected link for record %d: '%s' / '%s'",
466                                        i, buf2, links[0].filename);
467                 }
468
469         }
470 }
471
472 /* Test llapi_fd2parent/llapi_path2parent on mainpath (whatever its
473  * type). mainpath must exist. */
474 static void help_test40(void)
475 {
476         lustre_fid parent_fid;
477         lustre_fid fid2;
478         char buf[PATH_MAX];
479         int rc;
480
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'",
487                 buf, maindir);
488
489         /* By construction, mainpath is just under lustre_dir, so we
490          * can check that the parent fid of mainpath is indeed the one
491          * of lustre_dir. */
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");
497
498         /* Name too short */
499         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 0);
500         ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
501
502         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 5);
503         ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
504
505         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, strlen(maindir));
506         ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
507
508         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf,
509                                strlen(maindir)+1);
510         ASSERTF(rc == 0, "llapi_path2parent failed: %s", strerror(-rc));
511 }
512
513 static void test40(void)
514 {
515         int fd;
516         int rc;
517
518         /* Against a directory. */
519         rc = mkdir(mainpath, 0);
520         ASSERTF(rc == 0, "mkdir failed for '%s': %s",
521                 mainpath, strerror(errno));
522         help_test40();
523
524         cleanup();
525
526         /* Against a regular file */
527         fd = creat(mainpath, 0);
528         ASSERTF(fd >= 0, "creat failed for '%s': %s",
529                 mainpath, strerror(errno));
530         close(fd);
531 }
532
533 /* Test LL_IOC_GETPARENT directly */
534 static void test41(void)
535 {
536         int rc;
537         int fd;
538         int i;
539         union {
540                 struct getparent gp;
541                 char buf[1024];
542         } u;
543
544         /* Against a regular file */
545         fd = creat(mainpath, 0);
546         ASSERTF(fd >= 0, "creat failed for '%s': %s",
547                 mainpath, strerror(errno));
548
549         /* Ask a few times */
550         for (i = 0; i < 256; i++) {
551                 memset(u.buf, i, sizeof(u.buf)); /* poison */
552                 u.gp.gp_linkno = 0;
553                 u.gp.gp_name_size = 100;
554
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));
561         }
562
563         close(fd);
564 }
565
566 /* Test with linkno. Create sub directories, and put a link to the
567  * original file in them. */
568 static void test42(void)
569 {
570
571         const int num_links = 100;
572         struct {
573                 char subdir[PATH_MAX];
574                 lustre_fid subdir_fid;
575                 char filename[PATH_MAX];
576                 bool seen;
577         } links[num_links];
578         char link0[PATH_MAX];
579         char buf[PATH_MAX];
580         int rc;
581         int i;
582         int fd;
583         int linkno;
584         lustre_fid parent_fid;
585
586         /* Create the containing directory. */
587         rc = mkdir(mainpath, 0);
588         ASSERTF(rc == 0, "mkdir failed: for '%s': %s",
589                 mainpath, strerror(errno));
590
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");
597
598                 rc = snprintf(links[i].filename, sizeof(links[i].filename),
599                               "link%04d", i);
600                 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
601                         "invalid name for link");
602
603                 links[i].seen = false;
604         }
605
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));
611
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));
615         }
616
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");
621
622         fd = creat(link0, 0);
623         ASSERTF(fd >= 0, "create failed for '%s': %s", link0, strerror(errno));
624         close(fd);
625
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);
632
633                 rc = link(link0, buf);
634                 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
635                         link0, buf, strerror(errno));
636         }
637
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--) {
641                 bool found;
642
643                 rc = llapi_path2parent(link0, linkno, &parent_fid, buf,
644                                        sizeof(buf));
645                 ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
646                         link0, strerror(-rc));
647
648                 /* Find the name in the links that were created */
649                 found = false;
650                 for (i = 0; i < num_links; i++) {
651                         if (memcmp(&parent_fid, &links[i].subdir_fid,
652                                    sizeof(parent_fid)) != 0)
653                                 continue;
654
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;
661                         found = true;
662                         break;
663                 }
664                 ASSERTF(found == true, "link '%s' not found", buf);
665         }
666
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));
671 }
672
673 static void usage(char *prog)
674 {
675         fprintf(stderr, "Usage: %s [-d lustre_dir]\n", prog);
676         exit(EXIT_FAILURE);
677 }
678
679 static void process_args(int argc, char *argv[])
680 {
681         int c;
682
683         while ((c = getopt(argc, argv, "d:")) != -1) {
684                 switch (c) {
685                 case 'd':
686                         lustre_dir = optarg;
687                         break;
688                 case '?':
689                 default:
690                         fprintf(stderr, "Unknown option '%c'\n", optopt);
691                         usage(argv[0]);
692                 }
693         }
694 }
695
696 int main(int argc, char *argv[])
697 {
698         char fsname[8 + 1];
699         int rc;
700
701         process_args(argc, argv);
702         if (lustre_dir == NULL)
703                 lustre_dir = "/mnt/lustre";
704
705         rc = llapi_search_mounts(lustre_dir, 0, fsmountdir, fsname);
706         if (rc != 0) {
707                 fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
708                         lustre_dir);
709                 return EXIT_FAILURE;
710         }
711
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);
715
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");
719         cleanup();
720
721         atexit(cleanup);
722
723         PERFORM(test10);
724         PERFORM(test11);
725         PERFORM(test12);
726         PERFORM(test20);
727         PERFORM(test30);
728         PERFORM(test40);
729         PERFORM(test41);
730         PERFORM(test42);
731
732         return EXIT_SUCCESS;
733 }