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