src/open_by_handle: verify dir content only with -r flag
[xfstests-dev.git] / src / open_by_handle.c
index 581dc0512f40cf0dd9abcecf7d0975027dd52a7c..9c13b2ab072b8173d6592bb5ddfbf775119013b2 100644 (file)
 
 /*
 
 
 /*
 
-usage: open_by_handle [-cludmrwa] <test_dir> [num_files]
+usage: open_by_handle [-cludmrwapk] <test_dir> [num_files]
 
 Examples:
 
 
 Examples:
 
-1. Create test set of N files and try to get their NFS handles:
+1. Create test set of of test_dir with N files and try to get their NFS handles:
 
 
-   open_by_handle -c <test_dir> [N]
+   open_by_handle -cp <test_dir> [N]
 
    This is used by new helper _require_exportfs() to check
    if filesystem supports exportfs
 
    This is used by new helper _require_exportfs() to check
    if filesystem supports exportfs
@@ -41,7 +41,7 @@ Examples:
 2. Get file handles for existing test set, drop caches and try to
    open all files by handle:
 
 2. Get file handles for existing test set, drop caches and try to
    open all files by handle:
 
-   open_by_handle <test_dir> [N]
+   open_by_handle -p <test_dir> [N]
 
 3. Get file handles for existing test set, write data to files,
    drop caches, open all files by handle, read and verify written
 
 3. Get file handles for existing test set, write data to files,
    drop caches, open all files by handle, read and verify written
@@ -50,16 +50,23 @@ Examples:
    open_by_handle -rwa <test_dir> [N]
 
 4. Get file handles for existing test set, unlink all test files,
    open_by_handle -rwa <test_dir> [N]
 
 4. Get file handles for existing test set, unlink all test files,
-   drop caches, try to open all files by handle and expect ESTALE:
+   remove test_dir, drop caches, try to open all files by handle
+   and expect ESTALE:
 
 
-   open_by_handle -d <test_dir> [N]
+   open_by_handle -dp <test_dir> [N]
 
 
-5. Get file handles for existing test set, rename all test files,
+5. Get file handles for existing test set, keep open file handles for all
+   test files, unlink all test files, drop caches and try to open all files
+   by handle (should work):
+
+   open_by_handle -dk <test_dir> [N]
+
+6. Get file handles for existing test set, rename all test files,
    drop caches, try to open all files by handle (should work):
 
    open_by_handle -m <test_dir> [N]
 
    drop caches, try to open all files by handle (should work):
 
    open_by_handle -m <test_dir> [N]
 
-6. Get file handles for existing test set, hardlink all test files,
+7. Get file handles for existing test set, hardlink all test files,
    then unlink the original files, drop caches and try to open all
    files by handle (should work):
 
    then unlink the original files, drop caches and try to open all
    files by handle (should work):
 
@@ -85,20 +92,22 @@ Examples:
 #include <sys/types.h>
 #include <errno.h>
 #include <linux/limits.h>
 #include <sys/types.h>
 #include <errno.h>
 #include <linux/limits.h>
+#include <libgen.h>
 
 #define MAXFILES 1024
 
 struct handle {
        struct file_handle fh;
        unsigned char fid[MAX_HANDLE_SZ];
 
 #define MAXFILES 1024
 
 struct handle {
        struct file_handle fh;
        unsigned char fid[MAX_HANDLE_SZ];
-} handle[MAXFILES];
+} handle[MAXFILES], dir_handle;
 
 void usage(void)
 {
 
 void usage(void)
 {
-       fprintf(stderr, "usage: open_by_handle [-cludmrwa] <test_dir> [num_files]\n");
+       fprintf(stderr, "usage: open_by_handle [-cludmrwapk] <test_dir> [num_files]\n");
        fprintf(stderr, "\n");
        fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n");
        fprintf(stderr, "open_by_handle    <test_dir> [N] - get file handles of test files, drop caches and try to open by handle\n");
        fprintf(stderr, "\n");
        fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n");
        fprintf(stderr, "open_by_handle    <test_dir> [N] - get file handles of test files, drop caches and try to open by handle\n");
+       fprintf(stderr, "open_by_handle -k <test_dir> [N] - get file handles of files that are kept open, drop caches and try to open by handle\n");
        fprintf(stderr, "open_by_handle -w <test_dir> [N] - write data to test files before open by handle\n");
        fprintf(stderr, "open_by_handle -r <test_dir> [N] - read data from test files after open by handle and verify written data\n");
        fprintf(stderr, "open_by_handle -a <test_dir> [N] - write data to test files after open by handle\n");
        fprintf(stderr, "open_by_handle -w <test_dir> [N] - write data to test files before open by handle\n");
        fprintf(stderr, "open_by_handle -r <test_dir> [N] - read data from test files after open by handle and verify written data\n");
        fprintf(stderr, "open_by_handle -a <test_dir> [N] - write data to test files after open by handle\n");
@@ -106,6 +115,7 @@ void usage(void)
        fprintf(stderr, "open_by_handle -u <test_dir> [N] - unlink (hardlinked) test files, drop caches and try to open by handle\n");
        fprintf(stderr, "open_by_handle -d <test_dir> [N] - unlink test files and hardlinks, drop caches and try to open by handle\n");
        fprintf(stderr, "open_by_handle -m <test_dir> [N] - rename test files, drop caches and try to open by handle\n");
        fprintf(stderr, "open_by_handle -u <test_dir> [N] - unlink (hardlinked) test files, drop caches and try to open by handle\n");
        fprintf(stderr, "open_by_handle -d <test_dir> [N] - unlink test files and hardlinks, drop caches and try to open by handle\n");
        fprintf(stderr, "open_by_handle -m <test_dir> [N] - rename test files, drop caches and try to open by handle\n");
+       fprintf(stderr, "open_by_handle -p <test_dir>     - create/delete and try to open by handle also test_dir itself\n");
        exit(EXIT_FAILURE);
 }
 
        exit(EXIT_FAILURE);
 }
 
@@ -113,20 +123,23 @@ int main(int argc, char **argv)
 {
        int     i, c;
        int     fd;
 {
        int     i, c;
        int     fd;
-       int     ret;
+       int     ret = 0;
        int     failed = 0;
        int     failed = 0;
+       char    dname[PATH_MAX];
        char    fname[PATH_MAX];
        char    fname2[PATH_MAX];
        char    *test_dir;
        char    fname[PATH_MAX];
        char    fname2[PATH_MAX];
        char    *test_dir;
+       char    *mount_dir;
        int     mount_fd, mount_id;
        int     numfiles = 1;
        int     create = 0, delete = 0, nlink = 1, move = 0;
        int     mount_fd, mount_id;
        int     numfiles = 1;
        int     create = 0, delete = 0, nlink = 1, move = 0;
-       int     rd = 0, wr = 0, wrafter = 0;
+       int     rd = 0, wr = 0, wrafter = 0, parent = 0;
+       int     keepopen = 0;
 
        if (argc < 2 || argc > 4)
                usage();
 
 
        if (argc < 2 || argc > 4)
                usage();
 
-       while ((c = getopt(argc, argv, "cludmrwa")) != -1) {
+       while ((c = getopt(argc, argv, "cludmrwapk")) != -1) {
                switch (c) {
                case 'c':
                        create = 1;
                switch (c) {
                case 'c':
                        create = 1;
@@ -157,6 +170,12 @@ int main(int argc, char **argv)
                case 'm':
                        move = 1;
                        break;
                case 'm':
                        move = 1;
                        break;
+               case 'p':
+                       parent = 1;
+                       break;
+               case 'k':
+                       keepopen = 1;
+                       break;
                default:
                        fprintf(stderr, "illegal option '%s'\n", argv[optind]);
                case 'h':
                default:
                        fprintf(stderr, "illegal option '%s'\n", argv[optind]);
                case 'h':
@@ -173,9 +192,27 @@ int main(int argc, char **argv)
                usage();
        }
 
                usage();
        }
 
-       mount_fd = open(test_dir, O_RDONLY|O_DIRECTORY);
+       if (parent) {
+               strcpy(dname, test_dir);
+               /*
+                * -p flag implies that test_dir is NOT a mount point,
+                * so its parent can be used as mount_fd for open_by_handle_at.
+                */
+               mount_dir = dirname(dname);
+               if (create)
+                       ret = mkdir(test_dir, 0700);
+               if (ret < 0 && errno != EEXIST) {
+                       strcat(dname, ": mkdir");
+                       perror(dname);
+                       return EXIT_FAILURE;
+               }
+       } else {
+               mount_dir = test_dir;
+       }
+
+       mount_fd = open(mount_dir, O_RDONLY|O_DIRECTORY);
        if (mount_fd < 0) {
        if (mount_fd < 0) {
-               perror(test_dir);
+               perror(mount_dir);
                return EXIT_FAILURE;
        }
 
                return EXIT_FAILURE;
        }
 
@@ -187,7 +224,7 @@ int main(int argc, char **argv)
                sprintf(fname2, "%s/link%06d", test_dir, i);
                fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
                if (fd < 0) {
                sprintf(fname2, "%s/link%06d", test_dir, i);
                fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
                if (fd < 0) {
-                       strcat(fname, ": open");
+                       strcat(fname, ": open(O_CREAT)");
                        perror(fname);
                        return EXIT_FAILURE;
                }
                        perror(fname);
                        return EXIT_FAILURE;
                }
@@ -214,6 +251,25 @@ int main(int argc, char **argv)
                        perror(fname);
                        return EXIT_FAILURE;
                }
                        perror(fname);
                        return EXIT_FAILURE;
                }
+               if (keepopen) {
+                       /* Open without close to keep unlinked files around */
+                       fd = open(fname, O_RDONLY);
+                       if (fd < 0) {
+                               strcat(fname, ": open(O_RDONLY)");
+                               perror(fname);
+                               return EXIT_FAILURE;
+                       }
+               }
+       }
+
+       if (parent) {
+               dir_handle.fh.handle_bytes = MAX_HANDLE_SZ;
+               ret = name_to_handle_at(AT_FDCWD, test_dir, &dir_handle.fh, &mount_id, 0);
+               if (ret < 0) {
+                       strcat(dname, ": name_to_handle");
+                       perror(dname);
+                       return EXIT_FAILURE;
+               }
        }
 
        /* write data to files */
        }
 
        /* write data to files */
@@ -281,6 +337,15 @@ int main(int argc, char **argv)
                }
        }
 
                }
        }
 
+       if (parent && delete && !nlink) {
+               ret = rmdir(test_dir);
+               if (ret < 0) {
+                       strcat(dname, ": rmdir");
+                       perror(dname);
+                       return EXIT_FAILURE;
+               }
+       }
+
        /* sync to get log forced for unlink transactions to hit the disk */
        sync();
 
        /* sync to get log forced for unlink transactions to hit the disk */
        sync();
 
@@ -305,7 +370,7 @@ int main(int argc, char **argv)
        for (i=0; i < numfiles; i++) {
                errno = 0;
                fd = open_by_handle_at(mount_fd, &handle[i].fh, wrafter ? O_RDWR : O_RDONLY);
        for (i=0; i < numfiles; i++) {
                errno = 0;
                fd = open_by_handle_at(mount_fd, &handle[i].fh, wrafter ? O_RDWR : O_RDONLY);
-               if (nlink && fd >= 0) {
+               if ((nlink || keepopen) && fd >= 0) {
                        if (rd) {
                                char buf[4] = {0};
                                int size = read(fd, buf, 4);
                        if (rd) {
                                char buf[4] = {0};
                                int size = read(fd, buf, 4);
@@ -325,7 +390,7 @@ int main(int argc, char **argv)
                        }
                        close(fd);
                        continue;
                        }
                        close(fd);
                        continue;
-               } else if (!nlink && fd < 0 && (errno == ENOENT || errno == ESTALE)) {
+               } else if (!nlink && !keepopen && fd < 0 && (errno == ENOENT || errno == ESTALE)) {
                        continue;
                }
                sprintf(fname, "%s/file%06d", test_dir, i);
                        continue;
                }
                sprintf(fname, "%s/file%06d", test_dir, i);
@@ -333,12 +398,56 @@ int main(int argc, char **argv)
                        printf("open_by_handle(%s) opened an unlinked file!\n", fname);
                        close(fd);
                } else {
                        printf("open_by_handle(%s) opened an unlinked file!\n", fname);
                        close(fd);
                } else {
-                       printf("open_by_handle(%s) returned %d incorrectly on %s file!\n",
+                       printf("open_by_handle(%s) returned %d incorrectly on %s%s file!\n",
                                        fname, errno,
                                        fname, errno,
-                                       nlink ? "a linked" : "an unlinked");
+                                       nlink ? "a linked" : "an unlinked",
+                                       keepopen ? " open" : "");
                }
                failed++;
        }
                }
                failed++;
        }
+
+       if (parent) {
+               fd = open_by_handle_at(mount_fd, &dir_handle.fh, O_RDONLY|O_DIRECTORY);
+               if (fd >= 0) {
+                       if (!nlink) {
+                               printf("open_by_handle(%s) opened an unlinked dir!\n", dname);
+                               return EXIT_FAILURE;
+                       } else if (rd) {
+                               /*
+                                * Sanity check dir fd - expect to access orig file IFF
+                                * it was not unlinked nor renamed.
+                                */
+                               strcpy(fname, "file000000");
+                               ret = faccessat(fd, fname, F_OK, 0);
+                               if ((ret == 0) != (!delete && !move) ||
+                                   ((ret < 0) && errno != ENOENT)) {
+                                       strcat(fname, ": unexpected result from faccessat");
+                                       perror(fname);
+                                       return EXIT_FAILURE;
+                               }
+                               /*
+                                * Expect to access link file if ran test with -l flag
+                                * (nlink > 1), -m flag (orig moved to link name) or
+                                * -u flag (which implied previous -l run).
+                                */
+                               strcpy(fname2, "link000000");
+                               ret = faccessat(fd, fname2, F_OK, 0);
+                               if (ret < 0 && (nlink > 1 || delete || move ||
+                                               errno != ENOENT)) {
+                                       strcat(fname2, ": unexpected result from faccessat");
+                                       perror(fname2);
+                                       return EXIT_FAILURE;
+                               }
+                       }
+                       close(fd);
+               } else if (nlink || !(errno == ENOENT || errno == ESTALE)) {
+                       printf("open_by_handle(%s) returned %d incorrectly on %s dir!\n",
+                                       dname, errno,
+                                       nlink ? "a linked" : "an unlinked");
+                       return EXIT_FAILURE;
+               }
+       }
+
        if (failed)
                return EXIT_FAILURE;
        return EXIT_SUCCESS;
        if (failed)
                return EXIT_FAILURE;
        return EXIT_SUCCESS;