src/t_dir_offset2: Add option to create or unlink file
[xfstests-dev.git] / src / t_dir_offset2.c
index 7aeb990e6eb3ecf6623eb8442f98f746f865b520..75b41c1a29078527ab97d946b1c417de4f79914b 100644 (file)
@@ -8,11 +8,13 @@
  * that these offsets are seekable to entry with the right inode.
  */
 
+#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
 
@@ -32,19 +34,22 @@ static uint64_t d_ino_history[HISTORY_LEN];
 
 void usage()
 {
-       fprintf(stderr, "usage: t_dir_offset2: <dir> [bufsize]\n");
+       fprintf(stderr, "usage: t_dir_offset2: <dir> [[bufsize] [-|+]<filename> [-v]]\n");
        exit(EXIT_FAILURE);
 }
 
 int main(int argc, char *argv[])
 {
-       int fd;
+       int fd, fd2 = -1;
        char buf[BUF_SIZE];
        int nread, bufsize = BUF_SIZE;
        struct linux_dirent64 *d;
        int bpos, total, i;
        off_t lret;
        int retval = EXIT_SUCCESS;
+       const char *filename = NULL;
+       int exists = 0, found = 0;
+       int modify = 0, verbose = 0;
 
        if (argc > 2) {
                bufsize = atoi(argv[2]);
@@ -52,6 +57,19 @@ int main(int argc, char *argv[])
                        usage();
                if (bufsize > BUF_SIZE)
                        bufsize = BUF_SIZE;
+
+               if (argc > 3) {
+                       filename = argv[3];
+                       /* +<filename> creates, -<filename> removes */
+                       if (filename[0] == '+')
+                               modify = 1;
+                       else if (filename[0] == '-')
+                               modify = -1;
+                       if (modify)
+                               filename++;
+                       if (argc > 4 && !strcmp(argv[4], "-v"))
+                               verbose = 1;
+               }
        } else if (argc < 2) {
                usage();
        }
@@ -62,6 +80,14 @@ int main(int argc, char *argv[])
                exit(EXIT_FAILURE);
        }
 
+       if (filename) {
+               exists = !faccessat(fd, filename, F_OK, AT_SYMLINK_NOFOLLOW);
+               if (!exists && errno != ENOENT) {
+                       perror("faccessat");
+                       exit(EXIT_FAILURE);
+               }
+       }
+
        total = 0;
        for ( ; ; ) {
                nread = syscall(SYS_getdents64, fd, buf, bufsize);
@@ -70,6 +96,49 @@ int main(int argc, char *argv[])
                        exit(EXIT_FAILURE);
                }
 
+               if (modify && fd2 < 0 && total == 0) {
+                       printf("getdents at offset 0 returned %d bytes\n", nread);
+
+                       /* create/unlink entry after first getdents */
+                       if (modify > 0) {
+                               if (openat(fd, filename, O_CREAT, 0600) < 0) {
+                                       perror("openat");
+                                       exit(EXIT_FAILURE);
+                               }
+                               exists = 1;
+                               printf("created entry %s\n", filename);
+                       } else if (modify < 0) {
+                               if (unlinkat(fd, filename, 0) < 0) {
+                                       perror("unlinkat");
+                                       exit(EXIT_FAILURE);
+                               }
+                               exists = 0;
+                               printf("unlinked entry %s\n", filename);
+                       }
+
+                       /*
+                        * Old fd may not return new entry and may return stale
+                        * entries which is allowed.  Keep old fd open and open
+                        * a new fd to check for stale or missing entries later.
+                        */
+                       fd2 = open(argv[1], O_RDONLY | O_DIRECTORY);
+                       if (fd2 < 0) {
+                               perror("open fd2");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               if (nread == 0) {
+                       if (fd2 < 0 || fd == fd2)
+                               break;
+
+                       /* Re-iterate with new fd leaving old fd open */
+                       fd = fd2;
+                       total = 0;
+                       found = 0;
+                       continue;
+               }
+
                if (nread == 0)
                        break;
 
@@ -91,10 +160,28 @@ int main(int argc, char *argv[])
                        }
                        d_off_history[total] = d->d_off;
                        d_ino_history[total] = d->d_ino;
+                       if (filename) {
+                               if (verbose)
+                                       printf("entry #%d: %s (d_ino=%lld, d_off=%lld)\n",
+                                              i, d->d_name, (long long int)d->d_ino,
+                                              (long long int)d->d_off);
+                               if (!strcmp(filename, d->d_name))
+                                       found = 1;
+                       }
                        bpos += d->d_reclen;
                }
        }
 
+       if (filename) {
+               if (exists == found) {
+                       printf("entry %s %sfound as expected\n", filename, found ? "" : "not ");
+               } else {
+                       fprintf(stderr, "%s entry %s\n",
+                               exists ? "missing" : "stale", filename);
+                       exit(EXIT_FAILURE);
+               }
+       }
+
        /* check if seek works correctly */
        d = (struct linux_dirent64 *)buf;
        for (i = total - 1; i >= 0; i--)