7af245195b854901d30dbdc4e66da262165498e5
[xfstests-dev.git] / src / t_dir_offset2.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2011 GraÅžvydas Ignotas
4  */
5
6 /*
7  * This test checks if no duplicate d_off values are returned and
8  * that these offsets are seekable to entry with the right inode.
9  */
10
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <stdint.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/stat.h>
19 #include <sys/syscall.h>
20
21 struct linux_dirent64 {
22         uint64_t        d_ino;
23         uint64_t        d_off;
24         unsigned short  d_reclen;
25         unsigned char   d_type;
26         char            d_name[0];
27 };
28
29 #define BUF_SIZE 4096
30 #define HISTORY_LEN 1024
31
32 static uint64_t d_off_history[HISTORY_LEN];
33 static uint64_t d_ino_history[HISTORY_LEN];
34
35 void usage()
36 {
37         fprintf(stderr, "usage: t_dir_offset2: <dir> [[bufsize] <filename> [-v]]\n");
38         exit(EXIT_FAILURE);
39 }
40
41 int main(int argc, char *argv[])
42 {
43         int fd;
44         char buf[BUF_SIZE];
45         int nread, bufsize = BUF_SIZE;
46         struct linux_dirent64 *d;
47         int bpos, total, i;
48         off_t lret;
49         int retval = EXIT_SUCCESS;
50         const char *filename = NULL;
51         int exists = 0, found = 0;
52         int verbose = 0;
53
54         if (argc > 2) {
55                 bufsize = atoi(argv[2]);
56                 if (!bufsize)
57                         usage();
58                 if (bufsize > BUF_SIZE)
59                         bufsize = BUF_SIZE;
60
61                 if (argc > 3) {
62                         filename = argv[3];
63                         if (argc > 4 && !strcmp(argv[4], "-v"))
64                                 verbose = 1;
65                 }
66         } else if (argc < 2) {
67                 usage();
68         }
69
70         fd = open(argv[1], O_RDONLY | O_DIRECTORY);
71         if (fd < 0) {
72                 perror("open");
73                 exit(EXIT_FAILURE);
74         }
75
76         if (filename) {
77                 exists = !faccessat(fd, filename, F_OK, AT_SYMLINK_NOFOLLOW);
78                 if (!exists && errno != ENOENT) {
79                         perror("faccessat");
80                         exit(EXIT_FAILURE);
81                 }
82         }
83
84         total = 0;
85         for ( ; ; ) {
86                 nread = syscall(SYS_getdents64, fd, buf, bufsize);
87                 if (nread == -1) {
88                         perror("getdents");
89                         exit(EXIT_FAILURE);
90                 }
91
92                 if (nread == 0)
93                         break;
94
95                 for (bpos = 0; bpos < nread; total++) {
96                         d = (struct linux_dirent64 *) (buf + bpos);
97
98                         if (total >= HISTORY_LEN) {
99                                 fprintf(stderr, "too many files\n");
100                                 break;
101                         }
102
103                         for (i = 0; i < total; i++)
104                         {
105                                 if (d_off_history[i] == d->d_off) {
106                                         fprintf(stderr, "entries %d and %d have duplicate d_off %lld\n",
107                                                 i, total, (long long int)d->d_off);
108                                         retval = EXIT_FAILURE;
109                                 }
110                         }
111                         d_off_history[total] = d->d_off;
112                         d_ino_history[total] = d->d_ino;
113                         if (filename) {
114                                 if (verbose)
115                                         printf("entry #%d: %s (d_ino=%lld, d_off=%lld)\n",
116                                                i, d->d_name, (long long int)d->d_ino,
117                                                (long long int)d->d_off);
118                                 if (!strcmp(filename, d->d_name))
119                                         found = 1;
120                         }
121                         bpos += d->d_reclen;
122                 }
123         }
124
125         if (filename) {
126                 if (exists == found) {
127                         printf("entry %s %sfound as expected\n", filename, found ? "" : "not ");
128                 } else {
129                         fprintf(stderr, "%s entry %s\n",
130                                 exists ? "missing" : "stale", filename);
131                         exit(EXIT_FAILURE);
132                 }
133         }
134
135         /* check if seek works correctly */
136         d = (struct linux_dirent64 *)buf;
137         for (i = total - 1; i >= 0; i--)
138         {
139                 lret = lseek(fd, i > 0 ? d_off_history[i - 1] : 0, SEEK_SET);
140                 if (lret == -1) {
141                         perror("lseek");
142                         exit(EXIT_FAILURE);
143                 }
144
145                 nread = syscall(SYS_getdents64, fd, buf, bufsize);
146                 if (nread == -1) {
147                         perror("getdents");
148                         exit(EXIT_FAILURE);
149                 }
150
151                 if (nread == 0) {
152                         fprintf(stderr, "getdents returned 0 on entry %d\n", i);
153                         retval = EXIT_FAILURE;
154                 }
155
156                 if (d->d_ino != d_ino_history[i]) {
157                         fprintf(stderr, "entry %d has inode %lld, expected %lld\n",
158                                 i, (long long int)d->d_ino, (long long int)d_ino_history[i]);
159                         retval = EXIT_FAILURE;
160                 }
161         }
162
163         close(fd);
164         exit(retval);
165 }