Hey,

path_is_mount_point() can report some false positive that a file is a mount point on some file systems (like overlayfs).

The function tries to call name_to_handle_at(), if this one isn't supported by the file system, then a fallback using stat() is triggered on the entity (which can be a file). This compares the parent directory and entity st_dev.

However, some file systems (seems overlayfs at least) would report a major(st_dev) as 0 on directories and not on files. The current path_is_mount_point() fallback logic would thus reports that every files is a mount point. The enclosed patch introduces a second fallback to read /proc/mounts, and ensures there is no shadowing by later mounting of parent directories. This slower path is only used when the 2 first methods failed to determine with assurance if the path is a mount point or not.

Note that from what I heard, glib is going to use a similar mechanism. Also we could on the longer term maybe getting the whole path_is_mount_point() logic into libmount from util-linux, using mnt_get_mountpoint() (but this one only use st_dev comparison presently)?

As usual, I'm opened on any modification on this patch.
Cheers,
Didier
>From 47a8d7546f3fa959344431770f75d8e422192ba3 Mon Sep 17 00:00:00 2001
From: Didier Roche <didro...@ubuntu.com>
Date: Mon, 9 Mar 2015 09:09:30 +0100
Subject: [PATCH] path_is_mount_point: handle false positive on some fs

path_is_mount_point detection mechanism was wrongly reporting every file
as mount point on file systems like overlayfs:
- name_to_handle_at is not supported
- stat() on directories returns a 0 st_dev (and so different from file stat())
In this case, we fall back to the slower path: directly reading /proc/mounts
and matching any mount point corresponding to the path if present. Ensure that
there isn't any hiding mounts.
---
 src/shared/path-util.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 65 insertions(+), 1 deletion(-)

diff --git a/src/shared/path-util.c b/src/shared/path-util.c
index 12d1ec3..2464569 100644
--- a/src/shared/path-util.c
+++ b/src/shared/path-util.c
@@ -457,7 +457,9 @@ int path_is_mount_point(const char *t, bool allow_symlink) {
 
         union file_handle_union h = FILE_HANDLE_INIT;
         int mount_id = -1, mount_id_parent = -1;
-        _cleanup_free_ char *parent = NULL;
+        _cleanup_free_ char *parent = NULL, *path = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        bool mountpoint_found = false;
         struct stat a, b;
         int r;
         bool nosupp = false;
@@ -533,7 +535,69 @@ fallback:
         if (r < 0)
                 return -errno;
 
+        /* if parent major block is 0, this can be an overlay fs,
+           fallback to reading /proc/mounts */
+        if (major(b.st_dev) == 0)
+                goto fallback_proc_mounts;
+
         return a.st_dev != b.st_dev;
+
+fallback_proc_mounts:
+        if (allow_symlink) {
+                r = readlink_and_make_absolute(t, &path);
+                if (r < 0) {
+                        if (errno == ENOENT)
+                                return 0;
+
+                        return -errno;
+                }
+        } else
+                path = strdup(t);
+
+        f = fopen("/proc/mounts", "re");
+        if (!f) {
+                if (errno == ENOENT)
+                        return 0;
+                return -errno;
+        }
+
+        for (;;) {
+                _cleanup_free_ char *mountpoint = NULL, *unesc_mountpoint = NULL;
+                r = fscanf(f,
+                          "%*s "       /* (1) fs spec */
+                          "%ms "       /* (2) mount point */
+                          "%*[^\n]",   /* not interested by other values */
+                          &mountpoint);
+                if (r != 1) {
+                        if (r == EOF)
+                                break;
+                        if (ferror(f) && errno)
+                                return -errno;
+
+                        continue;
+                }
+
+                unesc_mountpoint = cunescape(mountpoint);
+                if (!unesc_mountpoint)
+                        return -ENOMEM;
+
+                /* check that our mount point isn't hidden by a later mount of a parent dir */
+                if (mountpoint_found) {
+                        size_t l = strlen(unesc_mountpoint);
+                        if (strlen(path) > l &&
+                            path_startswith(path, unesc_mountpoint) &&
+                            (unesc_mountpoint[l-1] == '/' || path[l] == '/'))
+                                mountpoint_found = false;
+                }
+
+                if (path_equal(path, unesc_mountpoint))
+                        mountpoint_found = true;
+        }
+
+        if (mountpoint_found)
+                return 1;
+
+        return 0;
 }
 
 int path_is_read_only_fs(const char *path) {
-- 
2.1.4

_______________________________________________
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/systemd-devel

Reply via email to