Whamcloud - gitweb
LU-17895 lnet: Validate input for lnetctl import
[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  *
26  * Copyright (c) 2016, Intel Corporation.
27  *
28  * Author: Frank Zago.
29  *
30  * A few portions are extracted from llapi_layout_test.c
31  *
32  * The purpose of this test is to test the llapi fid related function
33  * (fid2path, path2fid, ...)
34  *
35  * The program will exit as soon a non zero error code is returned.
36  */
37
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <getopt.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <poll.h>
44 #include <sys/ioctl.h>
45 #include <time.h>
46
47 #include <lustre/lustreapi.h>
48 #include <linux/lustre/lustre_fid.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 mnt_dir[PATH_MAX];  /* Lustre mountpoint */
84 static int mnt_fd = -1;
85 static char *lustre_dir;                /* Test directory inside Lustre */
86
87 /* Cleanup our test directory. */
88 static void cleanup(void)
89 {
90         char cmd[PATH_MAX];
91         int rc;
92
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);
96         rc = system(cmd);
97         ASSERTF(rc != -1, "Cannot execute rm command");
98         ASSERTF(WEXITSTATUS(rc) == 0,
99                 "rm command returned %d", WEXITSTATUS(rc));
100 }
101
102 /* Helper - call path2fid, fd2fid and fid2path against an existing
103  * file/directory
104  */
105 static void helper_fid2path(const char *filename, int fd)
106 {
107         struct lu_fid fid;
108         struct lu_fid fid2;
109         struct lu_fid fid3;
110         char fidstr[FID_LEN + 1];
111         char path1[PATH_MAX];
112         char path2[PATH_MAX];
113         char path3[PATH_MAX];
114         long long recno1;
115         long long recno2;
116         int linkno1;
117         int linkno2;
118         int rc;
119
120         rc = llapi_path2fid(filename, &fid);
121         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
122                 filename, strerror(-rc));
123
124         /* Without braces */
125         snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
126         recno1 = -1;
127         linkno1 = 0;
128         rc = llapi_fid2path(lustre_dir, fidstr, path1,
129                             sizeof(path1), &recno1, &linkno1);
130         ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
131                 fidstr, strerror(-rc));
132
133         /* Same with braces */
134         snprintf(fidstr, sizeof(fidstr), DFID, PFID(&fid));
135         recno2 = -1;
136         linkno2 = 0;
137         rc = llapi_fid2path(lustre_dir, fidstr, path2,
138                             sizeof(path2), &recno2, &linkno2);
139         ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
140                 fidstr, strerror(-rc));
141
142         /* Make sure both calls to llapi_fid2path returned the same data. */
143         ASSERTF(strcmp(path1, path2) == 0, "paths are different: '%s' / '%s'",
144                 path1, path2);
145         ASSERTF(recno1 == recno2, "recnos are different: %lld / %lld",
146                 recno1, recno2);
147         ASSERTF(linkno1 == linkno2, "linknos are different: %d / %d",
148                 linkno1, linkno2);
149
150         /* Use llapi_fid2path_at() */
151         recno2 = -1;
152         linkno2 = 0;
153         rc = llapi_fid2path_at(mnt_fd, &fid, path2, sizeof(path2),
154                                &recno2, &linkno2);
155         ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
156                 fidstr, strerror(-rc));
157
158         /* Make sure both calls to llapi_fid2path returned the same data. */
159         ASSERTF(strcmp(path1, path2) == 0, "paths are different: '%s' / '%s'",
160                 path1, path2);
161         ASSERTF(recno1 == recno2, "recnos are different: %lld / %lld",
162                 recno1, recno2);
163         ASSERTF(linkno1 == linkno2, "linknos are different: %d / %d",
164                 linkno1, linkno2);
165
166         /* Try fd2fid and check that the result is still the same. */
167         if (fd != -1) {
168                 rc = llapi_fd2fid(fd, &fid3);
169                 ASSERTF(rc == 0, "llapi_fd2fid failed for '%s': %s",
170                         mainpath, strerror(-rc));
171
172                 ASSERTF(memcmp(&fid, &fid3, sizeof(fid)) == 0,
173                         "fids are different");
174         }
175
176         /* Pass result back to fid2path and ensure the fid stays the same. */
177         rc = snprintf(path3, sizeof(path3), "%s/%s", mnt_dir, path1);
178         ASSERTF((rc > 0 && rc < sizeof(path3)), "invalid name");
179         rc = llapi_path2fid(path3, &fid2);
180         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
181                 path3, strerror(-rc));
182         ASSERTF(memcmp(&fid, &fid2, sizeof(fid)) == 0, "fids are different");
183 }
184
185 /* Test helper_fid2path */
186 static void test10(void)
187 {
188         int rc;
189         int fd;
190         struct stat statbuf;
191
192         /* Against Lustre root */
193         helper_fid2path(lustre_dir, -1);
194
195         /* Against a regular file */
196         fd = creat(mainpath, 0);
197         ASSERTF(fd >= 0, "creat failed for '%s': %s",
198                 mainpath, strerror(errno));
199         helper_fid2path(mainpath, fd);
200         close(fd);
201         rc = unlink(mainpath);
202         ASSERTF(rc == 0, "unlink failed for '%s': %s",
203                 mainpath, strerror(errno));
204
205         /* Against a pipe */
206         rc = mkfifo(mainpath, 0);
207         ASSERTF(rc == 0, "mkfifo failed for '%s': %s",
208                 mainpath, strerror(errno));
209         helper_fid2path(mainpath, -1);
210         rc = unlink(mainpath);
211         ASSERTF(rc == 0, "unlink failed for '%s': %s",
212                 mainpath, strerror(errno));
213
214         /* Against a directory */
215         rc = mkdir(mainpath, 0);
216         ASSERTF(rc == 0, "mkdir failed for '%s': %s",
217                 mainpath, strerror(errno));
218         helper_fid2path(mainpath, -1);
219         rc = rmdir(mainpath);
220         ASSERTF(rc == 0, "rmdir failed for '%s': %s",
221                 mainpath, strerror(errno));
222
223         /* Against a char device. Use same as /dev/null in case things
224          * go wrong.
225          */
226         rc = stat("/dev/null", &statbuf);
227         ASSERTF(rc == 0, "stat failed for /dev/null: %s", strerror(errno));
228         rc = mknod(mainpath, S_IFCHR, statbuf.st_rdev);
229         ASSERTF(rc == 0, "mknod failed for '%s': %s",
230                 mainpath, strerror(errno));
231         helper_fid2path(mainpath, -1);
232         rc = unlink(mainpath);
233         ASSERTF(rc == 0, "unlink failed for '%s': %s",
234                 mainpath, strerror(errno));
235
236         /* Against a block device device. Reuse same dev. */
237         rc = mknod(mainpath, S_IFBLK, statbuf.st_rdev);
238         ASSERTF(rc == 0, "mknod failed for '%s': %s",
239                 mainpath, strerror(errno));
240         helper_fid2path(mainpath, -1);
241         rc = unlink(mainpath);
242         ASSERTF(rc == 0, "unlink failed for '%s': %s",
243                 mainpath, strerror(errno));
244
245         /* Against a socket. */
246         rc = mknod(mainpath, S_IFSOCK, (dev_t)0);
247         ASSERTF(rc == 0, "mknod failed for '%s': %s",
248                 mainpath, strerror(errno));
249         helper_fid2path(mainpath, -1);
250         rc = unlink(mainpath);
251         ASSERTF(rc == 0, "unlink failed for '%s': %s",
252                 mainpath, strerror(errno));
253 }
254
255 /* Test against deleted files. */
256 static void test11(void)
257 {
258         int rc;
259         int fd;
260         struct lu_fid fid;
261         char fidstr[FID_LEN + 1];
262         char path[PATH_MAX];
263         long long recno;
264         int linkno;
265
266         /* Against a regular file */
267         fd = creat(mainpath, 0);
268         ASSERTF(fd >= 0, "creat failed for '%s': %s",
269                 mainpath, strerror(errno));
270         close(fd);
271
272         rc = llapi_path2fid(mainpath, &fid);
273         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
274                 mainpath, strerror(-rc));
275
276         rc = unlink(mainpath);
277         ASSERTF(rc == 0, "unlink failed for '%s': %s",
278                 mainpath, strerror(errno));
279
280         snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
281         recno = -1;
282         linkno = 0;
283         rc = llapi_fid2path(lustre_dir, fidstr, path,
284                             sizeof(path), &recno, &linkno);
285         ASSERTF(rc == -ENOENT, "llapi_fid2path failed for fid %s: %s",
286                 fidstr, strerror(-rc));
287 }
288
289 /* Test volatile file. */
290 static void test12(void)
291 {
292         int rc;
293         int fd;
294         int fd2;
295         int fd3;
296         struct lu_fid fid;
297
298         /* Against a volatile file */
299         rc = mkdir(mainpath, 0);
300         ASSERTF(rc == 0, "mkdir failed for '%s': %s",
301                 mainpath, strerror(errno));
302         fd = llapi_create_volatile_idx(mainpath, -1, 0);
303         ASSERTF(fd >= 0, "creat failed for '%s': %s",
304                 mainpath, strerror(errno));
305
306         rc = llapi_fd2fid(fd, &fid);
307         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
308                 mainpath, strerror(-rc));
309
310         /* No many ways to test, except to open by fid. */
311         fd2 = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
312         ASSERTF(fd2 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
313                 PFID(&fid), strerror(errno));
314
315         close(fd);
316
317         /* Check the file can still be opened, since fd2 is not closed. */
318         fd3 = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
319         ASSERTF(fd3 >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
320                 PFID(&fid), strerror(errno));
321
322         close(fd2);
323         close(fd3);
324
325         /* The volatile file is gone now. */
326         fd = llapi_open_by_fid(mainpath, &fid, O_RDONLY);
327         ASSERTF(fd < 0, "llapi_open_by_fid for " DFID_NOBRACE ": %d",
328                 PFID(&fid), fd);
329 }
330
331 /* Test with sub directories */
332 static void test20(void)
333 {
334         char testpath[PATH_MAX];
335         size_t len;
336         int dir_created = 0;
337         int rc;
338
339         rc = snprintf(testpath, sizeof(testpath), "%s", mainpath);
340         ASSERTF((rc > 0 && rc < sizeof(testpath)),
341                 "invalid name for testpath '%s'", mainpath);
342
343         rc = mkdir(testpath, 0700);
344         ASSERTF(rc == 0, "mkdir failed for '%s': %s",
345                 testpath, strerror(errno));
346
347         len = strlen(testpath);
348
349         /* Create subdirectories as long as we can. Each new subdir is
350          * "/x", so we need at least 3 characters left in testpath.
351          */
352         while (len <= sizeof(testpath) - 3) {
353                 strncat(testpath, "/x", sizeof(testpath) - 1);
354
355                 len += 2;
356
357                 rc = mkdir(testpath, 0700);
358                 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
359                         testpath, strerror(errno));
360
361                 dir_created++;
362
363                 helper_fid2path(testpath, -1);
364         }
365
366         /* And test the last one. */
367         helper_fid2path(testpath, -1);
368
369         /* Make sure we have created enough directories. Even with a
370          * reasonably long mountpath, we should have created at least 2000.
371          */
372         ASSERTF(dir_created >= 2000, "dir_created=%d -- '%s'",
373                 dir_created, testpath);
374 }
375
376 /* Test linkno from fid2path */
377 static void test30(void)
378 {
379         /* Note that since the links are stored in the extended attributes,
380          * only a few of these will fit (about 150 in this test). Still, create
381          * more than that to ensure the system doesn't break. See LU-5746.
382          */
383         const int num_links = 1000;
384         struct {
385                 char filename[PATH_MAX];
386                 bool seen;
387         } links[num_links];
388         char buf[PATH_MAX];
389         char buf2[PATH_MAX * 2];
390         struct lu_fid fid;
391         char fidstr[FID_LEN + 1];
392         int rc;
393         int i;
394         int j;
395         int fd;
396         int linkno;
397         bool past_link_limit = false;
398
399         /* Create the containing directory. */
400         rc = mkdir(mainpath, 0);
401         ASSERTF(rc == 0, "mkdir failed for '%s': %s",
402                 mainpath, strerror(errno));
403
404         /* Initializes the link array. */
405         for (i = 0; i < num_links; i++) {
406                 rc = snprintf(links[i].filename, sizeof(links[i].filename),
407                               "%s/%s/link%04d", lustre_dir, maindir, i);
408
409                 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
410                         "invalid name for link");
411
412                 links[i].seen = false;
413         }
414
415         /* Create the original file. */
416         fd = creat(links[0].filename, 0);
417         ASSERTF(fd >= 0, "create failed for '%s': %s",
418                 links[0].filename, strerror(errno));
419         close(fd);
420
421         rc = llapi_path2fid(links[0].filename, &fid);
422         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
423                 links[0].filename, strerror(-rc));
424         snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
425
426         /* Create the links */
427         for (i = 1; i < num_links; i++) {
428                 rc = link(links[0].filename, links[i].filename);
429                 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
430                         links[0].filename, links[i].filename, strerror(errno));
431         }
432
433         /* Query the links, making sure we got all of them */
434         for (i = 0; i < num_links + 10; i++) {
435                 long long recno;
436                 bool found;
437
438                 /* Without braces */
439                 recno = -1;
440                 linkno = i;
441                 rc = llapi_fid2path(links[0].filename, fidstr, buf,
442                                     sizeof(buf), &recno, &linkno);
443                 ASSERTF(rc == 0, "llapi_fid2path failed for fid %s: %s",
444                         fidstr, strerror(-rc));
445
446                 snprintf(buf2, sizeof(buf2), "%s/%s", mnt_dir, buf);
447
448                 if (past_link_limit == false) {
449                         /* Find the name in the links that were created */
450                         found = false;
451                         for (j = 0; j < num_links; j++) {
452                                 if (strcmp(buf2, links[j].filename) == 0) {
453                                         ASSERTF(links[j].seen == false,
454                                                 "link '%s' already seen",
455                                                 links[j].filename);
456                                         links[j].seen = true;
457                                         found = true;
458                                         break;
459                                 }
460                         }
461                         ASSERTF(found == true, "link '%s' not found", buf2);
462
463                         if (linkno == i) {
464                                 /* The linkno hasn't changed. This means it is
465                                  * the last entry stored.
466                                  */
467                                 past_link_limit = true;
468
469                                 fprintf(stderr,
470                                         "Was able to store %d links in the EA\n",
471                                         i);
472
473                                 /* Also assume that some links were returned.
474                                  * It's hard to compute the exact value.
475                                  */
476                                 ASSERTF(i > 50,
477                                         "not enough links were returned: %d",
478                                         i);
479                         }
480                 } else {
481                         /* Past the number of links stored in the EA, Lustre
482                          * will simply return the original file.
483                          */
484                         ASSERTF(strcmp(buf2, links[0].filename) == 0,
485                                        "unexpected link for record %d: '%s' / '%s'",
486                                        i, buf2, links[0].filename);
487                 }
488
489         }
490 }
491
492 /* Test special FIDs for lustre */
493 static void test31(void)
494 {
495         int fd;
496
497         fd = llapi_open_by_fid(mainpath, &LU_ROOT_FID, O_RDONLY);
498         ASSERTF(fd >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
499                 PFID(&LU_ROOT_FID), strerror(errno));
500
501         fd = llapi_open_by_fid(mainpath, &LU_DOT_LUSTRE_FID, O_RDONLY);
502         ASSERTF(fd >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
503                 PFID(&LU_DOT_LUSTRE_FID), strerror(errno));
504
505         fd = llapi_open_by_fid(mainpath, &LU_OBF_FID, O_RDONLY);
506         ASSERTF(fd >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %s",
507                 PFID(&LU_OBF_FID), strerror(errno));
508 }
509
510 /* Test llapi_fd2parent/llapi_path2parent on mainpath (whatever its
511  * type). mainpath must exist.
512  */
513 static void help_test40(void)
514 {
515         struct lu_fid parent_fid;
516         struct lu_fid fid2;
517         char buf[PATH_MAX];
518         int rc;
519
520         /* Successful call */
521         memset(buf, 0x55, sizeof(buf));
522         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, PATH_MAX);
523         ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
524                 mainpath, strerror(errno));
525         ASSERTF(strcmp(buf, maindir) == 0, "paths are different: '%s' / '%s'",
526                 buf, maindir);
527
528         /* By construction, mainpath is just under lustre_dir, so we
529          * can check that the parent fid of mainpath is indeed the one
530          * of lustre_dir.
531          */
532         rc = llapi_path2fid(lustre_dir, &fid2);
533         ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
534                 lustre_dir, strerror(-rc));
535         ASSERTF(memcmp(&parent_fid, &fid2, sizeof(fid2)) == 0,
536                 "fids are different");
537
538         /* Name too short */
539         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 0);
540         ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
541
542         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, 5);
543         ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
544
545         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf, strlen(maindir));
546         ASSERTF(rc == -EOVERFLOW, "llapi_path2parent error: %s", strerror(-rc));
547
548         rc = llapi_path2parent(mainpath, 0, &parent_fid, buf,
549                                strlen(maindir)+1);
550         ASSERTF(rc == 0, "llapi_path2parent failed: %s", strerror(-rc));
551 }
552
553 static void test40(void)
554 {
555         int fd;
556         int rc;
557
558         /* Against a directory. */
559         rc = mkdir(mainpath, 0);
560         ASSERTF(rc == 0, "mkdir failed for '%s': %s",
561                 mainpath, strerror(errno));
562         help_test40();
563
564         cleanup();
565
566         /* Against a regular file */
567         fd = creat(mainpath, 0);
568         ASSERTF(fd >= 0, "creat failed for '%s': %s",
569                 mainpath, strerror(errno));
570         close(fd);
571 }
572
573 /* Test LL_IOC_GETPARENT directly */
574 static void test41(void)
575 {
576         int rc;
577         int fd;
578         int i;
579         union {
580                 struct getparent gp;
581                 char buf[1024];
582         } u;
583
584         /* Against a regular file */
585         fd = creat(mainpath, 0);
586         ASSERTF(fd >= 0, "creat failed for '%s': %s",
587                 mainpath, strerror(errno));
588
589         /* Ask a few times */
590         for (i = 0; i < 256; i++) {
591                 memset(u.buf, i, sizeof(u.buf)); /* poison */
592                 u.gp.gp_linkno = 0;
593                 u.gp.gp_name_size = 100;
594
595                 rc = ioctl(fd, LL_IOC_GETPARENT, &u.gp);
596                 ASSERTF(rc == 0, "LL_IOC_GETPARENT failed: %s, rc=%d",
597                         strerror(errno), rc);
598                 ASSERTF(strcmp(u.gp.gp_name, maindir) == 0,
599                         "strings are different: %zd, %zd",
600                         strlen(u.gp.gp_name), strlen(maindir));
601         }
602
603         close(fd);
604 }
605
606 /* Test with linkno. Create sub directories, and put a link to the
607  * original file in them.
608  */
609 static void test42(void)
610 {
611
612         const int num_links = 100;
613         struct {
614                 char subdir[PATH_MAX];
615                 struct lu_fid subdir_fid;
616                 char filename[PATH_MAX];
617                 bool seen;
618         } links[num_links];
619         char link0[PATH_MAX];
620         char buf[PATH_MAX];
621         int rc;
622         int i;
623         int fd;
624         int linkno;
625         struct lu_fid parent_fid;
626
627         /* Create the containing directory. */
628         rc = mkdir(mainpath, 0);
629         ASSERTF(rc == 0, "mkdir failed: for '%s': %s",
630                 mainpath, strerror(errno));
631
632         /* Initializes the link array. */
633         for (i = 0; i < num_links; i++) {
634                 rc = snprintf(links[i].subdir, sizeof(links[i].subdir),
635                               "%s/sub%04d", mainpath, i);
636                 ASSERTF((rc > 0 && rc < sizeof(links[i].subdir)),
637                         "invalid name for subdir");
638
639                 rc = snprintf(links[i].filename, sizeof(links[i].filename),
640                               "link%04d", i);
641                 ASSERTF((rc > 0 && rc < sizeof(links[i].filename)),
642                         "invalid name for link");
643
644                 links[i].seen = false;
645         }
646
647         /* Create the subdirectories. */
648         for (i = 0; i < num_links; i++) {
649                 rc = mkdir(links[i].subdir, 0700);
650                 ASSERTF(rc == 0, "mkdir failed for '%s': %s",
651                         links[i].subdir, strerror(errno));
652
653                 rc = llapi_path2fid(links[i].subdir, &links[i].subdir_fid);
654                 ASSERTF(rc == 0, "llapi_path2fid failed for '%s': %s",
655                         links[i].subdir, strerror(-rc));
656         }
657
658         /* Create the original file. */
659         rc = snprintf(link0, sizeof(link0), "%s/%s",
660                       links[0].subdir, links[0].filename);
661         ASSERTF((rc > 0 && rc < sizeof(link0)), "invalid name for file");
662
663         fd = creat(link0, 0);
664         ASSERTF(fd >= 0, "create failed for '%s': %s", link0, strerror(errno));
665         close(fd);
666
667         /* Create the links */
668         for (i = 1; i < num_links; i++) {
669                 rc = snprintf(buf, sizeof(buf), "%s/%s",
670                               links[i].subdir, links[i].filename);
671                 ASSERTF((rc > 0 && rc < sizeof(buf)),
672                         "invalid name for link %d", i);
673
674                 rc = link(link0, buf);
675                 ASSERTF(rc == 0, "link failed for '%s' / '%s': %s",
676                         link0, buf, strerror(errno));
677         }
678
679         /* Query the links, making sure we got all of them. Do it in
680          * reverse order, just because!
681          */
682         for (linkno = num_links-1; linkno >= 0; linkno--) {
683                 bool found;
684
685                 rc = llapi_path2parent(link0, linkno, &parent_fid, buf,
686                                        sizeof(buf));
687                 ASSERTF(rc == 0, "llapi_path2parent failed for '%s': %s",
688                         link0, strerror(-rc));
689
690                 /* Find the name in the links that were created */
691                 found = false;
692                 for (i = 0; i < num_links; i++) {
693                         if (memcmp(&parent_fid, &links[i].subdir_fid,
694                                    sizeof(parent_fid)) != 0)
695                                 continue;
696
697                         ASSERTF(strcmp(links[i].filename, buf) == 0,
698                                 "name differ: '%s' / '%s'",
699                                 links[i].filename, buf);
700                         ASSERTF(links[i].seen == false,
701                                 "link '%s' already seen", links[i].filename);
702                         links[i].seen = true;
703                         found = true;
704                         break;
705                 }
706                 ASSERTF(found == true, "link '%s' not found", buf);
707         }
708
709         /* check non existent n+1 link */
710         rc = llapi_path2parent(link0, num_links, &parent_fid, buf, sizeof(buf));
711         ASSERTF(rc == -ENODATA, "llapi_path2parent error for '%s': %s",
712                 link0, strerror(-rc));
713 }
714
715 static void usage(char *prog)
716 {
717         fprintf(stderr, "Usage: %s [-d lustre_dir]\n", prog);
718         exit(EXIT_FAILURE);
719 }
720
721 static void process_args(int argc, char *argv[])
722 {
723         int c;
724
725         while ((c = getopt(argc, argv, "d:")) != -1) {
726                 switch (c) {
727                 case 'd':
728                         lustre_dir = optarg;
729                         break;
730                 case '?':
731                 default:
732                         fprintf(stderr, "Unknown option '%c'\n", optopt);
733                         usage(argv[0]);
734                 }
735         }
736 }
737
738 int main(int argc, char *argv[])
739 {
740         char fsname[8 + 1];
741         int rc;
742
743         process_args(argc, argv);
744         if (lustre_dir == NULL)
745                 lustre_dir = "/mnt/lustre";
746
747         rc = llapi_search_mounts(lustre_dir, 0, mnt_dir, fsname);
748         if (rc != 0) {
749                 fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
750                         lustre_dir);
751                 return EXIT_FAILURE;
752         }
753
754         mnt_fd = open(mnt_dir, O_RDONLY|O_DIRECTORY);
755         ASSERTF(!(mnt_fd < 0), "cannot open '%s': %s\n", mnt_dir,
756                 strerror(errno));
757
758         /* Play nice with Lustre test scripts. Non-line buffered output
759          * stream under I/O redirection may appear incorrectly.
760          */
761         setvbuf(stdout, NULL, _IOLBF, 0);
762
763         /* Create a test filename and reuse it. Remove possibly old files. */
764         rc = snprintf(mainpath, sizeof(mainpath), "%s/%s", lustre_dir, maindir);
765         ASSERTF((rc > 0 && rc < sizeof(mainpath)), "invalid name for mainpath");
766         cleanup();
767
768         atexit(cleanup);
769
770         PERFORM(test10);
771         PERFORM(test11);
772         PERFORM(test12);
773         PERFORM(test20);
774         PERFORM(test30);
775         PERFORM(test31);
776         PERFORM(test40);
777         PERFORM(test41);
778         PERFORM(test42);
779
780         return EXIT_SUCCESS;
781 }