alloc: upgrade to fallocate
[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, fd2 = -1;
44         char buf[BUF_SIZE];
45         int nread, bufsize = BUF_SIZE;
46         struct linux_dirent64 *d;
47         struct stat st = {};
48         int bpos, total, i;
49         off_t lret;
50         int retval = EXIT_SUCCESS;
51         const char *filename = NULL;
52         int exists = 0, found = 0;
53         int modify = 0, verbose = 0;
54
55         if (argc > 2) {
56                 bufsize = atoi(argv[2]);
57                 if (!bufsize)
58                         usage();
59                 if (bufsize > BUF_SIZE)
60                         bufsize = BUF_SIZE;
61
62                 if (argc > 3) {
63                         filename = argv[3];
64                         /* +<filename> creates, -<filename> removes */
65                         if (filename[0] == '+')
66                                 modify = 1;
67                         else if (filename[0] == '-')
68                                 modify = -1;
69                         if (modify)
70                                 filename++;
71                         if (argc > 4 && !strcmp(argv[4], "-v"))
72                                 verbose = 1;
73                 }
74         } else if (argc < 2) {
75                 usage();
76         }
77
78         fd = open(argv[1], O_RDONLY | O_DIRECTORY);
79         if (fd < 0) {
80                 perror("open");
81                 exit(EXIT_FAILURE);
82         }
83
84         if (filename) {
85                 exists = !fstatat(fd, filename, &st, AT_SYMLINK_NOFOLLOW);
86                 if (!exists && errno != ENOENT) {
87                         perror("fstatat");
88                         exit(EXIT_FAILURE);
89                 }
90         }
91
92         total = 0;
93         for ( ; ; ) {
94                 nread = syscall(SYS_getdents64, fd, buf, bufsize);
95                 if (nread == -1) {
96                         perror("getdents");
97                         exit(EXIT_FAILURE);
98                 }
99
100                 if (modify && fd2 < 0 && total == 0) {
101                         printf("getdents at offset 0 returned %d bytes\n", nread);
102
103                         /* create/unlink entry after first getdents */
104                         if (modify > 0) {
105                                 if (openat(fd, filename, O_CREAT, 0600) < 0) {
106                                         perror("openat");
107                                         exit(EXIT_FAILURE);
108                                 }
109                                 exists = 1;
110                                 printf("created entry %s\n", filename);
111                         } else if (modify < 0) {
112                                 if (unlinkat(fd, filename, 0) < 0) {
113                                         perror("unlinkat");
114                                         exit(EXIT_FAILURE);
115                                 }
116                                 exists = 0;
117                                 printf("unlinked entry %s\n", filename);
118                         }
119
120                         /*
121                          * Old fd may not return new entry and may return stale
122                          * entries which is allowed.  Keep old fd open and open
123                          * a new fd to check for stale or missing entries later.
124                          */
125                         fd2 = open(argv[1], O_RDONLY | O_DIRECTORY);
126                         if (fd2 < 0) {
127                                 perror("open fd2");
128                                 exit(EXIT_FAILURE);
129                         }
130                 }
131
132                 if (nread == 0) {
133                         if (fd2 < 0 || fd == fd2)
134                                 break;
135
136                         /* Re-iterate with new fd leaving old fd open */
137                         fd = fd2;
138                         total = 0;
139                         found = 0;
140                         continue;
141                 }
142
143                 for (bpos = 0; bpos < nread; total++) {
144                         d = (struct linux_dirent64 *) (buf + bpos);
145
146                         if (total >= HISTORY_LEN) {
147                                 fprintf(stderr, "too many files\n");
148                                 break;
149                         }
150
151                         for (i = 0; i < total; i++)
152                         {
153                                 if (d_off_history[i] == d->d_off) {
154                                         fprintf(stderr, "entries %d and %d have duplicate d_off %lld\n",
155                                                 i, total, (long long int)d->d_off);
156                                         retval = EXIT_FAILURE;
157                                 }
158                         }
159                         d_off_history[total] = d->d_off;
160                         d_ino_history[total] = d->d_ino;
161                         if (filename) {
162                                 if (verbose)
163                                         printf("entry #%d: %s (d_ino=%lld, d_off=%lld)\n",
164                                                i, d->d_name, (long long int)d->d_ino,
165                                                (long long int)d->d_off);
166                                 if (!strcmp(filename, d->d_name)) {
167                                         found = 1;
168                                         if (st.st_ino && d->d_ino != st.st_ino) {
169                                                 fprintf(stderr, "entry %s has inconsistent d_ino (%lld != %lld)\n",
170                                                                 filename,
171                                                                 (long long int)d->d_ino,
172                                                                 (long long int)st.st_ino);
173                                         }
174                                 }
175
176                         }
177                         bpos += d->d_reclen;
178                 }
179         }
180
181         if (filename) {
182                 if (exists == found) {
183                         printf("entry %s %sfound as expected\n", filename, found ? "" : "not ");
184                 } else {
185                         fprintf(stderr, "%s entry %s\n",
186                                 exists ? "missing" : "stale", filename);
187                         exit(EXIT_FAILURE);
188                 }
189         }
190
191         /* check if seek works correctly */
192         d = (struct linux_dirent64 *)buf;
193         for (i = total - 1; i >= 0; i--)
194         {
195                 lret = lseek(fd, i > 0 ? d_off_history[i - 1] : 0, SEEK_SET);
196                 if (lret == -1) {
197                         perror("lseek");
198                         exit(EXIT_FAILURE);
199                 }
200
201                 nread = syscall(SYS_getdents64, fd, buf, bufsize);
202                 if (nread == -1) {
203                         perror("getdents");
204                         exit(EXIT_FAILURE);
205                 }
206
207                 if (nread == 0) {
208                         fprintf(stderr, "getdents returned 0 on entry %d\n", i);
209                         retval = EXIT_FAILURE;
210                 }
211
212                 if (d->d_ino != d_ino_history[i]) {
213                         fprintf(stderr, "entry %d has inode %lld, expected %lld\n",
214                                 i, (long long int)d->d_ino, (long long int)d_ino_history[i]);
215                         retval = EXIT_FAILURE;
216                 }
217         }
218
219         close(fd);
220         exit(retval);
221 }