[LTP] [PATCH v2 5/8] fs/acl: Add chmod/chown ACL interaction tests

Sachin Sant sachinp@linux.ibm.com
Thu Jun 4 08:54:14 CEST 2026


Add acl_file_ops01 test with 2 test cases to validate chmod and
chown interaction with ACL entries:

1. chmod interaction - Verifies that chmod updates ACL_USER_OBJ,
   ACL_GROUP_OBJ, and ACL_OTHER entries to match new permission bits.
   Sets ACL with read-only, then chmod 0777, and confirms all three
   ACL entries now have rwx permissions.

2. chown interaction - Verifies that chown changes file owner and
   group without modifying ACL entry permissions. Sets ACL entries,
   performs chown, and confirms ACL permissions are preserved.

These tests ensure chmod and chown work correctly with ACL-enabled files.

Signed-off-by: Sachin Sant <sachinp@linux.ibm.com>
---
V2 changes:
- Updated copyright header as per LTP format.
- v1 link https://lore.kernel.org/ltp/20260602121958.27494-1-sachinp@linux.ibm.com/T/#t

V1 changes:
- Use HAVE_LIBACL guards in .c code
- Report TCONF when libacl is not available
- rfc link https://lore.kernel.org/ltp/477836fd-80c8-4168-bfe6-00b374bb2534@linux.ibm.com/T/#t

---
 runtest/fs                               |   1 +
 testcases/kernel/fs/acl/.gitignore       |   1 +
 testcases/kernel/fs/acl/acl_file_ops01.c | 302 +++++++++++++++++++++++
 3 files changed, 304 insertions(+)
 create mode 100644 testcases/kernel/fs/acl/acl_file_ops01.c

diff --git a/runtest/fs b/runtest/fs
index fd295edc7..f1eea055b 100644
--- a/runtest/fs
+++ b/runtest/fs
@@ -93,3 +93,4 @@ acl_user_obj01 acl_user_obj01
 acl_mask01 acl_mask01
 acl_other01 acl_other01
 acl_inherit01 acl_inherit01
+acl_file_ops01 acl_file_ops01
diff --git a/testcases/kernel/fs/acl/.gitignore b/testcases/kernel/fs/acl/.gitignore
index bc03ba1fd..eb4b4a227 100644
--- a/testcases/kernel/fs/acl/.gitignore
+++ b/testcases/kernel/fs/acl/.gitignore
@@ -2,3 +2,4 @@
 /acl_mask01
 /acl_other01
 /acl_inherit01
+/acl_file_ops01
diff --git a/testcases/kernel/fs/acl/acl_file_ops01.c b/testcases/kernel/fs/acl/acl_file_ops01.c
new file mode 100644
index 000000000..c7b5d777f
--- /dev/null
+++ b/testcases/kernel/fs/acl/acl_file_ops01.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 IBM
+ *
+ * Original shell test by Kai Zhao (ltcd3@cn.ibm.com)
+ * Converted to C by Sachin Sant <sachinp@linux.ibm.com>
+ */
+
+/*\
+ * Test chmod and chown interaction with ACLs.
+ *
+ * Verify that standard file operations (chmod, chown) interact correctly
+ * with ACLs:
+ * - chmod should update ACL_USER_OBJ, ACL_GROUP_OBJ, and ACL_OTHER entries
+ * - chown should change file owner/group without affecting ACL entries
+ * - ACL permissions should be preserved after ownership changes
+ *
+ * [Algorithm]
+ *
+ * Test 1 - chmod interaction:
+ * 1. Create file with read-only ACL entries
+ * 2. Use chmod to set permissions to 0777
+ * 3. Verify ACL entries are updated to rwx for user, group, and other
+ *
+ * Test 2 - chown interaction:
+ * 1. Create file with specific ACL entries (rw for user, r for group/other)
+ * 2. Use chown to change owner and group
+ * 3. Verify ownership changed correctly
+ * 4. Verify ACL entries preserved their permissions after chown
+ */
+
+#include "acl_lib.h"
+
+uid_t user1_uid, user2_uid, user3_uid;
+gid_t user1_gid, user2_gid, user3_gid;
+int users_created = 0;
+
+#ifdef HAVE_LIBACL
+
+/*
+ * Test chmod interaction with ACLs.
+ * chmod should update ACL_USER_OBJ, ACL_GROUP_OBJ, and ACL_OTHER.
+ */
+static void test_chmod_acl(void)
+{
+	acl_t acl;
+	acl_entry_t entry;
+	acl_permset_t permset;
+	acl_tag_t tag;
+	int fd = -1;
+	int i;
+	int user_ok = 0, group_ok = 0, other_ok = 0;
+
+	tst_res(TINFO, "Testing chmod interaction with ACLs");
+	reset_test_path();
+
+	fd = SAFE_OPEN(TESTFILE, O_CREAT | O_WRONLY, 0644);
+	SAFE_CLOSE(fd);
+
+	acl = acl_init(3);
+	if (!acl) {
+		cleanup_testfile();
+		tst_brk(TBROK | TERRNO, "acl_init failed");
+	}
+
+	add_acl_entry(acl, ACL_USER_OBJ, ACL_READ);
+	add_acl_entry(acl, ACL_GROUP_OBJ, ACL_READ);
+	add_acl_entry(acl, ACL_OTHER, ACL_READ);
+	set_acl_file(TESTFILE, ACL_TYPE_ACCESS, acl);
+	safe_acl_free(acl);
+
+	SAFE_CHMOD(TESTFILE, 0777);
+
+	acl = acl_get_file(TESTFILE, ACL_TYPE_ACCESS);
+	if (!acl) {
+		cleanup_testfile();
+		tst_brk(TBROK | TERRNO, "acl_get_file failed");
+	}
+
+	for (i = ACL_FIRST_ENTRY; ; i = ACL_NEXT_ENTRY) {
+		int ret;
+
+		ret = acl_get_entry(acl, i, &entry);
+		if (ret == -1) {
+			safe_acl_free(acl);
+			cleanup_testfile();
+			tst_brk(TBROK | TERRNO, "acl_get_entry failed");
+		}
+
+		if (ret != 1)
+			break;
+
+		if (acl_get_tag_type(entry, &tag) == -1) {
+			safe_acl_free(acl);
+			cleanup_testfile();
+			tst_brk(TBROK | TERRNO,
+				"acl_get_tag_type failed");
+		}
+
+		if (acl_get_permset(entry, &permset) == -1) {
+			safe_acl_free(acl);
+			cleanup_testfile();
+			tst_brk(TBROK | TERRNO,
+				"acl_get_permset failed");
+		}
+
+		if (tag == ACL_USER_OBJ) {
+			if (acl_get_perm(permset, ACL_READ) == 1 &&
+			    acl_get_perm(permset, ACL_WRITE) == 1 &&
+			    acl_get_perm(permset, ACL_EXECUTE) == 1)
+				user_ok = 1;
+		} else if (tag == ACL_GROUP_OBJ) {
+			if (acl_get_perm(permset, ACL_READ) == 1 &&
+			    acl_get_perm(permset, ACL_WRITE) == 1 &&
+			    acl_get_perm(permset, ACL_EXECUTE) == 1)
+				group_ok = 1;
+		} else if (tag == ACL_OTHER) {
+			if (acl_get_perm(permset, ACL_READ) == 1 &&
+			    acl_get_perm(permset, ACL_WRITE) == 1 &&
+			    acl_get_perm(permset, ACL_EXECUTE) == 1)
+				other_ok = 1;
+		}
+	}
+
+	safe_acl_free(acl);
+	cleanup_testfile();
+
+	if (user_ok && group_ok && other_ok)
+		tst_res(TPASS, "chmod correctly updated ACL entries");
+	else
+		tst_res(TFAIL, "chmod did not update ACL entries correctly");
+}
+
+/*
+ * Test chown interaction with ACLs.
+ * chown should change file owner and group without affecting ACL entries.
+ */
+static void test_chown_acl(void)
+{
+	acl_t acl;
+	acl_entry_t entry;
+	acl_tag_t tag;
+	acl_permset_t permset;
+	struct stat st;
+	int fd = -1;
+	int i, found_user_obj = 0, found_group_obj = 0, found_other = 0;
+
+	tst_res(TINFO, "Testing chown interaction with ACLs");
+	reset_test_path();
+
+	fd = SAFE_OPEN(TESTFILE, O_CREAT | O_WRONLY, 0644);
+	SAFE_CLOSE(fd);
+
+	acl = acl_init(3);
+	if (!acl) {
+		cleanup_testfile();
+		tst_brk(TBROK | TERRNO, "acl_init failed");
+	}
+
+	add_acl_entry(acl, ACL_USER_OBJ, ACL_READ | ACL_WRITE);
+	add_acl_entry(acl, ACL_GROUP_OBJ, ACL_READ);
+	add_acl_entry(acl, ACL_OTHER, ACL_READ);
+	set_acl_file(TESTFILE, ACL_TYPE_ACCESS, acl);
+	safe_acl_free(acl);
+
+	SAFE_CHOWN(TESTFILE, user2_uid, user2_gid);
+
+	SAFE_STAT(TESTFILE, &st);
+
+	if (st.st_uid != user2_uid || st.st_gid != user2_gid) {
+		cleanup_testfile();
+		tst_res(TFAIL, "chown did not change owner/group correctly");
+		return;
+	}
+
+	/* Verify ACL entries are preserved after chown */
+	acl = acl_get_file(TESTFILE, ACL_TYPE_ACCESS);
+	if (!acl) {
+		cleanup_testfile();
+		tst_brk(TBROK | TERRNO, "acl_get_file failed");
+	}
+
+	for (i = ACL_FIRST_ENTRY; ; i = ACL_NEXT_ENTRY) {
+		int ret = acl_get_entry(acl, i, &entry);
+
+		if (ret == -1) {
+			safe_acl_free(acl);
+			cleanup_testfile();
+			tst_brk(TBROK | TERRNO, "acl_get_entry failed");
+		}
+
+		if (ret != 1)
+			break;
+
+		if (acl_get_tag_type(entry, &tag) == -1) {
+			safe_acl_free(acl);
+			cleanup_testfile();
+			tst_brk(TBROK | TERRNO, "acl_get_tag_type failed");
+		}
+
+		if (acl_get_permset(entry, &permset) == -1) {
+			safe_acl_free(acl);
+			cleanup_testfile();
+			tst_brk(TBROK | TERRNO, "acl_get_permset failed");
+		}
+
+		if (tag == ACL_USER_OBJ) {
+			found_user_obj = 1;
+			if (!acl_get_perm(permset, ACL_READ) ||
+			    !acl_get_perm(permset, ACL_WRITE)) {
+				safe_acl_free(acl);
+				cleanup_testfile();
+				tst_res(TFAIL,
+					"ACL_USER_OBJ perms changed after chown");
+				return;
+			}
+		} else if (tag == ACL_GROUP_OBJ) {
+			found_group_obj = 1;
+			if (!acl_get_perm(permset, ACL_READ) ||
+			    acl_get_perm(permset, ACL_WRITE)) {
+				safe_acl_free(acl);
+				cleanup_testfile();
+				tst_res(TFAIL,
+					"ACL_GROUP_OBJ perms changed after chown");
+				return;
+			}
+		} else if (tag == ACL_OTHER) {
+			found_other = 1;
+			if (!acl_get_perm(permset, ACL_READ) ||
+			    acl_get_perm(permset, ACL_WRITE)) {
+				safe_acl_free(acl);
+				cleanup_testfile();
+				tst_res(TFAIL,
+					"ACL_OTHER perms changed after chown");
+				return;
+			}
+		}
+	}
+
+	safe_acl_free(acl);
+
+	if (!found_user_obj || !found_group_obj || !found_other) {
+		cleanup_testfile();
+		tst_res(TFAIL, "ACL entries missing after chown");
+		return;
+	}
+
+	cleanup_testfile();
+	tst_res(TPASS, "chown preserved ACL entries correctly");
+}
+
+static void setup(void)
+{
+	init_test_users();
+	reset_test_path();
+}
+
+static void cleanup(void)
+{
+	cleanup_test_paths();
+	cleanup_test_users();
+}
+
+static void run(unsigned int n)
+{
+	switch (n) {
+	case 0:
+		test_chmod_acl();
+		break;
+	case 1:
+		test_chown_acl();
+		break;
+	}
+}
+
+static struct tst_test test = {
+	.test = run,
+	.tcnt = 2,
+	.setup = setup,
+	.cleanup = cleanup,
+	.needs_root = 1,
+	.mount_device = 1,
+	.mntpoint = MNTPOINT,
+	.forks_child = 1,
+	.filesystems = (struct tst_fs[]) {
+		{.type = "ext2", .mnt_data = "acl"},
+		{.type = "ext3", .mnt_data = "acl"},
+		{.type = "ext4", .mnt_data = "acl"},
+		{.type = "xfs"},
+		{.type = "btrfs"},
+		{}
+	},
+	.needs_cmds = (struct tst_cmd[]) {
+		{.cmd = "useradd"},
+		{.cmd = "userdel"},
+		{}
+	}
+};
+
+#else
+TST_TEST_TCONF("libacl or ACL headers are not available");
+#endif
-- 
2.39.1



More information about the ltp mailing list