linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH fstests v3 0/3] generic: new testcases for delegation support
@ 2025-12-03 15:43 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
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Jeff Layton @ 2025-12-03 15:43 UTC (permalink / raw)
  To: fstests; +Cc: linux-fsdevel, Zorro Lang, Christian Brauner, Jeff Layton

The main difference from the last set are just changes to address
Zorro's review comments.

Support for delegations has been merged into mainline, but there is
still an outstanding bugfix [1] that will need to be merged before the
file delegation test passes properly.

To: fstests@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: Zorro Lang <zlang@redhat.com>
Cc: Christian Brauner <brauner@kernel.org>
Signed-off-by: Jeff Layton <jlayton@kernel.org>

[1]: https://lore.kernel.org/linux-fsdevel/20251201-dir-deleg-ro-v1-0-2e32cf2df9b7@kernel.org/

---
Changes in v3:
- Clean up per Zorro's comments
- Use ./new to generate boilerplate test scripts
- Add both tests to "locks" and "quick" groups
- Link to v2: https://lore.kernel.org/r/20251119-dir-deleg-v2-0-f952ba272384@kernel.org

Changes in v2:
- Add tests for file delegations
- Clean up after testing whether leases are supported
- Link to v1: https://lore.kernel.org/r/20251111-dir-deleg-v1-1-d476e0bc1ee5@kernel.org

---
Jeff Layton (3):
      common/rc: clean up after the _require_test_fcntl_setlease() test
      generic: add tests for directory delegations
      generic: add tests for file delegations

 common/locktest       |  19 +-
 common/rc             |  11 +
 src/locktest.c        | 621 +++++++++++++++++++++++++++++++++++++++++++++++++-
 tests/generic/783     |  19 ++
 tests/generic/783.out |   2 +
 tests/generic/784     |  20 ++
 tests/generic/784.out |   2 +
 7 files changed, 679 insertions(+), 15 deletions(-)
---
base-commit: 5b75444bc9123f261e0aa95f72328af4c827786a
change-id: 20251111-dir-deleg-ff05013ca540

Best regards,
-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH fstests v3 1/3] common/rc: clean up after the _require_test_fcntl_setlease() test
  2025-12-03 15:43 [PATCH fstests v3 0/3] generic: new testcases for delegation support Jeff Layton
@ 2025-12-03 15:43 ` 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-03 15:43 ` [PATCH fstests v3 3/3] generic: add tests for file delegations Jeff Layton
  2 siblings, 1 reply; 8+ messages in thread
From: Jeff Layton @ 2025-12-03 15:43 UTC (permalink / raw)
  To: fstests; +Cc: linux-fsdevel, Zorro Lang, Christian Brauner, Jeff Layton

Remove setlease_testfile after validating whether a lease can be set.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 common/rc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/common/rc b/common/rc
index a10ac17746a3ca4d9aca1d4ce434ccd6f39838b9..116216ca8aeb4e53f3e0d741cc99a050cb3a7462 100644
--- a/common/rc
+++ b/common/rc
@@ -4656,6 +4656,7 @@ _require_test_fcntl_setlease()
 	touch $TEST_DIR/setlease_testfile
 	$here/src/locktest -t $TEST_DIR/setlease_testfile >/dev/null 2>&1
 	local ret=$?
+	rm -f $TEST_DIR/setlease_testfile
 	[ $ret -eq 22 ] && _notrun "Require fcntl setlease support"
 	[ "$FSTYP" == "nfs" -a $ret -eq 11 ] && \
 		_notrun "NFS requires delegation before setlease"

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH fstests v3 2/3] generic: add tests for directory delegations
  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-03 15:43 ` Jeff Layton
  2025-12-05 17:15   ` Zorro Lang
  2025-12-03 15:43 ` [PATCH fstests v3 3/3] generic: add tests for file delegations Jeff Layton
  2 siblings, 1 reply; 8+ messages in thread
From: Jeff Layton @ 2025-12-03 15:43 UTC (permalink / raw)
  To: fstests; +Cc: linux-fsdevel, Zorro Lang, Christian Brauner, Jeff Layton

With the advent of directory delegation support coming to the kernel,
add support for testing them to the existing locktest.c program, and add
testcases for all of the different ways that they can be broken.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 common/locktest       |  14 +-
 common/rc             |  10 ++
 src/locktest.c        | 423 ++++++++++++++++++++++++++++++++++++++++++++++++--
 tests/generic/783     |  19 +++
 tests/generic/783.out |   2 +
 5 files changed, 453 insertions(+), 15 deletions(-)

diff --git a/common/locktest b/common/locktest
index 61e7dd42785dd3d4f21050e10bd5ec9d76a3b15a..12b5c27e0c03ad4c60985e3882026fce04e7330e 100644
--- a/common/locktest
+++ b/common/locktest
@@ -6,6 +6,7 @@ SERVER_LOG=$TEST_DIR/server.out
 SERVER_PORT=$TEST_DIR/server.port
 CLIENT_LOG=$TEST_DIR/client.out
 TESTFILE=$TEST_DIR/lock_file
+DELEGDIR=$TEST_DIR/dirdeleg
 client_pid=""
 server_pid=""
 
@@ -13,7 +14,7 @@ _cleanup()
 {
 	kill $client_pid > /dev/null 2>&1
 	kill $server_pid > /dev/null 2>&1
-	rm -f $TESTFILE
+	rm -rf $TESTFILE $DELEGDIR
 }
 
 _dump_logs_fail()
@@ -42,8 +43,6 @@ _run_generic() {
 	rm -f $CLIENT_LOG
 	touch $CLIENT_LOG
 
-	touch $TESTFILE
-
 	# Start the server
 	$here/src/locktest $mode $TESTFILE 2> $SERVER_LOG 1> $SERVER_PORT &
 	server_pid=$!
@@ -87,9 +86,18 @@ _run_generic() {
 }
 
 _run_locktest() {
+	touch $TESTFILE
+
 	_run_generic ""
 }
 
 _run_leasetest() {
+	touch $TESTFILE
+
 	_run_generic "-L"
 }
+
+_run_dirdelegtest() {
+	TESTFILE=$DELEGDIR
+	_run_generic "-D"
+}
diff --git a/common/rc b/common/rc
index 116216ca8aeb4e53f3e0d741cc99a050cb3a7462..8df2b387a8a85c7dafeea02f9f9422f52a7d0d01 100644
--- a/common/rc
+++ b/common/rc
@@ -4662,6 +4662,16 @@ _require_test_fcntl_setlease()
 		_notrun "NFS requires delegation before setlease"
 }
 
+_require_test_fcntl_setdeleg()
+{
+	_require_test_program "locktest"
+	mkdir $TEST_DIR/setdeleg_testdir
+	$here/src/locktest -t -D $TEST_DIR/setdeleg_testdir >/dev/null 2>&1
+	local ret=$?
+	rm -rf $TEST_DIR/setdeleg_testdir
+	[ $ret -eq 22 ] && _notrun "Require fcntl setdeleg support"
+}
+
 _require_ofd_locks()
 {
 	# Give a test run by getlk wrlck on testfile.
diff --git a/src/locktest.c b/src/locktest.c
index a6cf3b1d5a99dc2cf78f7ceb79622e1ab135c42c..eb40dce3f1b28ef34752518808ec2f3999cd4257 100644
--- a/src/locktest.c
+++ b/src/locktest.c
@@ -28,6 +28,7 @@
 #include <errno.h>
 #include <string.h>
 #include <signal.h>
+#include <limits.h>
 
 #define     HEX_2_ASC(x)    ((x) > 9) ? (x)-10+'a' : (x)+'0'
 #define 	FILE_SIZE	1024
@@ -60,8 +61,6 @@ extern int h_errno;
 #define SOCKET_CLOSE(S)     (close(S))
 #define INVALID_SOCKET      -1
 
-#define O_BINARY            0
-       
 #define HANDLE              int
 #define INVALID_HANDLE      -1
 #define SEEK(H, O)          (lseek(H, O, SEEK_SET))
@@ -78,6 +77,17 @@ extern int h_errno;
 #define ALLOC_ALIGNED(S)    (memalign(65536, S)) 
 #define FREE_ALIGNED(P)     (free(P)) 
 
+#ifndef F_GETDELEG
+#define F_GETDELEG	(1024 + 15)
+#define F_SETDELEG	(1024 + 16)
+
+struct delegation {
+	uint32_t d_flags;
+	uint16_t d_type;
+	uint16_t __pad;
+};
+#endif
+
 static char	*prog;
 static char	*filename = 0;
 static int	debug = 0;
@@ -86,11 +96,14 @@ static int	port = 0;
 static int 	testnumber = -1;
 static int	saved_errno = 0;
 static int      got_sigio = 0;
+static int	lease_is_deleg = 0;
 
 static SOCKET	s_fd = -1;              /* listen socket    */
 static SOCKET	c_fd = -1;	        /* IPC socket       */
 static HANDLE	f_fd = INVALID_HANDLE;	/* shared file      */
 
+static char *child[] = { "child0", "child1" };
+
 #define 	CMD_WRLOCK	0
 #define 	CMD_RDLOCK	1
 #define		CMD_UNLOCK	2
@@ -103,9 +116,19 @@ static HANDLE	f_fd = INVALID_HANDLE;	/* shared file      */
 #define		CMD_SIGIO	9
 #define		CMD_WAIT_SIGIO	10
 #define		CMD_TRUNCATE	11
-
-#define		PASS 	1
-#define		FAIL	0
+#define		CMD_GETDELEG	12
+#define		CMD_SETDELEG	13
+#define		CMD_CREATE	14
+#define		CMD_UNLINK	15
+#define		CMD_RENAME	16
+#define		CMD_SYMLINK	17
+#define		CMD_MKNOD	18
+#define		CMD_CHMOD	19
+#define		CMD_MKDIR	20
+#define		CMD_RMDIR	21
+
+#define		PASS 	0
+#define		FAIL	1
 
 #define		SERVER	0
 #define		CLIENT	1
@@ -119,6 +142,7 @@ static HANDLE	f_fd = INVALID_HANDLE;	/* shared file      */
 #define		FLAGS		2 /* index 2 is also used for do_open() flag, see below */
 #define		ARG		FLAGS /* Arguments for Lease operations */
 #define		TIME		FLAGS /* Time for waiting on sigio */
+#define		ARG2		3 /* second argument for dir operations */
 
 static char *get_cmd_str(int cmd)
 {
@@ -135,6 +159,16 @@ static char *get_cmd_str(int cmd)
 		case CMD_SIGIO:    return "Setup SIGIO"; break;
 		case CMD_WAIT_SIGIO: return "Wait for SIGIO"; break;
 		case CMD_TRUNCATE: return "Truncate"; break;
+		case CMD_SETDELEG: return "Set Delegation"; break;
+		case CMD_GETDELEG: return "Get Delegation"; break;
+		case CMD_CREATE: return "Create"; break;
+		case CMD_UNLINK: return "Remove"; break;
+		case CMD_RENAME: return "Rename"; break;
+		case CMD_SYMLINK: return "Symlink"; break;
+		case CMD_MKNOD: return "Mknod"; break;
+		case CMD_CHMOD: return "Chmod"; break;
+		case CMD_MKDIR: return "Mkdir"; break;
+		case CMD_RMDIR: return "Rmdir"; break;
 	}
 	return "unknown";
 }
@@ -682,6 +716,123 @@ static int64_t lease_tests[][6] =
 		{0,0,0,0,0,CLIENT}
 	};
 
+char *dirdeleg_descriptions[] = {
+    /*  1 */"Take Read Lease",
+    /*  2 */"Write Lease Should Fail",
+    /*  3 */"Dir Lease Should Be Broken on Create",
+    /*  4 */"Dir Lease Should Be Broken on Unlink",
+    /*  5 */"Dir Lease Should Be Broken on Rename",
+    /*  6 */"Dir Lease Should Be Broken on Symlink",
+    /*  7 */"Dir Lease Should Be Broken on Mknod",
+    /*  8 */"Dir Lease Should Be Broken on Chmod",
+    /*  9 */"Dir Lease Should Be Broken on Mkdir",
+    /* 10 */"Dir Lease Should Be Broken on Rmdir",
+};
+
+static int64_t dirdeleg_tests[][6] =
+	/*	test #	Action	[offset|flags|arg]	length		expected	server/client */
+	/*			[sigio_wait_time]						*/
+{
+	/* Various tests to exercise leases */
+
+/* SECTION 1: Simple verification of being able to take leases */
+	/* Take Read Lease, and release it */
+	{1,	CMD_OPEN,	O_DIRECTORY|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	},
+
+	/* Write Lease should fail */
+	{2,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
+	{2,	CMD_SETDELEG,	F_WRLCK,		0,	FAIL,		SERVER	},
+	{2,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
+
+	/* Get SIGIO when dir lease is broken by a create */
+	{3,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
+	{3,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{3,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{3,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
+	{3,	CMD_CREATE,	0,			0,	PASS,		CLIENT	},
+	{3,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
+	{3,	CMD_UNLINK,	0,			0,	PASS,		SERVER	},
+	{3,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
+
+	/* Get SIGIO when dir lease is broken by unlink */
+	{4,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
+	{4,	CMD_CREATE,	0,			0,	PASS,		SERVER	},
+	{4,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{4,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{4,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
+	{4,	CMD_UNLINK,	0,			0,	PASS,		CLIENT	},
+	{4,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
+	{4,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
+
+	/* Get SIGIO when dir lease is broken by rename */
+	{5,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
+	{5,	CMD_CREATE,	0,			0,	PASS,		SERVER	},
+	{5,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{5,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{5,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
+	{5,	CMD_RENAME,	0,			1,	PASS,		CLIENT	},
+	{5,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
+	{5,	CMD_UNLINK,	1,			0,	PASS,		CLIENT	},
+	{5,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
+
+	/* Get SIGIO when dir lease is broken by symlink */
+	{6,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
+	{6,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{6,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{6,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
+	{6,	CMD_SYMLINK,	0,			1,	PASS,		CLIENT	},
+	{6,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
+	{6,	CMD_UNLINK,	0,			0,	PASS,		CLIENT	},
+	{6,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
+
+	/* Get SIGIO when dir lease is broken by mknod */
+	{7,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
+	{7,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{7,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{7,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
+	{7,	CMD_MKNOD,	0,			0,	PASS,		CLIENT	},
+	{7,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
+	{7,	CMD_UNLINK,	0,			0,	PASS,		CLIENT	},
+	{7,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
+
+	/* Get SIGIO when dir lease is broken by chmod */
+	{8,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
+	{8,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{8,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{8,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
+	{8,	CMD_CHMOD,	0775,			0,	PASS,		CLIENT	},
+	{8,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
+	{8,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
+
+	/* Get SIGIO when dir lease is broken by mkdir */
+	{9,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
+	{9,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{9,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{9,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
+	{9,	CMD_MKDIR,	0,			0,	PASS,		CLIENT	},
+	{9,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
+	{9,	CMD_RMDIR,	0,			0,	PASS,		SERVER	},
+	{9,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
+
+	/* Get SIGIO when dir lease is broken by rmdir */
+	{10,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
+	{10,	CMD_MKDIR,	0,			0,	PASS,		SERVER	},
+	{10,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{10,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
+	{10,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
+	{10,	CMD_RMDIR,	0,			0,	PASS,		CLIENT	},
+	{10,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
+	{10,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
+
+	/* indicate end of array */
+	{0,0,0,0,0,SERVER},
+	{0,0,0,0,0,CLIENT}
+};
+
 static struct {
     int32_t		test;
     int32_t		command;
@@ -763,9 +914,14 @@ again:
 
 void release_lease(int fd)
 {
+	struct delegation deleg = { .d_type = F_UNLCK };
 	int rc;
 
-	rc = fcntl(fd, F_SETLEASE, F_UNLCK);
+	if (lease_is_deleg)
+		rc = fcntl(fd, F_SETDELEG, &deleg);
+	else
+		rc = fcntl(fd, F_SETLEASE, F_UNLCK);
+
 	if (rc != 0)
 		fprintf(stderr, "%s Failed to remove lease %d : %d %s\n",
 			__FILE__, rc, errno, strerror(errno));
@@ -826,9 +982,39 @@ int do_wait_sigio(int32_t time)
     return (got_sigio ? PASS: FAIL);
 }
 
+int create_directory(void)
+{
+	int ret;
+	struct stat st;
+
+	ret = mkdir(filename, 0777);
+	if (ret == 0)
+		return PASS;
+
+	if (errno != EEXIST) {
+		perror("directory create");
+		return FAIL;
+	}
+
+	ret = stat(filename, &st);
+	if (ret < 0) {
+		perror("stat");
+		return FAIL;
+	}
+
+	if (S_ISDIR(st.st_mode))
+		return PASS;
+
+	fprintf(stderr, "%s is not a directory\n", filename);
+	return FAIL;
+}
+
 int do_open(int flag)
 {
-    int flags = flag|O_CREAT|O_BINARY;
+    int flags = flag;
+
+    if (!(flag & O_DIRECTORY))
+	flags |= O_CREAT;
 
     if(debug > 1)
 	fprintf(stderr, "do_open %s 0x%x\n", filename, flags);
@@ -841,6 +1027,103 @@ int do_open(int flag)
     return PASS;
 }
 
+int do_create(int idx)
+{
+	int fd;
+
+	fd = openat(f_fd, child[idx], O_WRONLY|O_CREAT, 0666);
+	if (fd < 0) {
+		perror("openat");
+		return FAIL;
+	}
+	close(fd);
+	return PASS;
+}
+
+int do_unlink(int idx)
+{
+	int ret;
+
+	ret = unlinkat(f_fd, child[idx], 0);
+	if (ret < 0) {
+		perror("unlink");
+		return FAIL;
+	}
+	return PASS;
+}
+
+int do_rename(int old, int new)
+{
+	int ret;
+
+	ret = renameat(f_fd, child[old], f_fd, child[new]);
+	if (ret < 0) {
+		perror("rename");
+		return FAIL;
+	}
+	return PASS;
+}
+
+int do_symlink(int path, int target)
+{
+	int ret;
+
+	ret = symlinkat(child[target], f_fd, child[path]);
+	if (ret < 0) {
+		perror("symlink");
+		return FAIL;
+	}
+	return PASS;
+}
+
+int do_mknod(int idx)
+{
+	int ret;
+
+	ret = mknodat(f_fd, child[idx], S_IFREG, 0);
+	if (ret < 0) {
+		perror("mknod");
+		return FAIL;
+	}
+	return PASS;
+}
+
+int do_mkdir(int idx)
+{
+	int ret;
+
+	ret = mkdirat(f_fd, child[idx], 0777);
+	if (ret < 0) {
+		perror("mkdir");
+		return FAIL;
+	}
+	return PASS;
+}
+
+int do_rmdir(int idx)
+{
+	int ret;
+
+	ret = unlinkat(f_fd, child[idx], AT_REMOVEDIR);
+	if (ret < 0) {
+		perror("mkdir");
+		return FAIL;
+	}
+	return PASS;
+}
+
+int do_chmod(int mode)
+{
+	int ret;
+
+	ret = chmod(filename, mode);
+	if (ret < 0) {
+		perror("chmod");
+		return FAIL;
+	}
+	return PASS;
+}
+
 static int do_lock(int cmd, int type, int start, int length)
 {
     int ret;
@@ -884,6 +1167,7 @@ static int do_lease(int cmd, int arg, int expected)
 
     errno = 0;
 
+    lease_is_deleg = 0;
     ret = fcntl(f_fd, cmd, arg);
     saved_errno = errno;
 
@@ -897,6 +1181,37 @@ static int do_lease(int cmd, int arg, int expected)
     return(ret==0?PASS:FAIL);
 }
 
+static int do_deleg(int cmd, unsigned short arg, int expected)
+{
+    struct delegation deleg = { .d_type = arg };
+    int ret;
+
+    if(debug > 1)
+	fprintf(stderr, "do_deleg: cmd=%d arg=%d exp=%X\n",
+		cmd, arg, expected);
+
+    if (f_fd < 0)
+	return f_fd;
+
+    errno = 0;
+
+    lease_is_deleg = 1;
+    ret = fcntl(f_fd, cmd, &deleg);
+    saved_errno = errno;
+
+    if (cmd == F_GETDELEG && ret == 0)
+	    ret = deleg.d_type;
+
+    if (expected && (expected == ret))
+	    ret = 0;
+
+    if(ret)
+	fprintf(stderr, "%s do_deleg: ret = %d, errno = %d (%s)\n",
+		__FILE__, ret, errno, strerror(errno));
+
+    return(ret==0?PASS:FAIL);
+}
+
 int do_close(void)
 {	
     if(debug > 1) {
@@ -1020,6 +1335,7 @@ main(int argc, char *argv[])
 {
     int		i, sts;
     int		c;
+    int		openflags;
     struct sockaddr_in	myAddr;
     struct linger	noLinger = {1, 0};
     char	*host = NULL;
@@ -1030,6 +1346,7 @@ main(int argc, char *argv[])
     extern int	optind;
     int fail_count = 0;
     int run_leases = 0;
+    int run_dirdelegs = 0;
     int test_setlease = 0;
     
     atexit(cleanup);
@@ -1043,13 +1360,17 @@ main(int argc, char *argv[])
 	    prog = p+1;
     }
 
-    while ((c = getopt(argc, argv, "dLn:h:p:t?")) != EOF) {
+    while ((c = getopt(argc, argv, "dDLn:h:p:t?")) != EOF) {
 	switch (c) {
 
 	case 'd':	/* debug flag */
 	    debug++;
 	    break;
 
+	case 'D':
+	    run_dirdelegs = 1;
+	    break;
+
 	case 'L':	/* Lease testing */
 	    run_leases = 1;
 	    break;
@@ -1090,13 +1411,29 @@ main(int argc, char *argv[])
     }
 
     filename=argv[optind];
+
+    if (run_dirdelegs && create_directory() == FAIL)
+	    exit(1);
+
     if (debug)
 	fprintf(stderr, "Working on file : %s\n", filename);
-    if (do_open(O_RDWR) == FAIL)
+
+    if (run_dirdelegs) {
+	openflags = O_RDONLY | O_DIRECTORY;
+    } else {
+	openflags = O_RDWR;
+    }
+
+    if (do_open(openflags) == FAIL)
 	exit(1);
 
     if (test_setlease == 1) {
-	fcntl(f_fd, F_SETLEASE, F_UNLCK);
+	struct delegation deleg = { .d_type = F_UNLCK };
+
+	if (run_dirdelegs)
+		fcntl(f_fd, F_SETDELEG, &deleg);
+	else
+		fcntl(f_fd, F_SETLEASE, F_UNLCK);
 	saved_errno = errno;
 	close(f_fd);
 	exit(saved_errno);
@@ -1220,7 +1557,7 @@ main(int argc, char *argv[])
 	SRAND(6789L);
     }
 
-    if (server)
+    if (server && !run_dirdelegs)
 	/* only server need do shared file */
 	initialize(f_fd);
 
@@ -1229,7 +1566,9 @@ main(int argc, char *argv[])
      *
      * real work is in here ...
      */
-    if (run_leases)
+    if (run_dirdelegs)
+	fail_count = run(dirdeleg_tests, dirdeleg_descriptions);
+    else if (run_leases)
 	fail_count = run(lease_tests, lease_descriptions);
     else
 	fail_count = run(lock_tests, lock_descriptions);
@@ -1304,6 +1643,36 @@ int run(int64_t tests[][6], char *descriptions[])
 			case CMD_TRUNCATE:
 			    result = do_truncate(tests[index][OFFSET]);
 			    break;
+			case CMD_SETDELEG:
+			    result = do_deleg(F_SETDELEG, tests[index][ARG], 0);
+			    break;
+			case CMD_GETDELEG:
+			    result = do_deleg(F_GETDELEG, tests[index][ARG], tests[index][ARG]);
+			    break;
+			case CMD_CREATE:
+			    result = do_create(tests[index][ARG]);
+			    break;
+			case CMD_UNLINK:
+			    result = do_unlink(tests[index][ARG]);
+			    break;
+			case CMD_RENAME:
+			    result = do_rename(tests[index][ARG], tests[index][ARG2]);
+			    break;
+			case CMD_SYMLINK:
+			    result = do_symlink(tests[index][ARG], tests[index][ARG2]);
+			    break;
+			case CMD_MKNOD:
+			    result = do_mknod(tests[index][ARG]);
+			    break;
+			case CMD_CHMOD:
+			    result = do_chmod(tests[index][ARG]);
+			    break;
+			case CMD_MKDIR:
+			    result = do_mkdir(tests[index][ARG]);
+			    break;
+			case CMD_RMDIR:
+			    result = do_rmdir(tests[index][ARG]);
+			    break;
 		    }
 		    if( result != tests[index][RESULT]) {
 			fail_flag++;
@@ -1418,6 +1787,36 @@ int run(int64_t tests[][6], char *descriptions[])
 		case CMD_TRUNCATE:
 		    result = do_truncate(ctl.offset);
 		    break;
+		case CMD_SETDELEG:
+		    result = do_deleg(F_SETDELEG, ctl.offset, 0);
+		    break;
+		case CMD_GETDELEG:
+		    result = do_deleg(F_GETDELEG, ctl.offset, ctl.offset);
+		    break;
+		case CMD_CREATE:
+		    result = do_create(ctl.offset);
+		    break;
+		case CMD_UNLINK:
+		    result = do_unlink(ctl.offset);
+		    break;
+		case CMD_RENAME:
+		    result = do_rename(ctl.offset, ctl.length);
+		    break;
+		case CMD_SYMLINK:
+		    result = do_symlink(ctl.offset, ctl.length);
+		    break;
+		case CMD_MKNOD:
+		    result = do_mknod(ctl.offset);
+		    break;
+		case CMD_CHMOD:
+		    result = do_chmod(ctl.offset);
+		    break;
+		case CMD_MKDIR:
+		    result = do_mkdir(ctl.offset);
+		    break;
+		case CMD_RMDIR:
+		    result = do_rmdir(ctl.offset);
+		    break;
 	    }
 	    if( result != ctl.result ) {
 		fprintf(stderr,"Failure in %d:%s\n",
diff --git a/tests/generic/783 b/tests/generic/783
new file mode 100755
index 0000000000000000000000000000000000000000..92cd1fe83b6f8d80f7449a91d03a8dfd0238e826
--- /dev/null
+++ b/tests/generic/783
@@ -0,0 +1,19 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 Jeff Layton <jlayton@kernel.org>.  All Rights Reserved.
+#
+# FS QA Test 783
+#
+# Test directory delegation support
+#
+. ./common/preamble
+_begin_fstest auto locks quick
+
+. ./common/filter
+. ./common/locktest
+
+_require_test
+_require_test_fcntl_setdeleg
+
+_run_dirdelegtest
+_exit 0
diff --git a/tests/generic/783.out b/tests/generic/783.out
new file mode 100644
index 0000000000000000000000000000000000000000..4577997e1ff1fa2062e88f88bf94337db37a2e34
--- /dev/null
+++ b/tests/generic/783.out
@@ -0,0 +1,2 @@
+QA output created by 783
+success!

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH fstests v3 3/3] generic: add tests for file delegations
  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-03 15:43 ` [PATCH fstests v3 2/3] generic: add tests for directory delegations Jeff Layton
@ 2025-12-03 15:43 ` Jeff Layton
  2025-12-05 17:25   ` Zorro Lang
  2 siblings, 1 reply; 8+ messages in thread
From: Jeff Layton @ 2025-12-03 15:43 UTC (permalink / raw)
  To: fstests; +Cc: linux-fsdevel, Zorro Lang, Christian Brauner, Jeff Layton

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


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH fstests v3 1/3] common/rc: clean up after the _require_test_fcntl_setlease() test
  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
  0 siblings, 0 replies; 8+ messages in thread
From: Zorro Lang @ 2025-12-05 17:14 UTC (permalink / raw)
  To: Jeff Layton; +Cc: fstests, linux-fsdevel, Christian Brauner

On Wed, Dec 03, 2025 at 10:43:07AM -0500, Jeff Layton wrote:
> Remove setlease_testfile after validating whether a lease can be set.
> 
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---

Thanks Jeff, this version is good to me. As this feature has been in mainline
linux, I'd like to merge this patchset in next fstests release to get this
coverage.

Reviewed-by: Zorro Lang <zlang@redhat.com>

>  common/rc | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/common/rc b/common/rc
> index a10ac17746a3ca4d9aca1d4ce434ccd6f39838b9..116216ca8aeb4e53f3e0d741cc99a050cb3a7462 100644
> --- a/common/rc
> +++ b/common/rc
> @@ -4656,6 +4656,7 @@ _require_test_fcntl_setlease()
>  	touch $TEST_DIR/setlease_testfile
>  	$here/src/locktest -t $TEST_DIR/setlease_testfile >/dev/null 2>&1
>  	local ret=$?
> +	rm -f $TEST_DIR/setlease_testfile
>  	[ $ret -eq 22 ] && _notrun "Require fcntl setlease support"
>  	[ "$FSTYP" == "nfs" -a $ret -eq 11 ] && \
>  		_notrun "NFS requires delegation before setlease"
> 
> -- 
> 2.52.0
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH fstests v3 2/3] generic: add tests for directory delegations
  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
  0 siblings, 0 replies; 8+ messages in thread
From: Zorro Lang @ 2025-12-05 17:15 UTC (permalink / raw)
  To: Jeff Layton; +Cc: fstests, linux-fsdevel, Christian Brauner

On Wed, Dec 03, 2025 at 10:43:08AM -0500, Jeff Layton wrote:
> With the advent of directory delegation support coming to the kernel,
> add support for testing them to the existing locktest.c program, and add
> testcases for all of the different ways that they can be broken.
> 
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---

This version is good to me,

Reviewed-by: Zorro Lang <zlang@redhat.com>

>  common/locktest       |  14 +-
>  common/rc             |  10 ++
>  src/locktest.c        | 423 ++++++++++++++++++++++++++++++++++++++++++++++++--
>  tests/generic/783     |  19 +++
>  tests/generic/783.out |   2 +
>  5 files changed, 453 insertions(+), 15 deletions(-)
> 
> diff --git a/common/locktest b/common/locktest
> index 61e7dd42785dd3d4f21050e10bd5ec9d76a3b15a..12b5c27e0c03ad4c60985e3882026fce04e7330e 100644
> --- a/common/locktest
> +++ b/common/locktest
> @@ -6,6 +6,7 @@ SERVER_LOG=$TEST_DIR/server.out
>  SERVER_PORT=$TEST_DIR/server.port
>  CLIENT_LOG=$TEST_DIR/client.out
>  TESTFILE=$TEST_DIR/lock_file
> +DELEGDIR=$TEST_DIR/dirdeleg
>  client_pid=""
>  server_pid=""
>  
> @@ -13,7 +14,7 @@ _cleanup()
>  {
>  	kill $client_pid > /dev/null 2>&1
>  	kill $server_pid > /dev/null 2>&1
> -	rm -f $TESTFILE
> +	rm -rf $TESTFILE $DELEGDIR
>  }
>  
>  _dump_logs_fail()
> @@ -42,8 +43,6 @@ _run_generic() {
>  	rm -f $CLIENT_LOG
>  	touch $CLIENT_LOG
>  
> -	touch $TESTFILE
> -
>  	# Start the server
>  	$here/src/locktest $mode $TESTFILE 2> $SERVER_LOG 1> $SERVER_PORT &
>  	server_pid=$!
> @@ -87,9 +86,18 @@ _run_generic() {
>  }
>  
>  _run_locktest() {
> +	touch $TESTFILE
> +
>  	_run_generic ""
>  }
>  
>  _run_leasetest() {
> +	touch $TESTFILE
> +
>  	_run_generic "-L"
>  }
> +
> +_run_dirdelegtest() {
> +	TESTFILE=$DELEGDIR
> +	_run_generic "-D"
> +}
> diff --git a/common/rc b/common/rc
> index 116216ca8aeb4e53f3e0d741cc99a050cb3a7462..8df2b387a8a85c7dafeea02f9f9422f52a7d0d01 100644
> --- a/common/rc
> +++ b/common/rc
> @@ -4662,6 +4662,16 @@ _require_test_fcntl_setlease()
>  		_notrun "NFS requires delegation before setlease"
>  }
>  
> +_require_test_fcntl_setdeleg()
> +{
> +	_require_test_program "locktest"
> +	mkdir $TEST_DIR/setdeleg_testdir
> +	$here/src/locktest -t -D $TEST_DIR/setdeleg_testdir >/dev/null 2>&1
> +	local ret=$?
> +	rm -rf $TEST_DIR/setdeleg_testdir
> +	[ $ret -eq 22 ] && _notrun "Require fcntl setdeleg support"
> +}
> +
>  _require_ofd_locks()
>  {
>  	# Give a test run by getlk wrlck on testfile.
> diff --git a/src/locktest.c b/src/locktest.c
> index a6cf3b1d5a99dc2cf78f7ceb79622e1ab135c42c..eb40dce3f1b28ef34752518808ec2f3999cd4257 100644
> --- a/src/locktest.c
> +++ b/src/locktest.c
> @@ -28,6 +28,7 @@
>  #include <errno.h>
>  #include <string.h>
>  #include <signal.h>
> +#include <limits.h>
>  
>  #define     HEX_2_ASC(x)    ((x) > 9) ? (x)-10+'a' : (x)+'0'
>  #define 	FILE_SIZE	1024
> @@ -60,8 +61,6 @@ extern int h_errno;
>  #define SOCKET_CLOSE(S)     (close(S))
>  #define INVALID_SOCKET      -1
>  
> -#define O_BINARY            0
> -       
>  #define HANDLE              int
>  #define INVALID_HANDLE      -1
>  #define SEEK(H, O)          (lseek(H, O, SEEK_SET))
> @@ -78,6 +77,17 @@ extern int h_errno;
>  #define ALLOC_ALIGNED(S)    (memalign(65536, S)) 
>  #define FREE_ALIGNED(P)     (free(P)) 
>  
> +#ifndef F_GETDELEG
> +#define F_GETDELEG	(1024 + 15)
> +#define F_SETDELEG	(1024 + 16)
> +
> +struct delegation {
> +	uint32_t d_flags;
> +	uint16_t d_type;
> +	uint16_t __pad;
> +};
> +#endif
> +
>  static char	*prog;
>  static char	*filename = 0;
>  static int	debug = 0;
> @@ -86,11 +96,14 @@ static int	port = 0;
>  static int 	testnumber = -1;
>  static int	saved_errno = 0;
>  static int      got_sigio = 0;
> +static int	lease_is_deleg = 0;
>  
>  static SOCKET	s_fd = -1;              /* listen socket    */
>  static SOCKET	c_fd = -1;	        /* IPC socket       */
>  static HANDLE	f_fd = INVALID_HANDLE;	/* shared file      */
>  
> +static char *child[] = { "child0", "child1" };
> +
>  #define 	CMD_WRLOCK	0
>  #define 	CMD_RDLOCK	1
>  #define		CMD_UNLOCK	2
> @@ -103,9 +116,19 @@ static HANDLE	f_fd = INVALID_HANDLE;	/* shared file      */
>  #define		CMD_SIGIO	9
>  #define		CMD_WAIT_SIGIO	10
>  #define		CMD_TRUNCATE	11
> -
> -#define		PASS 	1
> -#define		FAIL	0
> +#define		CMD_GETDELEG	12
> +#define		CMD_SETDELEG	13
> +#define		CMD_CREATE	14
> +#define		CMD_UNLINK	15
> +#define		CMD_RENAME	16
> +#define		CMD_SYMLINK	17
> +#define		CMD_MKNOD	18
> +#define		CMD_CHMOD	19
> +#define		CMD_MKDIR	20
> +#define		CMD_RMDIR	21
> +
> +#define		PASS 	0
> +#define		FAIL	1
>  
>  #define		SERVER	0
>  #define		CLIENT	1
> @@ -119,6 +142,7 @@ static HANDLE	f_fd = INVALID_HANDLE;	/* shared file      */
>  #define		FLAGS		2 /* index 2 is also used for do_open() flag, see below */
>  #define		ARG		FLAGS /* Arguments for Lease operations */
>  #define		TIME		FLAGS /* Time for waiting on sigio */
> +#define		ARG2		3 /* second argument for dir operations */
>  
>  static char *get_cmd_str(int cmd)
>  {
> @@ -135,6 +159,16 @@ static char *get_cmd_str(int cmd)
>  		case CMD_SIGIO:    return "Setup SIGIO"; break;
>  		case CMD_WAIT_SIGIO: return "Wait for SIGIO"; break;
>  		case CMD_TRUNCATE: return "Truncate"; break;
> +		case CMD_SETDELEG: return "Set Delegation"; break;
> +		case CMD_GETDELEG: return "Get Delegation"; break;
> +		case CMD_CREATE: return "Create"; break;
> +		case CMD_UNLINK: return "Remove"; break;
> +		case CMD_RENAME: return "Rename"; break;
> +		case CMD_SYMLINK: return "Symlink"; break;
> +		case CMD_MKNOD: return "Mknod"; break;
> +		case CMD_CHMOD: return "Chmod"; break;
> +		case CMD_MKDIR: return "Mkdir"; break;
> +		case CMD_RMDIR: return "Rmdir"; break;
>  	}
>  	return "unknown";
>  }
> @@ -682,6 +716,123 @@ static int64_t lease_tests[][6] =
>  		{0,0,0,0,0,CLIENT}
>  	};
>  
> +char *dirdeleg_descriptions[] = {
> +    /*  1 */"Take Read Lease",
> +    /*  2 */"Write Lease Should Fail",
> +    /*  3 */"Dir Lease Should Be Broken on Create",
> +    /*  4 */"Dir Lease Should Be Broken on Unlink",
> +    /*  5 */"Dir Lease Should Be Broken on Rename",
> +    /*  6 */"Dir Lease Should Be Broken on Symlink",
> +    /*  7 */"Dir Lease Should Be Broken on Mknod",
> +    /*  8 */"Dir Lease Should Be Broken on Chmod",
> +    /*  9 */"Dir Lease Should Be Broken on Mkdir",
> +    /* 10 */"Dir Lease Should Be Broken on Rmdir",
> +};
> +
> +static int64_t dirdeleg_tests[][6] =
> +	/*	test #	Action	[offset|flags|arg]	length		expected	server/client */
> +	/*			[sigio_wait_time]						*/
> +{
> +	/* Various tests to exercise leases */
> +
> +/* SECTION 1: Simple verification of being able to take leases */
> +	/* Take Read Lease, and release it */
> +	{1,	CMD_OPEN,	O_DIRECTORY|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	},
> +
> +	/* Write Lease should fail */
> +	{2,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
> +	{2,	CMD_SETDELEG,	F_WRLCK,		0,	FAIL,		SERVER	},
> +	{2,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
> +
> +	/* Get SIGIO when dir lease is broken by a create */
> +	{3,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
> +	{3,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{3,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{3,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
> +	{3,	CMD_CREATE,	0,			0,	PASS,		CLIENT	},
> +	{3,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
> +	{3,	CMD_UNLINK,	0,			0,	PASS,		SERVER	},
> +	{3,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
> +
> +	/* Get SIGIO when dir lease is broken by unlink */
> +	{4,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
> +	{4,	CMD_CREATE,	0,			0,	PASS,		SERVER	},
> +	{4,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{4,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{4,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
> +	{4,	CMD_UNLINK,	0,			0,	PASS,		CLIENT	},
> +	{4,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
> +	{4,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
> +
> +	/* Get SIGIO when dir lease is broken by rename */
> +	{5,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
> +	{5,	CMD_CREATE,	0,			0,	PASS,		SERVER	},
> +	{5,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{5,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{5,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
> +	{5,	CMD_RENAME,	0,			1,	PASS,		CLIENT	},
> +	{5,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
> +	{5,	CMD_UNLINK,	1,			0,	PASS,		CLIENT	},
> +	{5,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
> +
> +	/* Get SIGIO when dir lease is broken by symlink */
> +	{6,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
> +	{6,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{6,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{6,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
> +	{6,	CMD_SYMLINK,	0,			1,	PASS,		CLIENT	},
> +	{6,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
> +	{6,	CMD_UNLINK,	0,			0,	PASS,		CLIENT	},
> +	{6,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
> +
> +	/* Get SIGIO when dir lease is broken by mknod */
> +	{7,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
> +	{7,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{7,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{7,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
> +	{7,	CMD_MKNOD,	0,			0,	PASS,		CLIENT	},
> +	{7,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
> +	{7,	CMD_UNLINK,	0,			0,	PASS,		CLIENT	},
> +	{7,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
> +
> +	/* Get SIGIO when dir lease is broken by chmod */
> +	{8,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
> +	{8,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{8,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{8,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
> +	{8,	CMD_CHMOD,	0775,			0,	PASS,		CLIENT	},
> +	{8,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
> +	{8,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
> +
> +	/* Get SIGIO when dir lease is broken by mkdir */
> +	{9,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
> +	{9,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{9,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{9,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
> +	{9,	CMD_MKDIR,	0,			0,	PASS,		CLIENT	},
> +	{9,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
> +	{9,	CMD_RMDIR,	0,			0,	PASS,		SERVER	},
> +	{9,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
> +
> +	/* Get SIGIO when dir lease is broken by rmdir */
> +	{10,	CMD_OPEN,	O_DIRECTORY|O_RDONLY,	0,	PASS,		SERVER	},
> +	{10,	CMD_MKDIR,	0,			0,	PASS,		SERVER	},
> +	{10,	CMD_SETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{10,	CMD_GETDELEG,	F_RDLCK,		0,	PASS,		SERVER	},
> +	{10,	CMD_SIGIO,	0,			0,	PASS,		SERVER	},
> +	{10,	CMD_RMDIR,	0,			0,	PASS,		CLIENT	},
> +	{10,	CMD_WAIT_SIGIO,	5,			0,	PASS,		SERVER	},
> +	{10,	CMD_CLOSE,	0,			0,	PASS,		SERVER	},
> +
> +	/* indicate end of array */
> +	{0,0,0,0,0,SERVER},
> +	{0,0,0,0,0,CLIENT}
> +};
> +
>  static struct {
>      int32_t		test;
>      int32_t		command;
> @@ -763,9 +914,14 @@ again:
>  
>  void release_lease(int fd)
>  {
> +	struct delegation deleg = { .d_type = F_UNLCK };
>  	int rc;
>  
> -	rc = fcntl(fd, F_SETLEASE, F_UNLCK);
> +	if (lease_is_deleg)
> +		rc = fcntl(fd, F_SETDELEG, &deleg);
> +	else
> +		rc = fcntl(fd, F_SETLEASE, F_UNLCK);
> +
>  	if (rc != 0)
>  		fprintf(stderr, "%s Failed to remove lease %d : %d %s\n",
>  			__FILE__, rc, errno, strerror(errno));
> @@ -826,9 +982,39 @@ int do_wait_sigio(int32_t time)
>      return (got_sigio ? PASS: FAIL);
>  }
>  
> +int create_directory(void)
> +{
> +	int ret;
> +	struct stat st;
> +
> +	ret = mkdir(filename, 0777);
> +	if (ret == 0)
> +		return PASS;
> +
> +	if (errno != EEXIST) {
> +		perror("directory create");
> +		return FAIL;
> +	}
> +
> +	ret = stat(filename, &st);
> +	if (ret < 0) {
> +		perror("stat");
> +		return FAIL;
> +	}
> +
> +	if (S_ISDIR(st.st_mode))
> +		return PASS;
> +
> +	fprintf(stderr, "%s is not a directory\n", filename);
> +	return FAIL;
> +}
> +
>  int do_open(int flag)
>  {
> -    int flags = flag|O_CREAT|O_BINARY;
> +    int flags = flag;
> +
> +    if (!(flag & O_DIRECTORY))
> +	flags |= O_CREAT;
>  
>      if(debug > 1)
>  	fprintf(stderr, "do_open %s 0x%x\n", filename, flags);
> @@ -841,6 +1027,103 @@ int do_open(int flag)
>      return PASS;
>  }
>  
> +int do_create(int idx)
> +{
> +	int fd;
> +
> +	fd = openat(f_fd, child[idx], O_WRONLY|O_CREAT, 0666);
> +	if (fd < 0) {
> +		perror("openat");
> +		return FAIL;
> +	}
> +	close(fd);
> +	return PASS;
> +}
> +
> +int do_unlink(int idx)
> +{
> +	int ret;
> +
> +	ret = unlinkat(f_fd, child[idx], 0);
> +	if (ret < 0) {
> +		perror("unlink");
> +		return FAIL;
> +	}
> +	return PASS;
> +}
> +
> +int do_rename(int old, int new)
> +{
> +	int ret;
> +
> +	ret = renameat(f_fd, child[old], f_fd, child[new]);
> +	if (ret < 0) {
> +		perror("rename");
> +		return FAIL;
> +	}
> +	return PASS;
> +}
> +
> +int do_symlink(int path, int target)
> +{
> +	int ret;
> +
> +	ret = symlinkat(child[target], f_fd, child[path]);
> +	if (ret < 0) {
> +		perror("symlink");
> +		return FAIL;
> +	}
> +	return PASS;
> +}
> +
> +int do_mknod(int idx)
> +{
> +	int ret;
> +
> +	ret = mknodat(f_fd, child[idx], S_IFREG, 0);
> +	if (ret < 0) {
> +		perror("mknod");
> +		return FAIL;
> +	}
> +	return PASS;
> +}
> +
> +int do_mkdir(int idx)
> +{
> +	int ret;
> +
> +	ret = mkdirat(f_fd, child[idx], 0777);
> +	if (ret < 0) {
> +		perror("mkdir");
> +		return FAIL;
> +	}
> +	return PASS;
> +}
> +
> +int do_rmdir(int idx)
> +{
> +	int ret;
> +
> +	ret = unlinkat(f_fd, child[idx], AT_REMOVEDIR);
> +	if (ret < 0) {
> +		perror("mkdir");
> +		return FAIL;
> +	}
> +	return PASS;
> +}
> +
> +int do_chmod(int mode)
> +{
> +	int ret;
> +
> +	ret = chmod(filename, mode);
> +	if (ret < 0) {
> +		perror("chmod");
> +		return FAIL;
> +	}
> +	return PASS;
> +}
> +
>  static int do_lock(int cmd, int type, int start, int length)
>  {
>      int ret;
> @@ -884,6 +1167,7 @@ static int do_lease(int cmd, int arg, int expected)
>  
>      errno = 0;
>  
> +    lease_is_deleg = 0;
>      ret = fcntl(f_fd, cmd, arg);
>      saved_errno = errno;
>  
> @@ -897,6 +1181,37 @@ static int do_lease(int cmd, int arg, int expected)
>      return(ret==0?PASS:FAIL);
>  }
>  
> +static int do_deleg(int cmd, unsigned short arg, int expected)
> +{
> +    struct delegation deleg = { .d_type = arg };
> +    int ret;
> +
> +    if(debug > 1)
> +	fprintf(stderr, "do_deleg: cmd=%d arg=%d exp=%X\n",
> +		cmd, arg, expected);
> +
> +    if (f_fd < 0)
> +	return f_fd;
> +
> +    errno = 0;
> +
> +    lease_is_deleg = 1;
> +    ret = fcntl(f_fd, cmd, &deleg);
> +    saved_errno = errno;
> +
> +    if (cmd == F_GETDELEG && ret == 0)
> +	    ret = deleg.d_type;
> +
> +    if (expected && (expected == ret))
> +	    ret = 0;
> +
> +    if(ret)
> +	fprintf(stderr, "%s do_deleg: ret = %d, errno = %d (%s)\n",
> +		__FILE__, ret, errno, strerror(errno));
> +
> +    return(ret==0?PASS:FAIL);
> +}
> +
>  int do_close(void)
>  {	
>      if(debug > 1) {
> @@ -1020,6 +1335,7 @@ main(int argc, char *argv[])
>  {
>      int		i, sts;
>      int		c;
> +    int		openflags;
>      struct sockaddr_in	myAddr;
>      struct linger	noLinger = {1, 0};
>      char	*host = NULL;
> @@ -1030,6 +1346,7 @@ main(int argc, char *argv[])
>      extern int	optind;
>      int fail_count = 0;
>      int run_leases = 0;
> +    int run_dirdelegs = 0;
>      int test_setlease = 0;
>      
>      atexit(cleanup);
> @@ -1043,13 +1360,17 @@ main(int argc, char *argv[])
>  	    prog = p+1;
>      }
>  
> -    while ((c = getopt(argc, argv, "dLn:h:p:t?")) != EOF) {
> +    while ((c = getopt(argc, argv, "dDLn:h:p:t?")) != EOF) {
>  	switch (c) {
>  
>  	case 'd':	/* debug flag */
>  	    debug++;
>  	    break;
>  
> +	case 'D':
> +	    run_dirdelegs = 1;
> +	    break;
> +
>  	case 'L':	/* Lease testing */
>  	    run_leases = 1;
>  	    break;
> @@ -1090,13 +1411,29 @@ main(int argc, char *argv[])
>      }
>  
>      filename=argv[optind];
> +
> +    if (run_dirdelegs && create_directory() == FAIL)
> +	    exit(1);
> +
>      if (debug)
>  	fprintf(stderr, "Working on file : %s\n", filename);
> -    if (do_open(O_RDWR) == FAIL)
> +
> +    if (run_dirdelegs) {
> +	openflags = O_RDONLY | O_DIRECTORY;
> +    } else {
> +	openflags = O_RDWR;
> +    }
> +
> +    if (do_open(openflags) == FAIL)
>  	exit(1);
>  
>      if (test_setlease == 1) {
> -	fcntl(f_fd, F_SETLEASE, F_UNLCK);
> +	struct delegation deleg = { .d_type = F_UNLCK };
> +
> +	if (run_dirdelegs)
> +		fcntl(f_fd, F_SETDELEG, &deleg);
> +	else
> +		fcntl(f_fd, F_SETLEASE, F_UNLCK);
>  	saved_errno = errno;
>  	close(f_fd);
>  	exit(saved_errno);
> @@ -1220,7 +1557,7 @@ main(int argc, char *argv[])
>  	SRAND(6789L);
>      }
>  
> -    if (server)
> +    if (server && !run_dirdelegs)
>  	/* only server need do shared file */
>  	initialize(f_fd);
>  
> @@ -1229,7 +1566,9 @@ main(int argc, char *argv[])
>       *
>       * real work is in here ...
>       */
> -    if (run_leases)
> +    if (run_dirdelegs)
> +	fail_count = run(dirdeleg_tests, dirdeleg_descriptions);
> +    else if (run_leases)
>  	fail_count = run(lease_tests, lease_descriptions);
>      else
>  	fail_count = run(lock_tests, lock_descriptions);
> @@ -1304,6 +1643,36 @@ int run(int64_t tests[][6], char *descriptions[])
>  			case CMD_TRUNCATE:
>  			    result = do_truncate(tests[index][OFFSET]);
>  			    break;
> +			case CMD_SETDELEG:
> +			    result = do_deleg(F_SETDELEG, tests[index][ARG], 0);
> +			    break;
> +			case CMD_GETDELEG:
> +			    result = do_deleg(F_GETDELEG, tests[index][ARG], tests[index][ARG]);
> +			    break;
> +			case CMD_CREATE:
> +			    result = do_create(tests[index][ARG]);
> +			    break;
> +			case CMD_UNLINK:
> +			    result = do_unlink(tests[index][ARG]);
> +			    break;
> +			case CMD_RENAME:
> +			    result = do_rename(tests[index][ARG], tests[index][ARG2]);
> +			    break;
> +			case CMD_SYMLINK:
> +			    result = do_symlink(tests[index][ARG], tests[index][ARG2]);
> +			    break;
> +			case CMD_MKNOD:
> +			    result = do_mknod(tests[index][ARG]);
> +			    break;
> +			case CMD_CHMOD:
> +			    result = do_chmod(tests[index][ARG]);
> +			    break;
> +			case CMD_MKDIR:
> +			    result = do_mkdir(tests[index][ARG]);
> +			    break;
> +			case CMD_RMDIR:
> +			    result = do_rmdir(tests[index][ARG]);
> +			    break;
>  		    }
>  		    if( result != tests[index][RESULT]) {
>  			fail_flag++;
> @@ -1418,6 +1787,36 @@ int run(int64_t tests[][6], char *descriptions[])
>  		case CMD_TRUNCATE:
>  		    result = do_truncate(ctl.offset);
>  		    break;
> +		case CMD_SETDELEG:
> +		    result = do_deleg(F_SETDELEG, ctl.offset, 0);
> +		    break;
> +		case CMD_GETDELEG:
> +		    result = do_deleg(F_GETDELEG, ctl.offset, ctl.offset);
> +		    break;
> +		case CMD_CREATE:
> +		    result = do_create(ctl.offset);
> +		    break;
> +		case CMD_UNLINK:
> +		    result = do_unlink(ctl.offset);
> +		    break;
> +		case CMD_RENAME:
> +		    result = do_rename(ctl.offset, ctl.length);
> +		    break;
> +		case CMD_SYMLINK:
> +		    result = do_symlink(ctl.offset, ctl.length);
> +		    break;
> +		case CMD_MKNOD:
> +		    result = do_mknod(ctl.offset);
> +		    break;
> +		case CMD_CHMOD:
> +		    result = do_chmod(ctl.offset);
> +		    break;
> +		case CMD_MKDIR:
> +		    result = do_mkdir(ctl.offset);
> +		    break;
> +		case CMD_RMDIR:
> +		    result = do_rmdir(ctl.offset);
> +		    break;
>  	    }
>  	    if( result != ctl.result ) {
>  		fprintf(stderr,"Failure in %d:%s\n",
> diff --git a/tests/generic/783 b/tests/generic/783
> new file mode 100755
> index 0000000000000000000000000000000000000000..92cd1fe83b6f8d80f7449a91d03a8dfd0238e826
> --- /dev/null
> +++ b/tests/generic/783
> @@ -0,0 +1,19 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2025 Jeff Layton <jlayton@kernel.org>.  All Rights Reserved.
> +#
> +# FS QA Test 783
> +#
> +# Test directory delegation support
> +#
> +. ./common/preamble
> +_begin_fstest auto locks quick
> +
> +. ./common/filter
> +. ./common/locktest
> +
> +_require_test
> +_require_test_fcntl_setdeleg
> +
> +_run_dirdelegtest
> +_exit 0
> diff --git a/tests/generic/783.out b/tests/generic/783.out
> new file mode 100644
> index 0000000000000000000000000000000000000000..4577997e1ff1fa2062e88f88bf94337db37a2e34
> --- /dev/null
> +++ b/tests/generic/783.out
> @@ -0,0 +1,2 @@
> +QA output created by 783
> +success!
> 
> -- 
> 2.52.0
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH fstests v3 3/3] generic: add tests for file delegations
  2025-12-03 15:43 ` [PATCH fstests v3 3/3] generic: add tests for file delegations Jeff Layton
@ 2025-12-05 17:25   ` Zorro Lang
  2025-12-06 18:35     ` Jeff Layton
  0 siblings, 1 reply; 8+ messages in thread
From: Zorro Lang @ 2025-12-05 17:25 UTC (permalink / raw)
  To: Jeff Layton; +Cc: fstests, linux-fsdevel, Christian Brauner

On Wed, Dec 03, 2025 at 10:43:09AM -0500, Jeff Layton wrote:
> 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>
> ---

This version is good to me. But this test fails without the:
https://lore.kernel.org/linux-fsdevel/20251201-dir-deleg-ro-v1-1-2e32cf2df9b7@kernel.org/

So maybe we can mark that:

  _fixed_by_kernel_commit xxxxxxxxxxxx ...

or

  _wants_kernel_commit xxxxxxxxxxxx ...

Anyway, we can add that after the patchset get merged. I'll merge this patchset
at first.

Reviewed-by: Zorro Lang <zlang@redhat.com>

>  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
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH fstests v3 3/3] generic: add tests for file delegations
  2025-12-05 17:25   ` Zorro Lang
@ 2025-12-06 18:35     ` Jeff Layton
  0 siblings, 0 replies; 8+ messages in thread
From: Jeff Layton @ 2025-12-06 18:35 UTC (permalink / raw)
  To: Zorro Lang; +Cc: fstests, linux-fsdevel, Christian Brauner

On Sat, 2025-12-06 at 01:25 +0800, Zorro Lang wrote:
> On Wed, Dec 03, 2025 at 10:43:09AM -0500, Jeff Layton wrote:
> > 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>
> > ---
> 
> This version is good to me. But this test fails without the:
> https://lore.kernel.org/linux-fsdevel/20251201-dir-deleg-ro-v1-1-2e32cf2df9b7@kernel.org/
> 


Thanks. Yes, that bug is unfortunate. I'm hoping Christian will take
that patch in soon so all of the tests will pass.

> So maybe we can mark that:
> 
>   _fixed_by_kernel_commit xxxxxxxxxxxx ...
> 
> or
> 
>   _wants_kernel_commit xxxxxxxxxxxx ...
> 
> Anyway, we can add that after the patchset get merged. I'll merge this patchset
> at first.
> 
> Reviewed-by: Zorro Lang <zlang@redhat.com>

If you like. This functionality is only in v6.19-rc so far, so there is
no released kernel that has this (yet).
-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2025-12-06 18:35 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH fstests v3 3/3] generic: add tests for file delegations Jeff Layton
2025-12-05 17:25   ` Zorro Lang
2025-12-06 18:35     ` Jeff Layton

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).