]> git-server-git.apps.pok.os.sepia.ceph.com Git - xfstests-dev.git/commitdiff
generic: add tests for file delegations v2025.12.09
authorJeff Layton <jlayton@kernel.org>
Wed, 3 Dec 2025 15:43:09 +0000 (10:43 -0500)
committerZorro Lang <zlang@kernel.org>
Fri, 5 Dec 2025 17:41:49 +0000 (01:41 +0800)
Mostly the same ones as leases, but some additional tests to validate
that they are broken on metadata changes.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Zorro Lang <zlang@redhat.com>
Signed-off-by: Zorro Lang <zlang@kernel.org>
common/locktest
src/locktest.c
tests/generic/787 [new file with mode: 0755]
tests/generic/787.out [new file with mode: 0644]

index 12b5c27e0c03ad4c60985e3882026fce04e7330e..9344c43d8ee97679b49357b4e75de89ad56221ff 100644 (file)
@@ -101,3 +101,8 @@ _run_dirdelegtest() {
        TESTFILE=$DELEGDIR
        _run_generic "-D"
 }
+
+_run_filedelegtest() {
+       TESTFILE=$DELEGDIR
+       _run_generic "-F"
+}
index eb40dce3f1b28ef34752518808ec2f3999cd4257..54ee1f07539ef08e768d2c809c40327f315d43e7 100644 (file)
@@ -126,6 +126,8 @@ static char *child[] = { "child0", "child1" };
 #define                CMD_CHMOD       19
 #define                CMD_MKDIR       20
 #define                CMD_RMDIR       21
+#define                CMD_UNLINK_S    22
+#define                CMD_RENAME_S    23
 
 #define                PASS    0
 #define                FAIL    1
@@ -169,6 +171,8 @@ static char *get_cmd_str(int cmd)
                case CMD_CHMOD: return "Chmod"; break;
                case CMD_MKDIR: return "Mkdir"; break;
                case CMD_RMDIR: return "Rmdir"; break;
+               case CMD_UNLINK_S: return "Remove Self"; break;
+               case CMD_RENAME_S: return "Rename Self"; break;
        }
        return "unknown";
 }
@@ -716,6 +720,150 @@ static int64_t lease_tests[][6] =
                {0,0,0,0,0,CLIENT}
        };
 
+char *filedeleg_descriptions[] = {
+    /*  1 */"Take Read Deleg",
+    /*  2 */"Take Write Deleg",
+    /*  3 */"Fail Write Deleg if file is open somewhere else",
+    /*  4 */"Fail Read Deleg if opened with write permissions",
+    /*  5 */"Read deleg gets SIGIO on write open",
+    /*  6 */"Write deleg gets SIGIO on read open",
+    /*  7 */"Read deleg does _not_ get SIGIO on read open",
+    /*  8 */"Read deleg gets SIGIO on write open",
+    /*  9 */"Write deleg gets SIGIO on truncate",
+    /* 10 */"Read deleg gets SIGIO on truncate",
+    /* 11 */"Read deleg gets SIGIO on chmod",
+    /* 12 */"Read deleg gets SIGIO on unlink",
+    /* 13 */"Read deleg gets SIGIO on rename",
+};
+
+static int64_t filedeleg_tests[][6] =
+       /*      test #  Action  [offset|flags|arg]      length          expected        server/client */
+       /*                      [sigio_wait_time]                                               */
+       {
+       /* Various tests to exercise delegs */
+
+       /* SECTION 1: Simple verification of being able to take delegs */
+       /* Take Read Deleg */
+       {1,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+       {1,     CMD_OPEN,       O_RDONLY,       0,      PASS,           CLIENT  },
+       {1,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+       {1,     CMD_OPEN,       O_RDONLY,       0,      PASS,           SERVER  },
+       {1,     CMD_SETDELEG,   F_RDLCK,        0,      PASS,           SERVER  },
+       {1,     CMD_GETDELEG,   F_RDLCK,        0,      PASS,           SERVER  },
+       {1,     CMD_SETDELEG,   F_UNLCK,        0,      PASS,           SERVER  },
+       {1,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+       {1,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
+       /* Take Write Deleg */
+       {2,     CMD_OPEN,       O_RDWR,         0,      PASS,           SERVER  },
+       {2,     CMD_SETDELEG,   F_WRLCK,        0,      PASS,           SERVER  },
+       {2,     CMD_GETDELEG,   F_WRLCK,        0,      PASS,           SERVER  },
+       {2,     CMD_SETDELEG,   F_UNLCK,        0,      PASS,           SERVER  },
+       {2,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+       /* Fail Write Deleg with other users */
+       {3,     CMD_OPEN,       O_RDONLY,       0,      PASS,           CLIENT  },
+       {3,     CMD_OPEN,       O_RDWR,         0,      PASS,           SERVER  },
+       {3,     CMD_SETDELEG,   F_WRLCK,        0,      FAIL,           SERVER  },
+       {3,     CMD_GETDELEG,   F_WRLCK,        0,      FAIL,           SERVER  },
+       {3,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+       {3,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+       /* Fail Read Deleg if opened for write */
+       {4,     CMD_OPEN,       O_RDWR,         0,      PASS,           SERVER  },
+       {4,     CMD_SETDELEG,   F_RDLCK,        0,      FAIL,           SERVER  },
+       {4,     CMD_GETDELEG,   F_RDLCK,        0,      FAIL,           SERVER  },
+       {4,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+
+       /* SECTION 2: Proper SIGIO notifications */
+       /* Get SIGIO when read deleg is broken by write */
+       {5,     CMD_OPEN,       O_RDONLY,       0,      PASS,           CLIENT  },
+       {5,     CMD_SETDELEG,   F_RDLCK,        0,      PASS,           CLIENT  },
+       {5,     CMD_GETDELEG,   F_RDLCK,        0,      PASS,           CLIENT  },
+       {5,     CMD_SIGIO,      0,              0,      PASS,           CLIENT  },
+       {5,     CMD_OPEN,       O_RDWR,         0,      PASS,           SERVER  },
+       {5,     CMD_WAIT_SIGIO, 5,              0,      PASS,           CLIENT  },
+       {5,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+       {5,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
+       /* Get SIGIO when write deleg is broken by read */
+       {6,     CMD_OPEN,       O_RDWR,         0,      PASS,           CLIENT  },
+       {6,     CMD_SETDELEG,   F_WRLCK,        0,      PASS,           CLIENT  },
+       {6,     CMD_GETDELEG,   F_WRLCK,        0,      PASS,           CLIENT  },
+       {6,     CMD_SIGIO,      0,              0,      PASS,           CLIENT  },
+       {6,     CMD_OPEN,       O_RDONLY,       0,      PASS,           SERVER  },
+       {6,     CMD_WAIT_SIGIO, 5,              0,      PASS,           CLIENT  },
+       {6,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+       {6,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
+       /* Don't get SIGIO when read deleg is taken by read */
+       {7,     CMD_OPEN,       O_RDONLY,       0,      PASS,           CLIENT  },
+       {7,     CMD_SETDELEG,   F_RDLCK,        0,      PASS,           CLIENT  },
+       {7,     CMD_GETDELEG,   F_RDLCK,        0,      PASS,           CLIENT  },
+       {7,     CMD_SIGIO,      0,              0,      PASS,           CLIENT  },
+       {7,     CMD_OPEN,       O_RDONLY,       0,      PASS,           SERVER  },
+       {7,     CMD_WAIT_SIGIO, 5,              0,      FAIL,           CLIENT  },
+       {7,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+       {7,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
+       /* Get SIGIO when Read deleg is broken by Write */
+       {8,     CMD_OPEN,       O_RDONLY,       0,      PASS,           CLIENT  },
+       {8,     CMD_SETDELEG,   F_RDLCK,        0,      PASS,           CLIENT  },
+       {8,     CMD_GETDELEG,   F_RDLCK,        0,      PASS,           CLIENT  },
+       {8,     CMD_SIGIO,      0,              0,      PASS,           CLIENT  },
+       {8,     CMD_OPEN,       O_RDWR,         0,      PASS,           SERVER  },
+       {8,     CMD_WAIT_SIGIO, 5,              0,      PASS,           CLIENT  },
+       {8,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+       {8,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
+       /* Get SIGIO when Write deleg is broken by Truncate */
+       {9,     CMD_OPEN,       O_RDWR,         0,      PASS,           CLIENT  },
+       {9,     CMD_SETDELEG,   F_WRLCK,        0,      PASS,           CLIENT  },
+       {9,     CMD_GETDELEG,   F_WRLCK,        0,      PASS,           CLIENT  },
+       {9,     CMD_SIGIO,      0,              0,      PASS,           CLIENT  },
+       {9,     CMD_TRUNCATE,   FILE_SIZE/2,    0,      PASS,           CLIENT  },
+       {9,     CMD_WAIT_SIGIO, 5,              0,      PASS,           CLIENT  },
+       {9,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
+       /* Get SIGIO when Read deleg is broken by Truncate */
+       {10,    CMD_OPEN,       O_RDONLY,       0,      PASS,           CLIENT  },
+       {10,    CMD_SETDELEG,   F_RDLCK,        0,      PASS,           CLIENT  },
+       {10,    CMD_GETDELEG,   F_RDLCK,        0,      PASS,           CLIENT  },
+       {10,    CMD_SIGIO,      0,              0,      PASS,           CLIENT  },
+       {10,    CMD_TRUNCATE,   FILE_SIZE/2,    0,      PASS,           SERVER  },
+       {10,    CMD_WAIT_SIGIO, 5,              0,      PASS,           CLIENT  },
+       {10,    CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
+       /* Get SIGIO when Read deleg is broken by Chmod */
+       {11,    CMD_OPEN,       O_RDONLY,       0,      PASS,           SERVER  },
+       {11,    CMD_SETDELEG,   F_RDLCK,        0,      PASS,           SERVER  },
+       {11,    CMD_GETDELEG,   F_RDLCK,        0,      PASS,           SERVER  },
+       {11,    CMD_SIGIO,      0,              0,      PASS,           SERVER  },
+       {11,    CMD_CHMOD,      0644,           0,      PASS,           CLIENT  },
+       {11,    CMD_WAIT_SIGIO, 5,              0,      PASS,           SERVER  },
+       {11,    CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+
+       /* Get SIGIO when file is unlinked */
+       {12,    CMD_OPEN,       O_RDONLY,       0,      PASS,           SERVER  },
+       {12,    CMD_SETDELEG,   F_RDLCK,        0,      PASS,           SERVER  },
+       {12,    CMD_GETDELEG,   F_RDLCK,        0,      PASS,           SERVER  },
+       {12,    CMD_SIGIO,      0,              0,      PASS,           SERVER  },
+       {12,    CMD_UNLINK_S,   0,              0,      PASS,           CLIENT  },
+       {12,    CMD_WAIT_SIGIO, 5,              0,      PASS,           SERVER  },
+       {12,    CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+
+       /* Get SIGIO when file is renamed */
+       {13,    CMD_OPEN,       O_RDONLY,       0,      PASS,           SERVER  },
+       {13,    CMD_SETDELEG,   F_RDLCK,        0,      PASS,           SERVER  },
+       {13,    CMD_GETDELEG,   F_RDLCK,        0,      PASS,           SERVER  },
+       {13,    CMD_SIGIO,      0,              0,      PASS,           SERVER  },
+       {13,    CMD_RENAME_S,   0,              0,      PASS,           CLIENT  },
+       {13,    CMD_WAIT_SIGIO, 5,              0,      PASS,           SERVER  },
+       {13,    CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+
+       /* indicate end of array */
+       {0,0,0,0,0,SERVER},
+       {0,0,0,0,0,CLIENT}
+};
+
 char *dirdeleg_descriptions[] = {
     /*  1 */"Take Read Lease",
     /*  2 */"Write Lease Should Fail",
@@ -1124,6 +1272,37 @@ int do_chmod(int mode)
        return PASS;
 }
 
+int do_unlink_self(void)
+{
+       int ret;
+
+       ret = unlink(filename);
+       if (ret < 0) {
+               perror("unlink");
+               return FAIL;
+       }
+       return PASS;
+}
+
+int do_rename_self(void)
+{
+       int ret;
+       char target[PATH_MAX];
+
+       ret = snprintf(target, sizeof(target), "%s2", filename);
+       if (ret >= sizeof(target)) {
+               perror("snprintf");
+               return FAIL;
+       }
+
+       ret = rename(filename, target);
+       if (ret < 0) {
+               perror("unlink");
+               return FAIL;
+       }
+       return PASS;
+}
+
 static int do_lock(int cmd, int type, int start, int length)
 {
     int ret;
@@ -1347,6 +1526,7 @@ main(int argc, char *argv[])
     int fail_count = 0;
     int run_leases = 0;
     int run_dirdelegs = 0;
+    int run_filedelegs = 0;
     int test_setlease = 0;
     
     atexit(cleanup);
@@ -1360,7 +1540,7 @@ main(int argc, char *argv[])
            prog = p+1;
     }
 
-    while ((c = getopt(argc, argv, "dDLn:h:p:t?")) != EOF) {
+    while ((c = getopt(argc, argv, "dDFLn:h:p:t?")) != EOF) {
        switch (c) {
 
        case 'd':       /* debug flag */
@@ -1371,6 +1551,10 @@ main(int argc, char *argv[])
            run_dirdelegs = 1;
            break;
 
+       case 'F':
+           run_filedelegs = 1;
+           break;
+
        case 'L':       /* Lease testing */
            run_leases = 1;
            break;
@@ -1430,7 +1614,7 @@ main(int argc, char *argv[])
     if (test_setlease == 1) {
        struct delegation deleg = { .d_type = F_UNLCK };
 
-       if (run_dirdelegs)
+       if (run_dirdelegs || run_filedelegs)
                fcntl(f_fd, F_SETDELEG, &deleg);
        else
                fcntl(f_fd, F_SETLEASE, F_UNLCK);
@@ -1568,6 +1752,8 @@ main(int argc, char *argv[])
      */
     if (run_dirdelegs)
        fail_count = run(dirdeleg_tests, dirdeleg_descriptions);
+    else if (run_filedelegs)
+       fail_count = run(filedeleg_tests, filedeleg_descriptions);
     else if (run_leases)
        fail_count = run(lease_tests, lease_descriptions);
     else
@@ -1673,6 +1859,12 @@ int run(int64_t tests[][6], char *descriptions[])
                        case CMD_RMDIR:
                            result = do_rmdir(tests[index][ARG]);
                            break;
+                       case CMD_UNLINK_S:
+                           result = do_unlink_self();
+                           break;
+                       case CMD_RENAME_S:
+                           result = do_rename_self();
+                           break;
                    }
                    if( result != tests[index][RESULT]) {
                        fail_flag++;
@@ -1817,6 +2009,12 @@ int run(int64_t tests[][6], char *descriptions[])
                case CMD_RMDIR:
                    result = do_rmdir(ctl.offset);
                    break;
+               case CMD_UNLINK_S:
+                   result = do_unlink_self();
+                   break;
+               case CMD_RENAME_S:
+                   result = do_rename_self();
+                   break;
            }
            if( result != ctl.result ) {
                fprintf(stderr,"Failure in %d:%s\n",
diff --git a/tests/generic/787 b/tests/generic/787
new file mode 100755 (executable)
index 0000000..6b538a9
--- /dev/null
@@ -0,0 +1,20 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 Jeff Layton <jlayton@kernel.org>.  All Rights Reserved.
+#
+# FS QA Test No. 787
+#
+# Test file delegation support
+#
+. ./common/preamble
+_begin_fstest auto quick locks
+
+# Import common functions.
+. ./common/filter
+. ./common/locktest
+
+_require_test
+_require_test_fcntl_setdeleg
+
+_run_filedelegtest
+_exit 0
diff --git a/tests/generic/787.out b/tests/generic/787.out
new file mode 100644 (file)
index 0000000..b28bed6
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 787
+success!