17aa40
From 397aaad6da5c4bfb160adca7a68f865086f2ed0a Mon Sep 17 00:00:00 2001
17aa40
From: Franck Bui <fbui@suse.com>
17aa40
Date: Thu, 30 Sep 2021 14:05:36 +0200
17aa40
Subject: [PATCH] mount-util: fix fd_is_mount_point() when both the parent and
17aa40
 directory are network fs
17aa40
17aa40
The second call to name_to_handle_at_loop() didn't check for the specific
17aa40
errors that can happen when the parent dir is mounted by nfs and instead of
17aa40
falling back like it's done for the child dir, fd_is_mount_point() failed in
17aa40
this case.
17aa40
17aa40
(cherry picked from commit 964ccab8286a7e75d7e9107f574f5cb23752bd5d)
17aa40
17aa40
Resolves: #2015057
17aa40
---
17aa40
 src/basic/mount-util.c | 71 ++++++++++++++++++++++++------------------
17aa40
 1 file changed, 41 insertions(+), 30 deletions(-)
17aa40
17aa40
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
17aa40
index 45348bf878..0c709001be 100644
17aa40
--- a/src/basic/mount-util.c
17aa40
+++ b/src/basic/mount-util.c
17aa40
@@ -139,6 +139,19 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id
17aa40
         return safe_atoi(p, mnt_id);
17aa40
 }
17aa40
 
17aa40
+static bool is_name_to_handle_at_fatal_error(int err) {
17aa40
+        /* name_to_handle_at() can return "acceptable" errors that are due to the context. For
17aa40
+         * example the kernel does not support name_to_handle_at() at all (ENOSYS), or the syscall
17aa40
+         * was blocked (EACCES/EPERM; maybe through seccomp, because we are running inside of a
17aa40
+         * container), or the mount point is not triggered yet (EOVERFLOW, think nfs4), or some
17aa40
+         * general name_to_handle_at() flakiness (EINVAL). However other errors are not supposed to
17aa40
+         * happen and therefore are considered fatal ones. */
17aa40
+
17aa40
+        assert(err < 0);
17aa40
+
17aa40
+        return !IN_SET(err, -EOPNOTSUPP, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL);
17aa40
+}
17aa40
+
17aa40
 int fd_is_mount_point(int fd, const char *filename, int flags) {
17aa40
         _cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
17aa40
         int mount_id = -1, mount_id_parent = -1;
17aa40
@@ -173,42 +186,40 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
17aa40
          * real mounts of their own. */
17aa40
 
17aa40
         r = name_to_handle_at_loop(fd, filename, &h, &mount_id, flags);
17aa40
-        if (IN_SET(r, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL))
17aa40
-                /* This kernel does not support name_to_handle_at() at all (ENOSYS), or the syscall was blocked
17aa40
-                 * (EACCES/EPERM; maybe through seccomp, because we are running inside of a container?), or the mount
17aa40
-                 * point is not triggered yet (EOVERFLOW, think nfs4), or some general name_to_handle_at() flakiness
17aa40
-                 * (EINVAL): fall back to simpler logic. */
17aa40
-                goto fallback_fdinfo;
17aa40
-        else if (r == -EOPNOTSUPP)
17aa40
-                /* This kernel or file system does not support name_to_handle_at(), hence let's see if the upper fs
17aa40
-                 * supports it (in which case it is a mount point), otherwise fallback to the traditional stat()
17aa40
-                 * logic */
17aa40
+        if (r < 0) {
17aa40
+                if (is_name_to_handle_at_fatal_error(r))
17aa40
+                        return r;
17aa40
+                if (r != -EOPNOTSUPP)
17aa40
+                        goto fallback_fdinfo;
17aa40
+
17aa40
+                /* This kernel or file system does not support name_to_handle_at(), hence let's see
17aa40
+                 * if the upper fs supports it (in which case it is a mount point), otherwise fall
17aa40
+                 * back to the traditional stat() logic */
17aa40
                 nosupp = true;
17aa40
-        else if (r < 0)
17aa40
-                return r;
17aa40
+        }
17aa40
 
17aa40
         r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
17aa40
-        if (r == -EOPNOTSUPP) {
17aa40
+        if (r < 0) {
17aa40
+                if (is_name_to_handle_at_fatal_error(r))
17aa40
+                        return r;
17aa40
+                if (r != -EOPNOTSUPP)
17aa40
+                        goto fallback_fdinfo;
17aa40
                 if (nosupp)
17aa40
-                        /* Neither parent nor child do name_to_handle_at()?  We have no choice but to fall back. */
17aa40
+                        /* Both the parent and the directory can't do name_to_handle_at() */
17aa40
                         goto fallback_fdinfo;
17aa40
-                else
17aa40
-                        /* The parent can't do name_to_handle_at() but the directory we are interested in can?  If so,
17aa40
-                         * it must be a mount point. */
17aa40
-                        return 1;
17aa40
-        } else if (r < 0)
17aa40
-                return r;
17aa40
 
17aa40
-        /* The parent can do name_to_handle_at() but the
17aa40
-         * directory we are interested in can't? If so, it
17aa40
-         * must be a mount point. */
17aa40
+                /* The parent can't do name_to_handle_at() but the directory we are
17aa40
+                 * interested in can?  If so, it must be a mount point. */
17aa40
+                return 1;
17aa40
+        }
17aa40
+
17aa40
+        /* The parent can do name_to_handle_at() but the directory we are interested in can't? If
17aa40
+         * so, it must be a mount point. */
17aa40
         if (nosupp)
17aa40
                 return 1;
17aa40
 
17aa40
-        /* If the file handle for the directory we are
17aa40
-         * interested in and its parent are identical, we
17aa40
-         * assume this is the root directory, which is a mount
17aa40
-         * point. */
17aa40
+        /* If the file handle for the directory we are interested in and its parent are identical,
17aa40
+         * we assume this is the root directory, which is a mount point. */
17aa40
 
17aa40
         if (h->handle_bytes == h_parent->handle_bytes &&
17aa40
             h->handle_type == h_parent->handle_type &&
17aa40
@@ -300,10 +311,10 @@ int path_get_mnt_id(const char *path, int *ret) {
17aa40
         int r;
17aa40
 
17aa40
         r = name_to_handle_at_loop(AT_FDCWD, path, NULL, ret, 0);
17aa40
-        if (IN_SET(r, -EOPNOTSUPP, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL)) /* kernel/fs don't support this, or seccomp blocks access, or untriggered mount, or name_to_handle_at() is flaky */
17aa40
-                return fd_fdinfo_mnt_id(AT_FDCWD, path, 0, ret);
17aa40
+        if (r == 0 || is_name_to_handle_at_fatal_error(r))
17aa40
+                return r;
17aa40
 
17aa40
-        return r;
17aa40
+        return fd_fdinfo_mnt_id(AT_FDCWD, path, 0, ret);
17aa40
 }
17aa40
 
17aa40
 int umount_recursive(const char *prefix, int flags) {