From 3997c10064a5d526b8d49d72bcbd3720ca793a5a Mon Sep 17 00:00:00 2001
From: Will Thompson <wjt@endlessm.com>
Date: Sun, 12 May 2019 08:37:21 -0700
Subject: [PATCH] Filesystem: ignore fstab entry if another device is mounted
 there

On Endless OS, all systems have the following /etc/fstab:

    LABEL=ostree / ext4  errors=remount-ro 0 0

If you insert a removable device containing a filesystem with the label
'ostree', then asks UDisks to mount it, it will attempt to mount it over
/. If the filesystem is not of type ext4, this will fail; if it is, this
will succeed, with potentially disastrous results.

There is already a check in is_in_fstab() which constructs the path to
the /dev/disk/by-label/$LABEL symlink and compares it to the device
being mounted. However, in this scenario that comparison succeeds: the
symlink is updated when the new device is detected.

To prevent this, use UDisksMountMonitor to look up the mount point
specified in fstab (using a new get_mount_for_path accessor); if an
existing mount is found, and it is a different device to the one being
(un)mounted, ignore the fstab entry.

(cherry picked from commit e86ea628177c3ea67525175f73a143e15ea74d67)
---
 src/udiskslinuxfilesystem.c | 43 ++++++++++++++++++++++++-------------
 src/udisksmountmonitor.c    | 33 ++++++++++++++++++++++++++++
 src/udisksmountmonitor.h    |  2 ++
 3 files changed, 63 insertions(+), 15 deletions(-)

diff --git a/src/udiskslinuxfilesystem.c b/src/udiskslinuxfilesystem.c
index f243046e..9ca70443 100644
--- a/src/udiskslinuxfilesystem.c
+++ b/src/udiskslinuxfilesystem.c
@@ -1025,11 +1025,13 @@ has_option (const gchar *options,
 }
 
 static gboolean
-is_in_fstab (UDisksBlock        *block,
-             const gchar        *fstab_path,
-             gchar             **out_mount_point,
-             gchar             **out_mount_options)
+is_in_fstab (UDisksDaemon *daemon,
+             UDisksBlock  *block,
+             const gchar  *fstab_path,
+             gchar       **out_mount_point,
+             gchar       **out_mount_options)
 {
+  UDisksMountMonitor *mount_monitor = udisks_daemon_get_mount_monitor (daemon);
   gboolean ret;
   FILE *f;
   char buf[8192];
@@ -1083,11 +1085,21 @@ is_in_fstab (UDisksBlock        *block,
 
       if (udisks_block_get_device_number (block) == sb.st_rdev)
         {
-          ret = TRUE;
-          if (out_mount_point != NULL)
-            *out_mount_point = g_strdup (m->mnt_dir);
-          if (out_mount_options != NULL)
-            *out_mount_options = g_strdup (m->mnt_opts);
+          /* If this block device is found in fstab, but something else is already
+           * mounted on that mount point, ignore the fstab entry.
+           */
+          UDisksMount *mount = udisks_mount_monitor_get_mount_for_path (mount_monitor,
+                                                                        m->mnt_dir);
+          if (mount == NULL || sb.st_rdev == udisks_mount_get_dev (mount))
+            {
+              ret = TRUE;
+              if (out_mount_point != NULL)
+                *out_mount_point = g_strdup (m->mnt_dir);
+              if (out_mount_options != NULL)
+                *out_mount_options = g_strdup (m->mnt_opts);
+            }
+
+          g_clear_object (&mount);
         }
 
     continue_loop:
@@ -1106,16 +1118,17 @@ is_in_fstab (UDisksBlock        *block,
  * TODO: check if systemd has a specific "unit" for the device
  */
 static gboolean
-is_system_managed (UDisksBlock        *block,
-                   gchar             **out_mount_point,
-                   gchar             **out_mount_options)
+is_system_managed (UDisksDaemon *daemon,
+                   UDisksBlock  *block,
+                   gchar       **out_mount_point,
+                   gchar       **out_mount_options)
 {
   gboolean ret;
 
   ret = TRUE;
 
   /* First, check /etc/fstab */
-  if (is_in_fstab (block, "/etc/fstab", out_mount_point, out_mount_options))
+  if (is_in_fstab (daemon, block, "/etc/fstab", out_mount_point, out_mount_options))
     goto out;
 
   ret = FALSE;
@@ -1184,7 +1197,7 @@ handle_mount (UDisksFilesystem       *filesystem,
   state = udisks_daemon_get_state (daemon);
 
   /* check if mount point is managed by e.g. /etc/fstab or similar */
-  if (is_system_managed (block, &mount_point_to_use, &fstab_mount_options))
+  if (is_system_managed (daemon, block, &mount_point_to_use, &fstab_mount_options))
     {
       system_managed = TRUE;
     }
@@ -1617,7 +1630,7 @@ handle_unmount (UDisksFilesystem       *filesystem,
     }
 
   /* check if mount point is managed by e.g. /etc/fstab or similar */
-  if (is_system_managed (block, &mount_point, &fstab_mount_options))
+  if (is_system_managed (daemon, block, &mount_point, &fstab_mount_options))
     {
       system_managed = TRUE;
     }
diff --git a/src/udisksmountmonitor.c b/src/udisksmountmonitor.c
index e7097faf..bc7abd57 100644
--- a/src/udisksmountmonitor.c
+++ b/src/udisksmountmonitor.c
@@ -704,3 +704,36 @@ udisks_mount_monitor_is_dev_in_use (UDisksMountMonitor  *monitor,
  out:
   return ret;
 }
+
+/**
+ * udisks_mount_monitor_get_mount_for_path:
+ * @monitor: A #UDisksMountMonitor.
+ * @mount_path: A filesystem path on which a device may be mounted.
+ *
+ * Gets the #UDisksMount mounted at @mount_path, if any.
+ *
+ * Returns: (transfer full) (nullable): the #UDisksMount (of type
+ *  #UDISKS_MOUNT_TYPE_FILESYSTEM) mounted at @mount_path, or %NULL if nothing
+ *  is mounted there.
+ */
+UDisksMount *
+udisks_mount_monitor_get_mount_for_path (UDisksMountMonitor  *monitor,
+                                         const gchar         *mount_path)
+{
+  GList *l;
+
+  g_return_val_if_fail (UDISKS_IS_MOUNT_MONITOR (monitor), NULL);
+  g_return_val_if_fail (mount_path != NULL, NULL);
+
+  udisks_mount_monitor_ensure (monitor);
+  for (l = monitor->mounts; l != NULL; l = l->next)
+    {
+      UDisksMount *mount = UDISKS_MOUNT (l->data);
+
+      if (udisks_mount_get_mount_type (mount) == UDISKS_MOUNT_TYPE_FILESYSTEM &&
+          g_strcmp0 (udisks_mount_get_mount_path (mount), mount_path) == 0)
+        return g_object_ref (mount);
+    }
+
+  return NULL;
+}
diff --git a/src/udisksmountmonitor.h b/src/udisksmountmonitor.h
index c30cf318..5bab112e 100644
--- a/src/udisksmountmonitor.h
+++ b/src/udisksmountmonitor.h
@@ -36,6 +36,8 @@ GList               *udisks_mount_monitor_get_mounts_for_dev (UDisksMountMonitor
 gboolean             udisks_mount_monitor_is_dev_in_use      (UDisksMountMonitor  *monitor,
                                                               dev_t                dev,
                                                               UDisksMountType     *out_type);
+UDisksMount         *udisks_mount_monitor_get_mount_for_path (UDisksMountMonitor  *monitor,
+                                                              const gchar         *mount_path);
 
 G_END_DECLS
 
-- 
2.37.2

