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