[LTP] [PATCH] syscalls/fanotify26: Add directory cases for the nested event
Amir Goldstein
amir73il@gmail.com
Sun Nov 1 17:53:10 CET 2026
On Sat, Oct 31, 2026 at 10:13 PM Edvard Conrad <edvard@tuta.io> wrote:
>
> A directory on overlayfs reaches its real directory through
> ovl_dir_open_realfile() rather than backing_file_open(), so the fix
> for regular files, commit c3d355f427ce ("fs: let backing files
> inherit FMODE_NONOTIFY from the user file"), does not cover it: a
> listener that marks the backing filesystem with FAN_OPEN_PERM |
> FAN_ONDIR is still sent a nested permission event from its own
> read(2) when the reported path is an overlay directory.
>
> Add the directory counterparts of the two existing cases. The
> listener's expected event mask stays FAN_OPEN_PERM: FAN_ONDIR is not
> reported to a group without FAN_REPORT_FID.
>
> The fourth case fails on a kernel without the pending fix "ovl:
> inherit FMODE_NONOTIFY when opening directory realfiles".
>
> Signed-off-by: Edvard Conrad <edvard@tuta.io>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
> ---
> Depends on the pending overlayfs fix, posted by Amir on the kernel
> thread:
> https://lore.kernel.org/all/CAOQ4uxjm=cGPnug18VuhKqiMvkWwLPNLO_TE3KxO8iLZG+veYQ@mail.gmail.com/
> Without it the fourth case fails on current mainline, so this should
> wait in the queue until that fix is in Linus' tree; a linux-git tag for
> it can then be added. The first three cases pass on v7.4-rc1 material
> today.
>
> Tested in a VM, each kernel run once and with -i 2:
>
> kernel file+backing dir+backing
> v6.13 PASS FAIL
> 6b4b543fa18 (no fix) FAIL FAIL
> + c3d355f427ce (files, in Linus') PASS FAIL
> + the pending ovl fix PASS PASS
>
> The "overlay mark only" cases pass everywhere. The directory case
> fails on v6.13 too: overlayfs never passed the nonotify state on to
> the real directory, so it predates the 6.14 change that broke files.
As far as I can tell its a regression from
130fdbc3d1f99 ("ovl: pass correct flags for opening real directory")
Thanks for adding this test.
I added your Tested-by to the fix patch.
Thanks,
Amir.
>
> .../kernel/syscalls/fanotify/fanotify26.c | 43 +++++++++++++------
> 1 file changed, 30 insertions(+), 13 deletions(-)
>
> diff --git a/testcases/kernel/syscalls/fanotify/fanotify26.c b/testcases/kernel/syscalls/fanotify/fanotify26.c
> index 63d7eea0f..3f6b525ef 100644
> --- a/testcases/kernel/syscalls/fanotify/fanotify26.c
> +++ b/testcases/kernel/syscalls/fanotify/fanotify26.c
> @@ -17,9 +17,11 @@
> * listener that reads with a single thread never returns, and every task that
> * touches a watched filesystem queues up behind it.
> *
> - * The first test case marks the overlay only and must pass on any kernel. The
> - * second one marks the backing filesystem as well, which is the shape an
> - * anti-malware scanner uses on an overlayfs root.
> + * Regular files and directories reach their real file through different
> + * paths in overlayfs, so both are tested. For each, the first test case marks
> + * the overlay only and must pass on any kernel, the second one marks the
> + * backing filesystem as well, which is the shape an anti-malware scanner uses
> + * on an overlayfs root.
> *
> * The test needs root to create permission events and to mount overlayfs.
> */
> @@ -39,8 +41,11 @@
>
> #define BACKING_MNT OVL_BASE_MNTPOINT
> #define FNAME "test_file"
> +#define DNAME "test_dir"
> #define OVL_FILE OVL_MNT "/" FNAME
> +#define OVL_DIR OVL_MNT "/" DNAME
> #define LOWER_FILE OVL_LOWER "/" FNAME
> +#define LOWER_DIR OVL_LOWER "/" DNAME
>
> /*
> * The listener either answers the event at once or is blocked for good, so
> @@ -55,9 +60,12 @@ static int fd_notify = -1;
> static struct tcase {
> const char *tname;
> int mark_backing;
> + int ondir;
> } tcases[] = {
> - {"Mark on the overlay", 0},
> - {"Mark on the overlay and on the backing filesystem", 1},
> + {"Overlay mark, file", 0, 0},
> + {"Overlay and backing mark, file", 1, 0},
> + {"Overlay mark, directory", 0, 1},
> + {"Overlay and backing mark, directory", 1, 1},
> };
>
> static void answer_one_event(pid_t opener)
> @@ -71,7 +79,9 @@ static void answer_one_event(pid_t opener)
> /*
> * A wrong mask or a stranger's event is a result, not harness
> * breakage, so report and still answer: the opener stays parked in
> - * the kernel until something does.
> + * the kernel until something does. FAN_ONDIR is not reported to a
> + * group without FAN_REPORT_FID, so a directory open arrives as a
> + * plain FAN_OPEN_PERM as well.
> */
> if (event.mask != FAN_OPEN_PERM) {
> tst_res(TFAIL, "Got event mask %llx, expected %llx",
> @@ -95,6 +105,7 @@ static void answer_one_event(pid_t opener)
> static void run(unsigned int n)
> {
> struct tcase *tc = &tcases[n];
> + uint64_t mask = FAN_OPEN_PERM | (tc->ondir ? FAN_ONDIR : 0);
> pid_t opener, reader;
>
> tst_res(TINFO, "Test #%d: %s", n, tc->tname);
> @@ -102,12 +113,12 @@ static void run(unsigned int n)
> fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY);
>
> SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
> - FAN_OPEN_PERM, AT_FDCWD, OVL_MNT);
> + mask, AT_FDCWD, OVL_MNT);
>
> if (tc->mark_backing) {
> SAFE_FANOTIFY_MARK(fd_notify,
> FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
> - FAN_OPEN_PERM, AT_FDCWD, BACKING_MNT);
> + mask, AT_FDCWD, BACKING_MNT);
> }
>
> opener = SAFE_FORK();
> @@ -121,7 +132,10 @@ static void run(unsigned int n)
> * answered it goes away, so a failure here is the fallout of
> * the test tearing a blocked listener down, not a result.
> */
> - fd = open(OVL_FILE, O_RDONLY);
> + if (tc->ondir)
> + fd = open(OVL_DIR, O_RDONLY | O_DIRECTORY);
> + else
> + fd = open(OVL_FILE, O_RDONLY);
> if (fd != -1)
> SAFE_CLOSE(fd);
> exit(0);
> @@ -161,18 +175,21 @@ static void run(unsigned int n)
>
> static void setup(void)
> {
> - /* The file has to exist in the lower layer to be opened through it. */
> + /* Both have to exist in the lower layer to be opened through it. */
> SAFE_UMOUNT(OVL_MNT);
> SAFE_TOUCH(LOWER_FILE, 0644, NULL);
> + SAFE_MKDIR(LOWER_DIR, 0755);
> SAFE_MOUNT_OVERLAY();
>
> /*
> - * Probe the combination run() uses, not a weaker proxy for it. Every
> - * case marks the overlay; the second also marks the base.
> + * Probe the widest combination run() uses, not a weaker proxy for
> + * it. Every case marks the overlay; the second of each pair also
> + * marks the base.
> */
> REQUIRE_FANOTIFY_EVENTS_SUPPORTED_ON_FS(FAN_CLASS_CONTENT,
> FAN_MARK_FILESYSTEM,
> - FAN_OPEN_PERM, OVL_MNT);
> + FAN_OPEN_PERM | FAN_ONDIR,
> + OVL_MNT);
> REQUIRE_MARK_TYPE_SUPPORTED_ON_FS(FAN_MARK_FILESYSTEM, BACKING_MNT);
> }
>
> --
> 2.56.0
>
More information about the ltp
mailing list