---- On Sat, 09 Aug 2025 00:39:53 +0400 Aleksa Sarai <cyphar@xxxxxxxxxx> wrote --- > +If > +.I flags > +does not contain > +.BR \%OPEN_TREE_CLONE , > +.BR open_tree () > +returns a file descriptor > +that is exactly equivalent to > +one produced by > +.BR openat (2) This is not true. They differ in handling of automounts. open_tree follows them in final component (by default), and openat - not. See reproducer in the end of this letter. I suggest merely adding this: > that is exactly equivalent to one produced by openat(2) (modulo automounts) -- Askar Safin https://types.pl/@safinaskar // Root in initial user namespace #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sched.h> #include <errno.h> #include <sys/stat.h> #include <sys/mount.h> #include <sys/syscall.h> #include <linux/openat2.h> #define MY_ASSERT(cond) do { \ if (!(cond)) { \ fprintf (stderr, "%s: assertion failed\n", #cond); \ exit (1); \ } \ } while (0) bool tracing_mounted (void) { struct statx tracing; if (statx (AT_FDCWD, "/tmp/debugfs/tracing", AT_NO_AUTOMOUNT, 0, &tracing) != 0) { perror ("statx tracing"); exit (1); } if (!(tracing.stx_attributes_mask & STATX_ATTR_MOUNT_ROOT)) { fprintf (stderr, "???\n"); exit (1); } return tracing.stx_attributes & STATX_ATTR_MOUNT_ROOT; } void mount_debugfs (void) { if (mount (NULL, "/tmp/debugfs", "debugfs", 0, NULL) != 0) { perror ("mount debugfs"); exit (1); } MY_ASSERT (!tracing_mounted ()); } void umount_debugfs (void) { umount ("/tmp/debugfs/tracing"); // Ignore errors if (umount ("/tmp/debugfs") != 0) { perror ("umount debugfs"); exit (1); } } int main (void) { // Init { if (chdir ("/") != 0) { perror ("chdir /"); exit (1); } if (unshare (CLONE_NEWNS) != 0) { perror ("unshare"); exit (1); } if (mount (NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0) { perror ("mount(NULL, /, NULL, MS_REC | MS_PRIVATE, NULL)"); exit (1); } if (mount (NULL, "/tmp", "tmpfs", 0, NULL) != 0) { perror ("mount tmpfs"); exit (1); } } if (mkdir ("/tmp/debugfs", 0777) != 0) { perror ("mkdir(/tmp/debugfs)"); exit (1); } // open(O_PATH) doesn't follow automounts { mount_debugfs (); { int fd = open ("/tmp/debugfs/tracing", O_PATH); MY_ASSERT (fd >= 0); MY_ASSERT (close (fd) == 0); } MY_ASSERT (!tracing_mounted ()); umount_debugfs (); } // open_tree does follow automounts (by default) { mount_debugfs (); { int fd = open_tree (AT_FDCWD, "/tmp/debugfs/tracing", 0); MY_ASSERT (fd >= 0); MY_ASSERT (close (fd) == 0); } MY_ASSERT (tracing_mounted ()); umount_debugfs (); } // open (O_PATH | O_DIRECTORY) { mount_debugfs (); { int fd = open ("/tmp/debugfs/tracing", O_PATH | O_DIRECTORY); MY_ASSERT (fd >= 0); MY_ASSERT (close (fd) == 0); } MY_ASSERT (tracing_mounted ()); umount_debugfs (); } // AT_NO_AUTOMOUNT { mount_debugfs (); { int fd = open_tree (AT_FDCWD, "/tmp/debugfs/tracing", AT_NO_AUTOMOUNT); MY_ASSERT (fd >= 0); MY_ASSERT (close (fd) == 0); } MY_ASSERT (!tracing_mounted ()); umount_debugfs (); } printf ("All tests passed\n"); exit (0); }