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