[LTP] fs/acl: Add ACL_USER_OBJ permissions test
linuxtestproject.agent@gmail.com
linuxtestproject.agent@gmail.com
Wed Jun 3 16:52:34 CEST 2026
On Wed, 3 Jun 2026 19:31:40 +0530, Sachin Sant wrote:
> fs/acl: Add ACL_USER_OBJ permissions test
> fs/acl: Add ACL mask interaction tests
> fs/acl: Add ACL_OTHER permissions test
> fs/acl: Add default ACL inheritance test
> fs/acl: Add chmod/chown ACL interaction tests
> fs/acl: Add symlink ACL operations test
> fs/acl: Add extended attributes test
> fs/acl: Remove old shell-based ACL test
The overall approach is sound — replacing the old monolithic shell script
with focused C tests is the right direction, and the shared acl_lib.h is
well-structured. However there are a few issues that need addressing before
this is ready.
---
BLOCKING
---
[1] acl_link01.c: test is vacuous and does not verify symlink-following
The test creates TESTFILE with mode 0644, then sets:
ACL_USER_OBJ = rw-
ACL_GROUP_OBJ = r--
ACL_OTHER = r--
via TESTSYMLINK. That ACL is bit-for-bit identical to the minimal ACL
already implied by mode 0644, so acl_set_file() changes nothing. Then
acl_get_file() is called on both TESTFILE and TESTSYMLINK; since both
resolve to the same inode, acl_cmp() is trivially 0 regardless of whether
the symlink was followed during the set call.
The test would pass even if acl_set_file() on a symlink were a no-op.
Fix: set a deliberately distinct ACL through the symlink — one that is
different from the file's initial state — and then verify the target file's
ACL reflects that change. For example:
SAFE_OPEN(TESTFILE, O_CREAT | O_WRONLY, 0600);
acl = acl_init(3);
add_acl_entry(acl, ACL_USER_OBJ, ACL_READ | ACL_WRITE | ACL_EXECUTE);
add_acl_entry(acl, ACL_GROUP_OBJ, ACL_READ | ACL_WRITE);
add_acl_entry(acl, ACL_OTHER, 0);
/* Set the new ACL through the symlink */
acl_set_file(TESTSYMLINK, ACL_TYPE_ACCESS, acl);
/* Now read from the target and from the symlink */
target_acl = acl_get_file(TESTFILE, ACL_TYPE_ACCESS);
symlink_acl = acl_get_file(TESTSYMLINK, ACL_TYPE_ACCESS);
/* Both must match the ACL we set, and the target must differ from 0600 */
With a distinct ACL, a failure to follow the symlink during the set call
would be caught because the target file's mode/ACL would remain at 0600.
---
[2] acl_other01.c: correctness depends on distro-specific useradd behaviour
The test relies on user2_gid != user1_gid so that user2 falls through to
ACL_OTHER rather than matching ACL_GROUP_OBJ (the directory's owning
group is user1_gid). Whether useradd creates a private group per user is
controlled by USERGROUPS_ENAB in /etc/login.defs. On many distributions
this defaults to "yes", but it is not universal. On a system where
USERGROUPS_ENAB=no, both users would share the same primary GID and user2
would match ACL_GROUP_OBJ (masked to --- by ACL_MASK), causing EACCES and
a false test failure.
Fix: explicitly request a user-private group:
tst_cmd((const char *[]){"useradd", "-M", "-U", username, NULL}, ...);
The "-U" flag creates a group with the same name as the user, guaranteeing
a distinct GID even when USERGROUPS_ENAB=no. Check that your target
useradd supports -U (it is part of shadow-utils and available on all
mainstream distros).
The same concern applies to acl_mask01.c which tests ACL_GROUP and
ACL_GROUP_OBJ interactions depending on user/group separation.
---
NON-BLOCKING
---
[3] acl_lib.h: create_user_if_needed() uses TCONF when TBROK is correct
if (tst_cmd((const char *[]){"useradd", "-M", username, NULL},
NULL, NULL, TST_CMD_PASS_RETVAL) != 0)
tst_brk(TCONF, "Failed to create user %s", username);
The needs_cmds field already handles the "useradd not installed" case
with TCONF. Once useradd is present but returns non-zero (e.g. PAM
policy, container user-namespace restrictions, nss failures), the test
cannot be set up — that is TBROK, not TCONF. Using TCONF here silently
skips the test instead of surfacing the setup failure.
tst_brk(TBROK, "Failed to create user %s", username);
---
[4] acl_lib.h: set_acl_file() should treat EOPNOTSUPP as TCONF
static inline void set_acl_file(const char *path, acl_type_t type, acl_t acl)
{
...
if (acl_set_file(path, type, acl) == -1)
tst_brk(TBROK | TERRNO, "acl_set_file(%s) failed", path);
}
If POSIX ACL support is missing at the kernel level (CONFIG_FS_POSIX_ACL
not set) and the filesystem returns EOPNOTSUPP, all tests that go through
set_acl_file() will report TBROK instead of TCONF. acl_link01.c already
handles this correctly inline; the same treatment should be in the shared
helper:
if (acl_set_file(path, type, acl) == -1) {
if (errno == EOPNOTSUPP)
tst_brk(TCONF | TERRNO, "ACL not supported on %s", path);
tst_brk(TBROK | TERRNO, "acl_set_file(%s) failed", path);
}
---
[5] acl_inherit01.c: acl_get_file() result is never inspected
file_acl = acl_get_file(TESTFILE, ACL_TYPE_ACCESS);
if (!file_acl) {
cleanup_testfile();
tst_brk(TBROK | TERRNO, "acl_get_file failed");
}
safe_acl_free(file_acl);
The test states it verifies that "the file inherits the default ACL as
its access ACL" but the retrieved ACL object is freed immediately without
any content check. The mode-bit verification (0444) is sufficient to
confirm the inherited ACL was applied for a minimal ACL, so either:
(a) Remove the acl_get_file() block and add a comment explaining that
for a minimal inherited ACL the mode bits are the canonical check, or
(b) Iterate file_acl and verify that each entry matches the expected
inherited permissions.
As written the block misleads the reader into thinking the ACL content
is being validated when it is not.
---
Sachin Sant <sachinp@linux.ibm.com>
More information about the ltp
mailing list