linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jeff Layton <jlayton@kernel.org>
To: fstests@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org, Zorro Lang <zlang@redhat.com>,
	 Christian Brauner <brauner@kernel.org>,
	Jeff Layton <jlayton@kernel.org>
Subject: [PATCH fstests v3 3/3] generic: add tests for file delegations
Date: Wed, 03 Dec 2025 10:43:09 -0500	[thread overview]
Message-ID: <20251203-dir-deleg-v3-3-be55fbf2ad53@kernel.org> (raw)
In-Reply-To: <20251203-dir-deleg-v3-0-be55fbf2ad53@kernel.org>

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>
---
 common/locktest       |   5 ++
 src/locktest.c        | 202 +++++++++++++++++++++++++++++++++++++++++++++++++-
 tests/generic/784     |  20 +++++
 tests/generic/784.out |   2 +
 4 files changed, 227 insertions(+), 2 deletions(-)

diff --git a/common/locktest b/common/locktest
index 12b5c27e0c03ad4c60985e3882026fce04e7330e..9344c43d8ee97679b49357b4e75de89ad56221ff 100644
--- a/common/locktest
+++ b/common/locktest
@@ -101,3 +101,8 @@ _run_dirdelegtest() {
 	TESTFILE=$DELEGDIR
 	_run_generic "-D"
 }
+
+_run_filedelegtest() {
+	TESTFILE=$DELEGDIR
+	_run_generic "-F"
+}
diff --git a/src/locktest.c b/src/locktest.c
index eb40dce3f1b28ef34752518808ec2f3999cd4257..54ee1f07539ef08e768d2c809c40327f315d43e7 100644
--- a/src/locktest.c
+++ b/src/locktest.c
@@ -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/784 b/tests/generic/784
new file mode 100755
index 0000000000000000000000000000000000000000..6c20bac9b7fb719af05afd507849213359f9ca0f
--- /dev/null
+++ b/tests/generic/784
@@ -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 784
+#
+# 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/784.out b/tests/generic/784.out
new file mode 100644
index 0000000000000000000000000000000000000000..7b499e08fed85d0430ecad03c824f745cb42ec44
--- /dev/null
+++ b/tests/generic/784.out
@@ -0,0 +1,2 @@
+QA output created by 784
+success!

-- 
2.52.0


  parent reply	other threads:[~2025-12-03 15:43 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-03 15:43 [PATCH fstests v3 0/3] generic: new testcases for delegation support Jeff Layton
2025-12-03 15:43 ` [PATCH fstests v3 1/3] common/rc: clean up after the _require_test_fcntl_setlease() test Jeff Layton
2025-12-05 17:14   ` Zorro Lang
2025-12-03 15:43 ` [PATCH fstests v3 2/3] generic: add tests for directory delegations Jeff Layton
2025-12-05 17:15   ` Zorro Lang
2025-12-03 15:43 ` Jeff Layton [this message]
2025-12-05 17:25   ` [PATCH fstests v3 3/3] generic: add tests for file delegations Zorro Lang
2025-12-06 18:35     ` Jeff Layton

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251203-dir-deleg-v3-3-be55fbf2ad53@kernel.org \
    --to=jlayton@kernel.org \
    --cc=brauner@kernel.org \
    --cc=fstests@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=zlang@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).