Your expectation is correct: the permissions should allow rocky to write to afile, since rocky has the write permission on the file and execute permission on all the containing directories. However, you've run into an additional security hardening mechanism on Linux. When this mechanism is active, certain writes to files in directories with the sticky bit are prevented.
The sticky bit on a directory is indicated by t as the last of the 10 characters of the file mode/permissions in the ls -l listing, and can be set with chmod +t. Normally, the effect of the sticky bit on a directory is that only the owner of a file can remove or rename it. It does not affect writing to an existing file.
However, when the fs.protected_regular sysctl is enabled, the sticky bit has an additional consequence: users cannot open a file for writing in a sticky directory unless they own the file, if they open the file in a way that would create it if the file did not exist, which the program indicates by passing the O_CREAT file to the open system call. Note the subtlety here: the normal behavior is solely based on whether the file exists, whereas the hardened behavior is based on whether the file would be created if it didn't exist. This only affects opening the file in a way that might create it: other ways to write to the file are still permitted.
Note that this does not affect files in a directory that doesn't have the sticky bit. In particular, if you create a directory /tmp/foo and /tmp/foo does not have the sticky bit, then permissions of files in /tmp/foo behave normally.
The following table summarizes the behavior of the open system call when opening the file for writing in the relevant scenarios (where the directory exists and the program can access files in it, and if the file exists then the program has write permission on it):
| directory |
file |
with O_CREAT |
without O_CREAT |
rwxr-xr-x (755) |
exists, writable (666) |
ok |
ok |
rwxr-xr-x (755) |
does not exist |
ok |
ENOENT |
rwxrwxrwx (777) |
exists, writable (666) |
ok |
ok |
rwxrwxrwx (777) |
does not exist |
ok |
ENOENT |
rwxr-xr-t (1755) |
exists, writable (666) |
ok |
ok |
rwxr-xr-t (1755) |
does not exist |
ok |
ENOENT |
rwxrwxrwt (1777) normally |
exists, writable (666) |
ok |
ok |
rwxrwxrwt (1777) normally |
does not exist |
ok |
ENOENT |
rwxrwxrwt (1777) on hardened Linux |
exists, writable (666) |
EACCES |
ok |
rwxrwxrwt (1777) on hardened Linux |
does not exist |
ok |
ENOENT |
(ENOENT is the error “No such file or directory”. EACCES is the error “Permission denied”.)
You can control whether this hardening is enabled with sysctl fs.protected_regular=0 (disable), sysctl fs.protected_regular=1 (enable for world-writable directories) or sysctl fs.protected_regular=2 (enable for world-writable or group-writable directories). At boot time, this may be set from /etc/sysctl.conf or /etc/sysctl.d/*. Some distributions enable it by default, others don't. The protected_regular setting applies to regular files; there is a similar setting for named pipes.
To see this for yourself (if you're running on Linux and have root access), run the following commands as root:
mkdir /tmp/experiment
mkdir -m 1777 /tmp/experiment/sticky-world
mkdir -m 777 /tmp/experiment/ordinary-world
mkdir -m 1755 /tmp/experiment/sticky-user
mkdir -m 755 /tmp/experiment/ordinary-user
for d in /tmp/experiment/*; do touch $d/file; chown nobody:nogroup $d/file; chmod 666 $d/file; done
Checking the permissions:
$ ls -l /tmp/experiment
total 16
drwxr-xr-x 2 root root 4096 Jul 3 21:10 ordinary-user
drwxrwxrwx 2 root root 4096 Jul 3 21:10 ordinary-world
drwxr-xr-t 2 root root 4096 Jul 3 21:10 sticky-user
drwxrwxrwt 2 root root 4096 Jul 3 21:10 sticky-world
$ ls -l /tmp/experiment/*/file
-rw-rw-rw- 1 nobody nogroup 0 Jul 3 21:10 /tmp/experiment/ordinary-user/file
-rw-rw-rw- 1 nobody nogroup 0 Jul 3 21:10 /tmp/experiment/ordinary-world/file
-rw-rw-rw- 1 nobody nogroup 0 Jul 3 21:10 /tmp/experiment/sticky-user/file
-rw-rw-rw- 1 nobody nogroup 0 Jul 3 21:10 /tmp/experiment/sticky-world/file
Now, as a non-root user who isn't nobody, let's run some code that tries opening the files for writing, once with the O_CREAT flag and once without. I use Perl because the shell doesn't give direct access to O_CREAT (shell write redirections always have O_CREAT enabled except for appending in some shells when noclobber is enabled). On a hardened Linux:
$ perl -w -MFcntl -e 'foreach (@ARGV) { sysopen(F, $_, O_WRONLY | O_CREAT) or warn "$_: $!\n"; close F; }' /tmp/experiment/*/file
/tmp/experiment/sticky-world/file: Permission denied
$ perl -w -MFcntl -e 'foreach (@ARGV) { sysopen(F, $_, O_WRONLY) or warn "$_: $!\n"; close F; }' /tmp/experiment/*/file
Without the Linux hardening, all the file openings would succeed since the program has permission to write to the file.
If you have audit enabled, the attempt to write to /tmp/experiment/sticky-world/file triggers an ANOM_CREAT audit event, which is logged in /var/log/audit/audit.log (or wherever those logs go on your distribution.) (This is how I discovered this Linux mechanism, which didn't know about before.)
ls -ld /tmp/afileplease. (Added to your question.). Andidrun as bothsebandrockyusersgetfacl -n /tmp/afile. And I assume that you have no issues writing to the file as userseb.df -T /tmp. From the output ofdftake the filesystem (/dev/...) and show it's mount options by runningmount | grep /dev/....