From: Samuel Cabrero Date: Wed, 8 Oct 2025 15:09:22 +0000 (+0200) Subject: smbd: Fix crossing direct automounter mount points X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=82cb952f45215b053ba7e80bed008890968a1c76;p=thirdparty%2Fsamba.git smbd: Fix crossing direct automounter mount points The workaround implemented in commit ac7a16f9cc4bd97ef546d1b7b02605991000d0f9 to trigger automounts does not work for direct automounts (either with systemd-automount or autofs daemon). In direct automounts the mount point is a real directory instead of a "ghost" directory so when turning the O_PATH handle into a real one through /proc/self/fd/ openat() does not return ENOENT, it returs a fd referring to the mount point without triggering the mount. To trigger the mount first we have to know when we are crossing mount points by using the RESOLVE_NO_XDEV flag in open_how.resolve, then we can check with fstatfs() the .f_type and fallback to a path-based open for automounts or retry without RESOLVE_NO_XDEV otherwise. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805 Signed-off-by: Samuel Cabrero Reviewed-by: Ralph Boehme --- diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 4212f9ad32d..02f3c53598f 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -911,6 +911,7 @@ static NTSTATUS reopen_from_fsp_pathref_based( }; mode_t mode = fsp->fsp_name->st.st_ex_mode; int new_fd; + struct vfs_open_how pathref_how = *how; if (S_ISLNK(mode)) { return NT_STATUS_STOPPED_ON_SYMLINK; @@ -921,26 +922,88 @@ static NTSTATUS reopen_from_fsp_pathref_based( fsp->fsp_flags.is_pathref = false; +#if defined(HAVE_FSTATFS) && defined(HAVE_LINUX_MAGIC_H) + /* + * There is no point in setting RESOLVE_NO_XDEV if we can't + * check with fstatfs later in fsp_is_automount_mountpoint + */ + if (S_ISDIR(fsp->fsp_name->st.st_ex_mode) && + fsp->conn->open_how_resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV) { + /* + * If the *at cwd_fsp is a pathref (opened with O_PATH) + * and old_fd refers to an automounter mount point not + * yet mounted, we will get a fd referring to the + * mount point without actually triggering the mount + * (man 2 openat). To detect this situation set the + * RESOLVE_NO_XDEV flag so openat2 will return an + * error when crossing mount points. Then check + * with fstatfs if it is an autofs mount point or not, + * falling back to name-based openat or retry without + * RESOLVE_NO_XDEV otherwise (could be a bind mount, + * other type of mount of an automounter mount point + * already mounted). + */ + pathref_how.resolve |= VFS_OPEN_HOW_RESOLVE_NO_XDEV; + } +#endif + +retry: new_fd = SMB_VFS_OPENAT(fsp->conn, fsp->conn->cwd_fsp, &proc_fname, fsp, - how); + &pathref_how); if (new_fd == -1) { int saved_errno = errno; if (saved_errno == ENOENT && fsp_is_automount_mountpoint(fsp, pathref_fd)) { /* - * When reopening an as-yet unmounted autofs - * mount point we get ENOENT. We have to retry - * pathbased. + * This is a not yet triggered indirect automount + * detected by openat(pathref_fd). Retry name-based. */ return reopen_from_fsp_namebased(dirfsp, smb_fname, fsp, how, p_file_created); + } else if (saved_errno == EXDEV && + pathref_how.resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV && + fsp_is_automount_mountpoint(fsp, pathref_fd)) + { + /* + * This is a not yet triggered direct or indirect + * automount, detected by + * openat2(pathref_fd, .., RESOLVE_NO_XDEV). + * Retry name-based. + */ + return reopen_from_fsp_namebased(dirfsp, + smb_fname, + fsp, + how, + p_file_created); + } else if (saved_errno == ENOSYS && + pathref_how.resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV) + { + /* + * The kernel doesn't support openat2() yet, or any + * VFS module rejected the flag. Notify to the user + * and retry without RESOLVE_NO_XDEV. + */ + DBG_WARNING("Failed to open directory disallowing the " + "traversal of mount points during path " + "resolution. Retrying allowing traversal, " + "but automounts won't be triggered.\n"); + pathref_how.resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_XDEV; + goto retry; + } else if (saved_errno == EXDEV && + pathref_how.resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV) + { + /* + * Just crossing a mount. Retry allowing traversals. + */ + pathref_how.resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_XDEV; + goto retry; } status = map_nt_error_from_unix(saved_errno);