Whamcloud - gitweb
LU-6913 test: fix conf-sanity test_30b defect
[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.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
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
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26
27 /*
28  * Copyright 2014 Cray Inc, all rights reserved.
29  * Author: Frank Zago.
30  *
31  * A few portions are extracted from llapi_layout_test.c
32  *
33  * The purpose of this test is to test the llapi fid related function
34  * (fid2path, path2fid, ...)
35  *
36  * The program will exit as soon a non zero error code is returned.
37  */
38
39 #include <stdlib.h>
40 #include <errno.h>
41 #include <getopt.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include <poll.h>
45 #include <time.h>
46
47 #include <lustre/lustreapi.h>
48 #include <lustre/lustre_idl.h>
49
50 #define ERROR(fmt, ...)                                                 \
51         fprintf(stderr, "%s: %s:%d: %s: " fmt "\n",                     \
52                 program_invocation_short_name, __FILE__, __LINE__,      \
53                 __func__, ## __VA_ARGS__);
54
55 #define DIE(fmt, ...)                  \
56         do {                           \
57                 ERROR(fmt, ## __VA_ARGS__);     \
58                 exit(EXIT_FAILURE);             \
59         } while (0)
60
61 #define ASSERTF(cond, fmt, ...)                                         \
62         do {                                                            \
63                 if (!(cond))                                            \
64                         DIE("assertion '%s' failed: "fmt,               \
65                             #cond, ## __VA_ARGS__);                     \
66         } while (0)
67
68 #define PERFORM(testfn) \
69         do {                                                            \
70                 cleanup();                                              \
71                 fprintf(stderr, "Starting test " #testfn " at %lld\n",  \
72                         (unsigned long long)time(NULL));                \
73                 testfn();                                               \
74                 fprintf(stderr, "Finishing test " #testfn " at %lld\n", \
75                        (unsigned long long)time(NULL));                 \
76                 cleanup();                                              \
77         } while (0)
78
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";
82
83 static char fsmountdir[PATH_MAX];       /* Lustre mountpoint */
84 static char *lustre_dir;                /* Test directory inside Lustre */
85
86 /* Cleanup our test directory. */
87 static void cleanup(void)
88 {
89         char cmd[PATH_MAX];
90         int rc;
91
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);
95         rc = system(cmd);
96         ASSERTF(rc != -1, "Cannot execute rm command");
97         ASSERTF(WEXITSTATUS(rc) == 0,
98                 "rm command returned %d", WEXITSTATUS(rc));
99 }
100
101 /* Helper - call path2fid, fd2fid and fid2path against an existing
102  * file/directory */
103 static void helper_fid2path(const char *filename, int fd)
104 {
105         lustre_fid fid;
106         lustre_fid fid2;
107         lustre_fid fid3;
108         char fidstr[FID_LEN + 1];
109         char path1[PATH_MAX];
110         char path2[PATH_MAX];
111         char path3[PATH_MAX];
112         long long recno1;
113         long long recno2;
114         int linkno1;
115         int linkno2;
116         int rc;
117
118         rc = llapi_path2fid(filename, &fid);
119         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
120                 filename, strerror(-rc));
121
122         /* Without braces */
123         snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
124         recno1 = -1;
125         linkno1 = 0;
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));
130
131         /* Same with braces */
132         snprintf(fidstr, sizeof(fidstr), DFID, PFID(&fid));
133         recno2 = -1;
134         linkno2 = 0;
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));
139
140         /* Make sure both calls to llapi_fid2path returned the same
141          * data. */
142         ASSERTF(strcmp(path1, path2) == 0, "paths are different: '%s' / '%s'",
143                 path1, path2);
144         ASSERTF(recno1 == recno2, "recnos are different: %lld / %lld",
145                 recno1, recno2);
146         ASSERTF(linkno1 == linkno2, "linknos are different: %d / %d",
147                 linkno1, linkno2);
148
149         /* Try fd2fid and check that the result is still the same. */
150         if (fd != -1) {
151                 rc = llapi_fd2fid(fd, &fid3);
152                 ASSERTF(rc == 0, "llapi_fd2fid failed for '%s': %s",
153                         mainpath, strerror(-rc));
154
155                 ASSERTF(memcmp(&fid, &fid3, sizeof(fid)) == 0,
156                         "fids are different");
157         }
158
159         /* Pass the result back to fid2path and ensure the fid stays
160          * the same. */
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");
167 }
168
169 /* Test helper_fid2path */
170 static void test10(void)
171 {
172         int rc;
173         int fd;
174         struct stat statbuf;
175
176         /* Against Lustre root */
177         helper_fid2path(lustre_dir, -1);
178
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);
184         close(fd);
185         rc = unlink(mainpath);
186         ASSERTF(rc == 0, "unlink failed for '%s': %s",
187                 mainpath, strerror(errno));
188
189         /* Against a pipe */
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));
197
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));
206
207         /* Against a char device. Use same as /dev/null in case things
208          * go wrong. */
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));
218
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));
227
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));
236 }
237
238 /* Test against deleted files. */
239 static void test11(void)
240 {
241         int rc;
242         int fd;
243         lustre_fid fid;
244         char fidstr[FID_LEN + 1];
245         char path[PATH_MAX];
246         long long recno;
247         int linkno;
248
249         /* Against a regular file */
250         fd = creat(mainpath, 0);
251         ASSERTF(fd >= 0, "creat failed for '%s': %s",
252                 mainpath, strerror(errno));
253         close(fd);
254
255         rc = llapi_path2fid(mainpath, &fid);
256         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
257                 mainpath, strerror(-rc));
258
259         rc = unlink(mainpath);
260         ASSERTF(rc == 0, "unlink failed for '%s': %s",
261                 mainpath, strerror(errno));
262
263         snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
264         recno = -1;
265         linkno = 0;
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));
270 }
271
272 /* Test volatile file. */
273 static void test12(void)
274 {
275         int rc;
276         int fd;
277         int fd2;
278         int fd3;
279         lustre_fid fid;
280
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, 0600);
286         ASSERTF(fd >= 0, "creat failed for '%s': %s",
287                 mainpath, strerror(errno));
288
289         rc = llapi_fd2fid(fd, &fid);
290         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
291                 mainpath, strerror(-rc));
292
293         /* No many ways to test, except to open by fid. */
294         fd2 = llapi_open_by_fid(mainpath, &fid, 0600);
295         ASSERTF(fd2 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
296                 PFID(&fid), strerror(errno));
297
298         close(fd);
299
300         /* Check the file can still be opened, since fd2 is not
301          * closed. */
302         fd3 = llapi_open_by_fid(mainpath, &fid, 0600);
303         ASSERTF(fd3 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
304                 PFID(&fid), strerror(errno));
305
306         close(fd2);
307         close(fd3);
308
309         /* The volatile file is gone now. */
310         fd = llapi_open_by_fid(mainpath, &fid, 0600);
311         ASSERTF(fd < 0, "llapi_open_by_fid for " DFID_NOBRACE ": %d",
312                 PFID(&fid), fd);
313 }
314
315 /* Test with sub directories */
316 static void test20(void)
317 {
318         char testpath[PATH_MAX];
319         size_t len;
320         int dir_created = 0;
321         int rc;
322
323         rc = snprintf(testpath, sizeof(testpath), "%s", mainpath);
324         ASSERTF((rc > 0 && rc < sizeof(testpath)),
325                 "invalid name for testpath '%s'", mainpath);
326
327         rc = mkdir(testpath, S_IRWXU);
328         ASSERTF(rc == 0, "mkdir failed for '%s': %s",
329                 testpath, strerror(errno));
330
331         len = strlen(testpath);
332
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", 2);
337
338                 len += 2;
339
340                 rc = mkdir(testpath, S_IRWXU);
341                 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
342                         testpath, strerror(errno));
343
344                 dir_created++;
345
346                 helper_fid2path(testpath, -1);
347         }
348
349         /* And test the last one. */
350         helper_fid2path(testpath, -1);
351
352         /* Make sure we have created enough directories. Even with a
353          * reasonably long mountpath, we should have created at least
354          * 2000. */
355         ASSERTF(dir_created >= 2000, "dir_created=%d -- '%s'",
356                 dir_created, testpath);
357 }
358
359 /* Test linkno from fid2path */
360 static void test30(void)
361 {
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;
367         struct {
368                 char filename[PATH_MAX];
369                 bool seen;
370         } links[num_links];
371         char buf[PATH_MAX];
372         char buf2[PATH_MAX];
373         lustre_fid fid;
374         char fidstr[FID_LEN + 1];
375         int rc;
376         int i;
377         int j;
378         int fd;
379         int linkno;
380         bool past_link_limit = false;
381
382         /* Create the containing directory. */
383         rc = mkdir(mainpath, 0);
384         ASSERTF(rc == 0, "mkdir failed for '%s': %s",
385                 mainpath, strerror(errno));
386
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);
391
392                 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
393                         "invalid name for link");
394
395                 links[i].seen = false;
396         }
397
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));
402         close(fd);
403
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));
408
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));
414         }
415
416         /* Query the links, making sure we got all of them */
417         for (i = 0; i < num_links + 10; i++) {
418                 long long recno;
419                 bool found;
420
421                 /* Without braces */
422                 recno = -1;
423                 linkno = 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));
428
429                 snprintf(buf2, sizeof(buf2), "%s/%s", fsmountdir, buf);
430
431                 if (past_link_limit == false) {
432                         /* Find the name in the links that were created */
433                         found = false;
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",
438                                                 links[j].filename);
439                                         links[j].seen = true;
440                                         found = true;
441                                         break;
442                                 }
443                         }
444                         ASSERTF(found == true, "link '%s' not found", buf2);
445
446                         if (linkno == i) {
447                                 /* The linkno hasn't changed. This
448                                  * means it is the last entry
449                                  * stored. */
450                                 past_link_limit = true;
451
452                                 fprintf(stderr,
453                                         "Was able to store %d links in the EA\n",
454                                         i);
455
456                                 /* Also assume that some links were
457                                  * returned. It's hard to compute the
458                                  * exact value. */
459                                 ASSERTF(i > 50,
460                                         "not enough links were returned: %d",
461                                         i);
462                         }
463                 } else {
464                         /* Past the number of links stored in the EA,
465                          * Lustre will simply return the original
466                          * file. */
467                         ASSERTF(strcmp(buf2, links[0].filename) == 0,
468                                        "unexpected link for record %d: '%s' / '%s'",
469                                        i, buf2, links[0].filename);
470                 }
471
472         }
473 }
474
475 /* Test llapi_fd2parent/llapi_path2parent on mainpath (whatever its
476  * type). mainpath must exist. */
477 static void help_test40(void)
478 {
479         lustre_fid parent_fid;
480         lustre_fid fid2;
481         char buf[PATH_MAX];
482         int rc;
483
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'",
490                 buf, maindir);
491
492         /* By construction, mainpath is just under lustre_dir, so we
493          * can check that the parent fid of mainpath is indeed the one
494          * of lustre_dir. */
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");
500
501         /* Name too short */
502         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 0);
503         ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
504
505         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 5);
506         ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
507
508         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, strlen(maindir));
509         ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
510
511         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf,
512                                strlen(maindir)+1);
513         ASSERTF(rc == 0, "llapi_path2parent failed: %s", strerror(-rc));
514 }
515
516 static void test40(void)
517 {
518         int fd;
519         int rc;
520
521         /* Against a directory. */
522         rc = mkdir(mainpath, 0);
523         ASSERTF(rc == 0, "mkdir failed for '%s': %s",
524                 mainpath, strerror(errno));
525         help_test40();
526
527         cleanup();
528
529         /* Against a regular file */
530         fd = creat(mainpath, 0);
531         ASSERTF(fd >= 0, "creat failed for '%s': %s",
532                 mainpath, strerror(errno));
533         close(fd);
534 }
535
536 /* Test LL_IOC_GETPARENT directly */
537 static void test41(void)
538 {
539         int rc;
540         int fd;
541         int i;
542         union {
543                 struct getparent gp;
544                 char buf[1024];
545         } u;
546
547         /* Against a regular file */
548         fd = creat(mainpath, 0);
549         ASSERTF(fd >= 0, "creat failed for '%s': %s",
550                 mainpath, strerror(errno));
551
552         /* Ask a few times */
553         for (i = 0; i < 256; i++) {
554                 memset(u.buf, i, sizeof(u.buf)); /* poison */
555                 u.gp.gp_linkno = 0;
556                 u.gp.gp_name_size = 100;
557
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));
564         }
565
566         close(fd);
567 }
568
569 /* Test with linkno. Create sub directories, and put a link to the
570  * original file in them. */
571 static void test42(void)
572 {
573
574         const int num_links = 100;
575         struct {
576                 char subdir[PATH_MAX];
577                 lustre_fid subdir_fid;
578                 char filename[PATH_MAX];
579                 bool seen;
580         } links[num_links];
581         char link0[PATH_MAX];
582         char buf[PATH_MAX];
583         int rc;
584         int i;
585         int fd;
586         int linkno;
587         lustre_fid parent_fid;
588
589         /* Create the containing directory. */
590         rc = mkdir(mainpath, 0);
591         ASSERTF(rc == 0, "mkdir failed: for '%s': %s",
592                 mainpath, strerror(errno));
593
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");
600
601                 rc = snprintf(links[i].filename, sizeof(links[i].filename),
602                               "link%04d", i);
603                 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
604                         "invalid name for link");
605
606                 links[i].seen = false;
607         }
608
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));
614
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));
618         }
619
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");
624
625         fd = creat(link0, 0);
626         ASSERTF(fd >= 0, "create failed for '%s': %s", link0, strerror(errno));
627         close(fd);
628
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);
635
636                 rc = link(link0, buf);
637                 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
638                         link0, buf, strerror(errno));
639         }
640
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--) {
644                 bool found;
645
646                 rc = llapi_path2parent(link0, linkno, &parent_fid, buf,
647                                        sizeof(buf));
648                 ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
649                         link0, strerror(-rc));
650
651                 /* Find the name in the links that were created */
652                 found = false;
653                 for (i = 0; i < num_links; i++) {
654                         if (memcmp(&parent_fid, &links[i].subdir_fid,
655                                    sizeof(parent_fid)) != 0)
656                                 continue;
657
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;
664                         found = true;
665                         break;
666                 }
667                 ASSERTF(found == true, "link '%s' not found", buf);
668         }
669
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));
674 }
675
676 static void usage(char *prog)
677 {
678         fprintf(stderr, "Usage: %s [-d lustre_dir]\n", prog);
679         exit(EXIT_FAILURE);
680 }
681
682 static void process_args(int argc, char *argv[])
683 {
684         int c;
685
686         while ((c = getopt(argc, argv, "d:")) != -1) {
687                 switch (c) {
688                 case 'd':
689                         lustre_dir = optarg;
690                         break;
691                 case '?':
692                 default:
693                         fprintf(stderr, "Unknown option '%c'\n", optopt);
694                         usage(argv[0]);
695                 }
696         }
697 }
698
699 int main(int argc, char *argv[])
700 {
701         char fsname[8 + 1];
702         int rc;
703
704         process_args(argc, argv);
705         if (lustre_dir == NULL)
706                 lustre_dir = "/mnt/lustre";
707
708         rc = llapi_search_mounts(lustre_dir, 0, fsmountdir, fsname);
709         if (rc != 0) {
710                 fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
711                         lustre_dir);
712                 return EXIT_FAILURE;
713         }
714
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);
718
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");
722         cleanup();
723
724         atexit(cleanup);
725
726         PERFORM(test10);
727         PERFORM(test11);
728         PERFORM(test12);
729         PERFORM(test20);
730         PERFORM(test30);
731         PERFORM(test40);
732         PERFORM(test41);
733         PERFORM(test42);
734
735         return EXIT_SUCCESS;
736 }