containers / containers/fuse-overlayfs
open(O_NOATIME) of uidmapped files can fail with EPERM
- Dominant language
- Rust
- Stars
- 691
- Forks
- 109
- Avg merge
- 5d 7h
- Merged PRs (30d)
- 2
Description
When fuse-overlayfs receives open("/mountdir/file", O_NOATIME), it calls e.g. open("/lowerdir/file", O_NOATIME). If the underlying open() fails with EPERM, fuse-overlayfs also returns EPERM back to the caller. That might sound reasonable, but I'd argue it doesn't comply with open() docs, and it can break valid client programs.
Per https://man7.org/linux/man-pages/man2/open.2.html:
> **O_NOATIME** (since Linux 2.6.8)
> Do not update the file last access time (st_atime in the inode) when the file is read(2).
>
> This flag can be employed only if one of the following conditions is true: • The effective UID of the process matches the owner UID of the file. • The calling process has the CAP_FOWNER capability in its user namespace and the owner UID of the file has a mapping in the namespace.
>
> This flag is intended for use by indexing or backup programs, where its use can significantly reduce the amount of disk activity. This flag may not be effective on all filesystems. One example is NFS, where the server maintains the access time.
>
> **EPERM** The O_NOATIME flag was specified, but the effective user ID of the caller did not match the owner of the file and the caller was not privileged.
There are broadly two kinds of client programs that use O_NOATIME: 1. Those that try with open(...|O_NOATIME), and if it fails, try again open(...) without the flag. 2. Those that first check the file's owner, and only if it matches my UID, add the O_NOATIME flag -- perfectly reasonably assuming that this will prevent EPERM, and they don't need to handle it specially.
Both strategies comply with the open() manpage as written. But the second kind can break with fuse-overlayfs. When it runs as an unprivileged user with the uidmapping option, stat("/mountdir/file") will say that I own the file, but open("/mountdir/file", O_NOATIME) will still fail.
An example of the first kind is [systemd-tmpfiles](https://sources.debian.org/src/systemd/261.2-1/src/tmpfiles/tmpfiles.c#L555) or [bup](https://sources.debian.org/src/bup/0.33.10-1/lib/bup/_helpers.c?hl=1125#L1125). An example of the second kind is [the real overlayfs](https://elixir.bootlin.com/linux/v7.2/source/fs/overlayfs/file.c#L35-L49) when its lowerdir points to a fuse-overlayfs mount. It adds O_NOATIME (OVL_OPEN_FLAGS) if-and-only-if we're inode_owner_or_capable(). Sorry, I know this example is somewhat artificial, but you can imagine a normal userspace program doing the same thing.
### Steps to reproduce
```console
$ fuse-overlayfs --version
fuse-overlayfs: version 1.17
FUSE library version 3.18.2
using FUSE kernel interface version 7.45
fusermount3 version: 3.18.2
$ mkdir /tmp/mp
$ fuse-overlayfs -o lowerdir=/ -o uidmapping=0:$(id -u):1 -o gidmapping=0:$(id -g):1 /tmp/mp
$ python3 -c 'import os; print(os.stat("/tmp/mp/usr/bin/test")); os.open("/tmp/mp/usr/bin/test", os.O_NOATIME)'
os.stat_result(st_mode=33261, st_ino=130686, st_dev=68, st_nlink=1, st_uid=1000, st_gid=1000, st_size=55576, st_atime=1784837891, st_mtime=1784093337, st_ctime=1784837891)
Traceback (most recent call last):
File "", line 1, in
import os; print(os.stat("/tmp/mp/usr/bin/test")); os.open("/tmp/mp/usr/bin/test", os.O_NOATIME)
~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PermissionError: [Errno 1] Operation not permitted: '/tmp/mp/usr/bin/test'
```
### Suggested fix
I see several options, depending on your preferred balance of simplicity/effort and semantic precision. My recommendation is the middle one.
- Always ignore O_NOATIME - e.g. perhaps by removing it from VALID_OPEN_FLAGS.
- When O_NOATIME is given, try it, but if underlying openat/openat2 returns EPERM, retry without it (regardless of st_uid).
- When O_NOATIME is given, try it, but if underlying openat/openat2 returns EPERM, *and* the file's mapped st_uid matches fuse_context->uid, retry without it. Otherwise just return the EPERM.
See also: [Is O_NOATIME safe to ignore?](https://askubuntu.com/questions/609430/is-o-noatime-safe-to-ignore) tl;dr: Yes. Per open(2), "This flag may not be effective on all filesystems." Updating the atime when you shouldn't is much better than returning EPERM when you shouldn't.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by locating VALID_OPEN_FLAGS and the openat/openat2 handling in fuse-overlayfs, then reproduce the failure with the provided uidmapping mount and Python command. Compare the suggested O_NOATIME retry behaviors, choose the intended semantics for mapped ownership, and verify that valid opens no longer return an erroneous EPERM while genuine permission failures remain unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, linux
- Domain
- operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100