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