use /proc/self/mountinfo and handle a device being mounted in multiple places
authorDavid Zeuthen <davidz@redhat.com>
Thu, 2 Apr 2009 17:54:20 +0000 (13:54 -0400)
committerDavid Zeuthen <davidz@redhat.com>
Thu, 2 Apr 2009 17:54:20 +0000 (13:54 -0400)
This is an effort to fix

 https://bugzilla.redhat.com/show_bug.cgi?id=491924

Basically we have to somehow cope with a device being mounted in
multiple places. So we replace the property

 String device-mount-path

with an array of mount paths

 String device-mount-paths[]

We also try to make an effort to ensure that the shortest mount point
is shown before others. E.g. if you bind mount / on /foo then

 device-mount-paths = ["/", "/foo"]

This means that desktop clients can just take the first mount path.

Also make a note that this is the mount point in the root
namespace. On systems clients running in other namespaces care has to
be taken; in this case the client probably needs to examine
/proc/self/mountinfo on his own to determine if a given block device
is mounted or not.

Also drop support for the 'remount' option; we might add it back later
but since we now support multiple mount points it makes everything a
bit weird.

12 files changed:
src/devkit-disks-daemon.c
src/devkit-disks-device-private.c
src/devkit-disks-device-private.h
src/devkit-disks-device.c
src/devkit-disks-device.h
src/devkit-disks-mount-file.c
src/devkit-disks-mount-monitor.c
src/devkit-disks-mount-monitor.h
src/devkit-disks-mount.c
src/job-shared.h
src/org.freedesktop.DeviceKit.Disks.Device.xml
tools/devkit-disks.c

index bbccc82..8e4b423 100644 (file)
@@ -1067,8 +1067,8 @@ register_disks_daemon (DevkitDisksDaemon *daemon)
                           G_CALLBACK (device_event_signal_handler), daemon);
 
         daemon->priv->mount_monitor = devkit_disks_mount_monitor_new ();
-        g_signal_connect (daemon->priv->mount_monitor, "mounted", (GCallback) mount_added, daemon);
-        g_signal_connect (daemon->priv->mount_monitor, "unmounted", (GCallback) mount_removed, daemon);
+        g_signal_connect (daemon->priv->mount_monitor, "mount-added", (GCallback) mount_added, daemon);
+        g_signal_connect (daemon->priv->mount_monitor, "mount-removed", (GCallback) mount_removed, daemon);
 
         return TRUE;
 error:
index 9752523..63b4f85 100644 (file)
@@ -69,7 +69,7 @@ ptr_str_array_equals_strv (GPtrArray *a, GStrv b)
   if (a->len == 0 && b == NULL)
     return TRUE;
 
-  b_len = g_strv_length (b);
+  b_len = (b != NULL ? g_strv_length (b) : 0);
 
   if (a->len != b_len)
     return FALSE;
@@ -409,13 +409,13 @@ devkit_disks_device_set_device_is_mounted (DevkitDisksDevice *device, gboolean v
 }
 
 void
-devkit_disks_device_set_device_mount_path (DevkitDisksDevice *device, const gchar *value)
+devkit_disks_device_set_device_mount_paths (DevkitDisksDevice *device, GStrv value)
 {
-  if (G_UNLIKELY (g_strcmp0 (device->priv->device_mount_path, value) != 0))
+  if (G_UNLIKELY (!ptr_str_array_equals_strv (device->priv->device_mount_paths, value)))
     {
-      g_free (device->priv->device_mount_path);
-      device->priv->device_mount_path = g_strdup (value);
-      emit_changed (device, "device_mount_path");
+      ptr_str_array_free (device->priv->device_mount_paths);
+      device->priv->device_mount_paths = ptr_str_array_from_strv (value);
+      emit_changed (device, "device_mount_paths");
     }
 }
 
index 9bd3f10..09b9cb1 100644 (file)
@@ -121,7 +121,7 @@ struct DevkitDisksDevicePrivate
         guint64 device_size;
         guint64 device_block_size;
         gboolean device_is_mounted;
-        char *device_mount_path;
+        GPtrArray *device_mount_paths;
         uid_t device_mounted_by_uid;
         char *device_presentation_name;
         char *device_presentation_icon_name;
@@ -251,7 +251,7 @@ void devkit_disks_device_set_device_is_linux_md (DevkitDisksDevice *device, gboo
 void devkit_disks_device_set_device_size (DevkitDisksDevice *device, guint64 value);
 void devkit_disks_device_set_device_block_size (DevkitDisksDevice *device, guint64 value);
 void devkit_disks_device_set_device_is_mounted (DevkitDisksDevice *device, gboolean value);
-void devkit_disks_device_set_device_mount_path (DevkitDisksDevice *device, const gchar *value);
+void devkit_disks_device_set_device_mount_paths (DevkitDisksDevice *device, GStrv value);
 void devkit_disks_device_set_device_mounted_by_uid (DevkitDisksDevice *device, guint value);
 void devkit_disks_device_set_device_presentation_name (DevkitDisksDevice *device, const gchar *value);
 void devkit_disks_device_set_device_presentation_icon_name (DevkitDisksDevice *device, const gchar *value);
index fb7cd49..4d2f90b 100644 (file)
@@ -154,7 +154,7 @@ enum
         PROP_DEVICE_BLOCK_SIZE,
         PROP_DEVICE_IS_MOUNTED,
         PROP_DEVICE_IS_BUSY,
-        PROP_DEVICE_MOUNT_PATH,
+        PROP_DEVICE_MOUNT_PATHS,
         PROP_DEVICE_MOUNTED_BY_UID,
         PROP_DEVICE_PRESENTATION_NAME,
         PROP_DEVICE_PRESENTATION_ICON_NAME,
@@ -367,8 +367,8 @@ get_property (GObject         *object,
                 /* this property is special; it's value is computed on demand */
                g_value_set_boolean (value, devkit_disks_device_local_is_busy (device));
                break;
-       case PROP_DEVICE_MOUNT_PATH:
-               g_value_set_string (value, device->priv->device_mount_path);
+       case PROP_DEVICE_MOUNT_PATHS:
+               g_value_set_boxed (value, device->priv->device_mount_paths);
                break;
        case PROP_DEVICE_MOUNTED_BY_UID:
                g_value_set_uint (value, device->priv->device_mounted_by_uid);
@@ -800,8 +800,10 @@ devkit_disks_device_class_init (DevkitDisksDeviceClass *klass)
                 g_param_spec_boolean ("device-is-busy", NULL, NULL, FALSE, G_PARAM_READABLE));
         g_object_class_install_property (
                 object_class,
-                PROP_DEVICE_MOUNT_PATH,
-                g_param_spec_string ("device-mount-path", NULL, NULL, NULL, G_PARAM_READABLE));
+                PROP_DEVICE_MOUNT_PATHS,
+                g_param_spec_boxed ("device-mount-paths", NULL, NULL,
+                                    dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING),
+                                    G_PARAM_READABLE));
         g_object_class_install_property (
                 object_class,
                 PROP_DEVICE_MOUNTED_BY_UID,
@@ -1177,6 +1179,7 @@ devkit_disks_device_init (DevkitDisksDevice *device)
 
         device->priv->device_file_by_id = g_ptr_array_new ();
         device->priv->device_file_by_path = g_ptr_array_new ();
+        device->priv->device_mount_paths = g_ptr_array_new ();
         device->priv->partition_flags = g_ptr_array_new ();
         device->priv->drive_media_compatibility = g_ptr_array_new ();
         device->priv->linux_md_component_state = g_ptr_array_new ();
@@ -1227,7 +1230,7 @@ devkit_disks_device_finalize (GObject *object)
         g_ptr_array_foreach (device->priv->device_file_by_path, (GFunc) g_free, NULL);
         g_ptr_array_free (device->priv->device_file_by_id, TRUE);
         g_ptr_array_free (device->priv->device_file_by_path, TRUE);
-        g_free (device->priv->device_mount_path);
+        g_ptr_array_free (device->priv->device_mount_paths, TRUE);
         g_free (device->priv->device_presentation_name);
         g_free (device->priv->device_presentation_icon_name);
 
@@ -2659,10 +2662,10 @@ update_info_mount_state (DevkitDisksDevice *device)
 {
         DevkitDisksMountMonitor *monitor;
         DevkitDisksMount *mount;
+        GList *mounts;
         gboolean was_mounted;
-        gchar *old_mount_path;
 
-        old_mount_path = NULL;
+        mounts = NULL;
 
         /* defer setting the mount point until FilesystemMount returns and
          * the mounts file is written
@@ -2671,14 +2674,24 @@ update_info_mount_state (DevkitDisksDevice *device)
                 goto out;
 
         monitor = devkit_disks_daemon_local_get_mount_monitor (device->priv->daemon);
-        mount = devkit_disks_mount_monitor_get_mount_for_dev (monitor, device->priv->dev);
+
+        mounts = devkit_disks_mount_monitor_get_mounts_for_dev (monitor, device->priv->dev);
 
         was_mounted = device->priv->device_is_mounted;
-        old_mount_path = g_strdup (device->priv->device_mount_path);
 
-        if (mount != NULL) {
+        if (mounts != NULL) {
+                GList *l;
+                guint n;
+                gchar **mount_paths;
+
+                mount = g_object_ref (mounts->data);
+                mount_paths = g_new0 (gchar *, g_list_length (mounts) + 1);
+                for (l = mounts, n = 0; l != NULL; l = l->next, n++) {
+                        mount_paths[n] = g_strdup (devkit_disks_mount_get_mount_path (DEVKIT_DISKS_MOUNT (l->data)));
+                }
+
                 devkit_disks_device_set_device_is_mounted (device, TRUE);
-                devkit_disks_device_set_device_mount_path (device, devkit_disks_mount_get_mount_path (mount));
+                devkit_disks_device_set_device_mount_paths (device, mount_paths);
                 if (!was_mounted) {
                         uid_t mounted_by_uid;
 
@@ -2686,16 +2699,26 @@ update_info_mount_state (DevkitDisksDevice *device)
                                 mounted_by_uid = 0;
                         devkit_disks_device_set_device_mounted_by_uid (device, mounted_by_uid);
                 }
+
+                g_strfreev (mount_paths);
+
         } else {
                 gboolean remove_dir_on_unmount;
+                gchar *old_mount_path;
+
+                old_mount_path = NULL;
+                if (device->priv->device_mount_paths->len > 0)
+                        old_mount_path = g_strdup (((gchar **) device->priv->device_mount_paths->pdata)[0]);
 
                 devkit_disks_device_set_device_is_mounted (device, FALSE);
-                devkit_disks_device_set_device_mount_path (device, NULL);
+                devkit_disks_device_set_device_mount_paths (device, NULL);
                 devkit_disks_device_set_device_mounted_by_uid (device, 0);
 
                 /* clean up stale mount directory */
                 remove_dir_on_unmount = FALSE;
-                if (was_mounted && devkit_disks_mount_file_has_device (device->priv->device_file, NULL, &remove_dir_on_unmount)) {
+                if (was_mounted && devkit_disks_mount_file_has_device (device->priv->device_file,
+                                                                       NULL,
+                                                                       &remove_dir_on_unmount)) {
                         devkit_disks_mount_file_remove (device->priv->device_file, old_mount_path);
                         if (remove_dir_on_unmount) {
                                 if (g_rmdir (old_mount_path) != 0) {
@@ -2704,10 +2727,13 @@ update_info_mount_state (DevkitDisksDevice *device)
                         }
                 }
 
+                g_free (old_mount_path);
+
         }
 
  out:
-        g_free (old_mount_path);
+        g_list_foreach (mounts, (GFunc) g_object_unref, NULL);
+        g_list_free (mounts);
 
         return TRUE;
 }
@@ -3501,12 +3527,6 @@ devkit_disks_device_local_get_device_file (DevkitDisksDevice *device)
         return device->priv->device_file;
 }
 
-const char *
-devkit_disks_device_local_get_mount_path (DevkitDisksDevice *device)
-{
-        return device->priv->device_mount_path;
-}
-
 /*--------------------------------------------------------------------------------------------------------------*/
 
 static gboolean
@@ -3933,17 +3953,15 @@ out:
 typedef struct {
         char *mount_point;
         gboolean remove_dir_on_unmount;
-        gboolean is_remount;
 } MountData;
 
 static MountData *
-filesystem_mount_data_new (const char *mount_point, gboolean remove_dir_on_unmount, gboolean is_remount)
+filesystem_mount_data_new (const char *mount_point, gboolean remove_dir_on_unmount)
 {
         MountData *data;
         data = g_new0 (MountData, 1);
         data->mount_point = g_strdup (mount_point);
         data->remove_dir_on_unmount = remove_dir_on_unmount;
-        data->is_remount = is_remount;
         return data;
 }
 
@@ -4091,7 +4109,6 @@ static const char *any_allow[] = {"exec",
                                   "atime",
                                   "noatime",
                                   "nodiratime",
-                                  "remount",
                                   "ro",
                                   "rw",
                                   "sync",
@@ -4332,13 +4349,11 @@ filesystem_mount_completed_cb (DBusGMethodInvocation *context,
 
                 dbus_g_method_return (context, data->mount_point);
         } else {
-                if (!data->is_remount) {
-                        devkit_disks_mount_file_remove (device->priv->device_file, data->mount_point);
+                devkit_disks_mount_file_remove (device->priv->device_file, data->mount_point);
 
-                        if (data->remove_dir_on_unmount) {
-                                if (g_rmdir (data->mount_point) != 0) {
-                                        g_warning ("Error removing dir in late mount error path: %m");
-                                }
+                if (data->remove_dir_on_unmount) {
+                        if (g_rmdir (data->mount_point) != 0) {
+                                g_warning ("Error removing dir in late mount error path: %m");
                         }
                 }
 
@@ -4374,14 +4389,12 @@ devkit_disks_device_filesystem_mount (DevkitDisksDevice     *device,
         gboolean remove_dir_on_unmount;
         const FSMountOptions *fsmo;
         char **options;
-        gboolean is_remount;
         char uid_buf[32];
 
         fstype = NULL;
         options = NULL;
         mount_options = NULL;
         mount_point = NULL;
-        is_remount = FALSE;
 
         if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (device->priv->daemon, context)) == NULL)
                 goto out;
@@ -4467,9 +4480,6 @@ devkit_disks_device_filesystem_mount (DevkitDisksDevice     *device,
                         goto out;
                 }
 
-                if (strcmp (option, "remount") == 0)
-                        is_remount = TRUE;
-
                 g_string_append_c (s, ',');
                 g_string_append (s, option);
         }
@@ -4478,94 +4488,59 @@ devkit_disks_device_filesystem_mount (DevkitDisksDevice     *device,
         g_print ("**** USING MOUNT OPTIONS '%s' FOR DEVICE %s\n", mount_options, device->priv->device_file);
 
         if (device->priv->device_is_mounted) {
-                if (!is_remount) {
-                        throw_error (context, DEVKIT_DISKS_ERROR_ALREADY_MOUNTED,
-                                     "Device is already mounted");
-                        goto out;
-                }
+                throw_error (context, DEVKIT_DISKS_ERROR_ALREADY_MOUNTED,
+                             "Device is already mounted");
+                goto out;
         }
 
-        /* TODO: check for auth if user tries to remount something mounted by another user */
-
-        /* handle some constraints required by remount */
-        if (is_remount) {
-                if (!device->priv->device_is_mounted ||
-                    device->priv->device_mount_path == NULL) {
-                        throw_error (context,
-                                     DEVKIT_DISKS_ERROR_NOT_MOUNTED,
-                                     "Can't remount a device that is not mounted");
-                        goto out;
-                }
-
-                if (strlen (filesystem_type) > 0) {
-                        throw_error (context,
-                                     DEVKIT_DISKS_ERROR_FAILED,
-                                     "Can't remount a device with a different file system type");
-                        goto out;
-                }
+        /* Determine the mount point to use.
+         *
+         * TODO: use characteristics of the drive such as the name, connection etc.
+         *       to get better names (/media/disk is kinda lame).
+         */
+        if (device->priv->id_label != NULL && strlen (device->priv->id_label) > 0 ) {
+                mount_point = g_build_filename ("/media", device->priv->id_label, NULL);
+        } else if (device->priv->id_uuid != NULL && strlen (device->priv->id_uuid) > 0) {
+                mount_point = g_build_filename ("/media", device->priv->id_uuid, NULL);
+        } else {
+                mount_point = g_strdup ("/media/disk");
         }
 
-        if (!is_remount) {
-                /* Determine the mount point to use.
-                 *
-                 * TODO: use characteristics of the drive such as the name, connection etc.
-                 *       to get better names (/media/disk is kinda lame).
-                 */
-                if (device->priv->id_label != NULL && strlen (device->priv->id_label) > 0 ) {
-                        mount_point = g_build_filename ("/media", device->priv->id_label, NULL);
-                } else if (device->priv->id_uuid != NULL && strlen (device->priv->id_uuid) > 0) {
-                        mount_point = g_build_filename ("/media", device->priv->id_uuid, NULL);
-                } else {
-                        mount_point = g_strdup ("/media/disk");
-                }
-
 try_another_mount_point:
-                /* ... then uniqify the mount point and mkdir it */
-                if (g_file_test (mount_point, G_FILE_TEST_EXISTS)) {
-                        char *s = mount_point;
-                        /* TODO: append numbers instead of _, __ and so on */
-                        mount_point = g_strdup_printf ("%s_", mount_point);
-                        g_free (s);
-                        goto try_another_mount_point;
-                }
+        /* ... then uniqify the mount point and mkdir it */
+        if (g_file_test (mount_point, G_FILE_TEST_EXISTS)) {
+                char *s = mount_point;
+                /* TODO: append numbers instead of _, __ and so on */
+                mount_point = g_strdup_printf ("%s_", mount_point);
+                g_free (s);
+                goto try_another_mount_point;
+        }
 
-                remove_dir_on_unmount = TRUE;
+        remove_dir_on_unmount = TRUE;
 
-                if (g_mkdir (mount_point, 0700) != 0) {
-                        throw_error (context, DEVKIT_DISKS_ERROR_FAILED, "Error creating moint point: %m");
-                        goto out;
-                }
-
-                n = 0;
-                argv[n++] = "mount";
-                argv[n++] = "-t";
-                argv[n++] = fstype;
-                argv[n++] = "-o";
-                argv[n++] = mount_options;
-                argv[n++] = device->priv->device_file;
-                argv[n++] = mount_point;
-                argv[n++] = NULL;
-        } else {
-                /* we recycle the mount point on remount */
-                mount_point = g_strdup (device->priv->device_mount_path);
-                n = 0;
-                argv[n++] = "mount";
-                argv[n++] = "-o";
-                argv[n++] = mount_options;
-                argv[n++] = mount_point;
-                argv[n++] = NULL;
+        if (g_mkdir (mount_point, 0700) != 0) {
+                throw_error (context, DEVKIT_DISKS_ERROR_FAILED, "Error creating moint point: %m");
+                goto out;
         }
 
+        n = 0;
+        argv[n++] = "mount";
+        argv[n++] = "-t";
+        argv[n++] = fstype;
+        argv[n++] = "-o";
+        argv[n++] = mount_options;
+        argv[n++] = device->priv->device_file;
+        argv[n++] = mount_point;
+        argv[n++] = NULL;
+
 run_job:
-        if (!is_remount) {
-                /* now that we have a mount point, immediately add it to the
-                 * /var/lib/DeviceKit-disks/mtab file
-                 */
-                devkit_disks_mount_file_add (device->priv->device_file,
-                                             mount_point,
-                                             caller_uid,
-                                             remove_dir_on_unmount);
-        }
+        /* now that we have a mount point, immediately add it to the
+         * /var/lib/DeviceKit-disks/mtab file
+         */
+        devkit_disks_mount_file_add (device->priv->device_file,
+                                     mount_point,
+                                     caller_uid,
+                                     remove_dir_on_unmount);
 
         error = NULL;
         if (!job_new (context,
@@ -4576,14 +4551,12 @@ run_job:
                       argv,
                       NULL,
                       filesystem_mount_completed_cb,
-                      filesystem_mount_data_new (mount_point, remove_dir_on_unmount, is_remount),
+                      filesystem_mount_data_new (mount_point, remove_dir_on_unmount),
                       (GDestroyNotify) filesystem_mount_data_free)) {
-                if (!is_remount) {
-                        devkit_disks_mount_file_remove (device->priv->device_file, mount_point);
-                        if (remove_dir_on_unmount) {
-                                if (g_rmdir (mount_point) != 0) {
-                                        g_warning ("Error removing dir in early mount error path: %m");
-                                }
+                devkit_disks_mount_file_remove (device->priv->device_file, mount_point);
+                if (remove_dir_on_unmount) {
+                        if (g_rmdir (mount_point) != 0) {
+                                g_warning ("Error removing dir in early mount error path: %m");
                         }
                 }
                 goto out;
@@ -4661,7 +4634,7 @@ devkit_disks_device_filesystem_unmount (DevkitDisksDevice     *device,
                 polkit_caller_get_uid (pk_caller, &uid);
 
         if (!device->priv->device_is_mounted ||
-            device->priv->device_mount_path == NULL) {
+            device->priv->device_mount_paths->len == 0) {
                 throw_error (context,
                              DEVKIT_DISKS_ERROR_NOT_MOUNTED,
                              "Device is not mounted");
@@ -4727,17 +4700,17 @@ devkit_disks_device_filesystem_unmount (DevkitDisksDevice     *device,
                         goto out;
         }
 
+        mount_path = g_strdup (((gchar **) device->priv->device_mount_paths->pdata)[0]);
+
         n = 0;
         argv[n++] = "umount";
         if (force_unmount) {
                 /* on Linux we currently only have lazy unmount to emulate this */
                 argv[n++] = "-l";
         }
-        argv[n++] = device->priv->device_mount_path;
+        argv[n++] = mount_path;
         argv[n++] = NULL;
 
-        mount_path = g_strdup (device->priv->device_mount_path);
-
 run_job:
         error = NULL;
         if (!job_new (context,
@@ -4877,7 +4850,7 @@ devkit_disks_device_filesystem_list_open_files (DevkitDisksDevice     *device,
                 goto out;
 
         if (!device->priv->device_is_mounted ||
-            device->priv->device_mount_path == NULL) {
+            device->priv->device_mount_paths->len == 0) {
                 throw_error (context,
                              DEVKIT_DISKS_ERROR_NOT_MOUNTED,
                              "Device is not mounted");
@@ -4895,7 +4868,7 @@ devkit_disks_device_filesystem_list_open_files (DevkitDisksDevice     *device,
         n = 0;
         argv[n++] = "lsof";
         argv[n++] = "-t";
-        argv[n++] = device->priv->device_mount_path;
+        argv[n++] = ((gchar **) device->priv->device_mount_paths->pdata)[0];
         argv[n++] = NULL;
 
         error = NULL;
@@ -8688,7 +8661,7 @@ typedef struct {
 } ForceUnmountData;
 
 static ForceUnmountData *
-force_unmount_data_new (char                     *mount_path,
+force_unmount_data_new (const gchar              *mount_path,
                         ForceRemovalCompleteFunc  fr_callback,
                         gpointer                  fr_user_data)
 {
@@ -8752,12 +8725,15 @@ force_unmount (DevkitDisksDevice        *device,
         int n;
         char *argv[16];
         GError *error;
+        const gchar *mount_path;
+
+        mount_path = ((gchar **) device->priv->device_mount_paths->pdata)[0];
 
         n = 0;
         argv[n++] = "umount";
         /* on Linux, we only have lazy unmount for now */
         argv[n++] = "-l";
-        argv[n++] = device->priv->device_mount_path;
+        argv[n++] = (gchar *) mount_path;
         argv[n++] = NULL;
 
         error = NULL;
@@ -8769,7 +8745,7 @@ force_unmount (DevkitDisksDevice        *device,
                       argv,
                       NULL,
                       force_unmount_completed_cb,
-                      force_unmount_data_new (device->priv->device_mount_path, callback, user_data),
+                      force_unmount_data_new (mount_path, callback, user_data),
                       (GDestroyNotify) force_unmount_data_unref)) {
                 g_warning ("Couldn't spawn unmount for force unmounting: %s", error->message);
                 g_error_free (error);
@@ -8928,7 +8904,7 @@ force_removal (DevkitDisksDevice        *device,
          *    companion. If so, tear it down if it was setup by us.
          *
          */
-        if (device->priv->device_is_mounted && device->priv->device_mount_path != NULL) {
+        if (device->priv->device_is_mounted && device->priv->device_mount_paths->len > 0) {
                 gboolean remove_dir_on_unmount;
 
                 if (devkit_disks_mount_file_has_device (device->priv->device_file, NULL, &remove_dir_on_unmount)) {
index 667870c..3ca79ab 100644 (file)
@@ -68,7 +68,6 @@ const char        *devkit_disks_device_local_get_native_path     (DevkitDisksDev
 
 dev_t              devkit_disks_device_local_get_dev             (DevkitDisksDevice *device);
 const char        *devkit_disks_device_local_get_device_file     (DevkitDisksDevice *device);
-const char        *devkit_disks_device_local_get_mount_path      (DevkitDisksDevice *device);
 
 /* exported methods */
 
index 5de48fc..ae5cdb2 100644 (file)
@@ -298,11 +298,10 @@ devkit_disks_mount_file_clean_stale (GList *existing_devices)
                                 char *mount_path_escaped;
 
                                 if (!device->priv->device_is_mounted ||
-                                    device->priv->device_mount_path == NULL)
+                                    device->priv->device_mount_paths == NULL)
                                         continue;
 
-                                mount_path_escaped =
-                                        g_uri_escape_string (device->priv->device_mount_path, NULL, TRUE);
+                                mount_path_escaped = g_uri_escape_string (((gchar **) device->priv->device_mount_paths->pdata)[0], NULL, TRUE);
 
                                 if (strcmp (line_mount_path, mount_path_escaped) == 0) {
                                         entry_is_valid = TRUE;
index ff4722a..0868473 100644 (file)
@@ -43,8 +43,8 @@
 
 enum
 {
-        MOUNTED_SIGNAL,
-        UNMOUNTED_SIGNAL,
+        MOUNT_ADDED_SIGNAL,
+        MOUNT_REMOVED_SIGNAL,
         LAST_SIGNAL,
 };
 
@@ -53,8 +53,8 @@ static guint signals[LAST_SIGNAL] = { 0 };
 struct DevkitDisksMountMonitorPrivate
 {
        GIOChannel *mounts_channel;
-        GHashTable *mounts;
         gboolean    have_data;
+        GList      *mounts;
 };
 
 G_DEFINE_TYPE (DevkitDisksMountMonitor, devkit_disks_mount_monitor, G_TYPE_OBJECT)
@@ -69,7 +69,8 @@ devkit_disks_mount_monitor_finalize (GObject *object)
         if (monitor->priv->mounts_channel != NULL)
                 g_io_channel_unref (monitor->priv->mounts_channel);
 
-        g_hash_table_unref (monitor->priv->mounts);
+        g_list_foreach (monitor->priv->mounts, (GFunc) g_object_unref, NULL);
+        g_list_free (monitor->priv->mounts);
 
         if (G_OBJECT_CLASS (devkit_disks_mount_monitor_parent_class)->finalize != NULL)
                 (* G_OBJECT_CLASS (devkit_disks_mount_monitor_parent_class)->finalize) (object);
@@ -82,10 +83,7 @@ devkit_disks_mount_monitor_init (DevkitDisksMountMonitor *monitor)
                                                      DEVKIT_DISKS_TYPE_MOUNT_MONITOR,
                                                      DevkitDisksMountMonitorPrivate);
 
-        monitor->priv->mounts = g_hash_table_new_full (g_direct_hash,
-                                                       g_direct_equal,
-                                                       NULL,
-                                                       g_object_unref);
+        monitor->priv->mounts = NULL;
 }
 
 static void
@@ -98,17 +96,17 @@ devkit_disks_mount_monitor_class_init (DevkitDisksMountMonitorClass *klass)
         g_type_class_add_private (klass, sizeof (DevkitDisksMountMonitorPrivate));
 
         /**
-         * DevkitDisksMountMonitor::mounted
+         * DevkitDisksMountMonitor::mount-added
          * @monitor: A #DevkitDisksMountMonitor.
-         * @mount: The #DevkitDisksMount that was mounted.
+         * @mount: The #DevkitDisksMount that was added.
          *
-         * Emitted when a filesystem is mounted.
+         * Emitted when a mount is added.
          */
-        signals[MOUNTED_SIGNAL] =
-                g_signal_new ("mounted",
+        signals[MOUNT_ADDED_SIGNAL] =
+                g_signal_new ("mount-added",
                               G_OBJECT_CLASS_TYPE (klass),
                               G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
-                              0,
+                              G_STRUCT_OFFSET (DevkitDisksMountMonitorClass, mount_added),
                               NULL,
                               NULL,
                               g_cclosure_marshal_VOID__OBJECT,
@@ -117,17 +115,17 @@ devkit_disks_mount_monitor_class_init (DevkitDisksMountMonitorClass *klass)
                               DEVKIT_DISKS_TYPE_MOUNT);
 
         /**
-         * DevkitDisksMountMonitor::unmounted
+         * DevkitDisksMountMonitor::mount-removed
          * @monitor: A #DevkitDisksMountMonitor.
-         * @mount: The #DevkitDisksMount that was unmounted.
+         * @mount: The #DevkitDisksMount that was removed.
          *
-         * Emitted when a filesystem is unmounted.
+         * Emitted when a mount is removed.
          */
-        signals[UNMOUNTED_SIGNAL] =
-                g_signal_new ("unmounted",
+        signals[MOUNT_REMOVED_SIGNAL] =
+                g_signal_new ("mount-removed",
                               G_OBJECT_CLASS_TYPE (klass),
                               G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
-                              0,
+                              G_STRUCT_OFFSET (DevkitDisksMountMonitorClass, mount_removed),
                               NULL,
                               NULL,
                               g_cclosure_marshal_VOID__OBJECT,
@@ -193,14 +191,17 @@ mounts_changed_event (GIOChannel *channel, GIOCondition cond, gpointer user_data
        if (cond & ~G_IO_ERR)
                 goto out;
 
+        g_debug ("/proc/self/mountinfo changed");
+
         devkit_disks_mount_monitor_ensure (monitor);
-        old_mounts = g_hash_table_get_values (monitor->priv->mounts);
+
+        old_mounts = g_list_copy (monitor->priv->mounts);
         g_list_foreach (old_mounts, (GFunc) g_object_ref, NULL);
 
         devkit_disks_mount_monitor_invalidate (monitor);
         devkit_disks_mount_monitor_ensure (monitor);
 
-        cur_mounts = g_hash_table_get_values (monitor->priv->mounts);
+        cur_mounts = g_list_copy (monitor->priv->mounts);
 
         old_mounts = g_list_sort (old_mounts, (GCompareFunc) devkit_disks_mount_compare);
         cur_mounts = g_list_sort (cur_mounts, (GCompareFunc) devkit_disks_mount_compare);
@@ -214,7 +215,7 @@ mounts_changed_event (GIOChannel *channel, GIOCondition cond, gpointer user_data
         for (l = removed; l != NULL; l = l->next) {
                 DevkitDisksMount *mount = DEVKIT_DISKS_MOUNT (l->data);
                 g_signal_emit (monitor,
-                               signals[UNMOUNTED_SIGNAL],
+                               signals[MOUNT_REMOVED_SIGNAL],
                                0,
                                mount);
         }
@@ -222,7 +223,7 @@ mounts_changed_event (GIOChannel *channel, GIOCondition cond, gpointer user_data
         for (l = added; l != NULL; l = l->next) {
                 DevkitDisksMount *mount = DEVKIT_DISKS_MOUNT (l->data);
                 g_signal_emit (monitor,
-                               signals[MOUNTED_SIGNAL],
+                               signals[MOUNT_ADDED_SIGNAL],
                                0,
                                mount);
         }
@@ -247,11 +248,11 @@ devkit_disks_mount_monitor_new (void)
         mount_monitor = DEVKIT_DISKS_MOUNT_MONITOR (g_object_new (DEVKIT_DISKS_TYPE_MOUNT_MONITOR, NULL));
 
         error = NULL;
-       mount_monitor->priv->mounts_channel = g_io_channel_new_file ("/proc/mounts", "r", &error);
+       mount_monitor->priv->mounts_channel = g_io_channel_new_file ("/proc/self/mountinfo", "r", &error);
        if (mount_monitor->priv->mounts_channel != NULL) {
                g_io_add_watch (mount_monitor->priv->mounts_channel, G_IO_ERR, mounts_changed_event, mount_monitor);
        } else {
-                g_warning ("No /proc/mounts file: %s", error->message);
+                g_warning ("No /proc/self/mountinfo file: %s", error->message);
                 g_error_free (error);
                 g_object_unref (mount_monitor);
                 mount_monitor = NULL;
@@ -266,59 +267,131 @@ void
 devkit_disks_mount_monitor_invalidate (DevkitDisksMountMonitor *monitor)
 {
         monitor->priv->have_data = FALSE;
-        g_hash_table_remove_all (monitor->priv->mounts);
+
+        g_list_foreach (monitor->priv->mounts, (GFunc) g_object_unref, NULL);
+        g_list_free (monitor->priv->mounts);
+        monitor->priv->mounts = NULL;
+}
+
+static gboolean
+have_mount (DevkitDisksMountMonitor *monitor,
+            dev_t                    dev,
+            const gchar             *mount_point)
+{
+        GList *l;
+        gboolean ret;
+
+        ret = FALSE;
+
+        for (l = monitor->priv->mounts; l != NULL; l = l->next) {
+                DevkitDisksMount *mount = DEVKIT_DISKS_MOUNT (l->data);
+                if (devkit_disks_mount_get_dev (mount) == dev &&
+                    g_strcmp0 (devkit_disks_mount_get_mount_path (mount), mount_point) == 0) {
+                        ret = TRUE;
+                        break;
+                }
+        }
+
+        return ret;
 }
 
 static void
 devkit_disks_mount_monitor_ensure (DevkitDisksMountMonitor *monitor)
 {
-        struct mntent *m;
-        FILE *f;
+        gchar *contents;
+        gchar **lines;
+        GError *error;
+        guint n;
+
+        contents = NULL;
+        lines = NULL;
 
         if (monitor->priv->have_data)
                 goto out;
 
-        f = fopen ("/proc/mounts", "r");
-        if (f == NULL) {
-                g_warning ("error opening /proc/mounts: %m");
+        error = NULL;
+        if (!g_file_get_contents ("/proc/self/mountinfo", &contents, NULL, &error)) {
+                g_warning ("Error reading /proc/self/mountinfo: %s", error->message);
+                g_error_free (error);
                 goto out;
         }
-        while ((m = getmntent (f)) != NULL) {
-                DevkitDisksMount *mount;
-                struct stat statbuf;
 
-                /* ignore if not an absolute patch */
-                if (m->mnt_fsname[0] != '/')
+        /* See Documentation/filesystems/proc.txt for the format of /proc/self/mountinfo
+         *
+         * Note that things like space are encoded as \020.
+         */
+
+        lines = g_strsplit (contents, "\n", 0);
+        for (n = 0; lines[n] != NULL; n++) {
+                guint mount_id;
+                guint parent_id;
+                guint major, minor;
+                gchar encoded_root[PATH_MAX];
+                gchar encoded_mount_point[PATH_MAX];
+                gchar *mount_point;
+                dev_t dev;
+
+                if (strlen (lines[n]) == 0)
+                        continue;
+
+                if (sscanf (lines[n], "%d %d %d:%d %s %s",
+                            &mount_id,
+                            &parent_id,
+                            &major,
+                            &minor,
+                            encoded_root,
+                            encoded_mount_point) != 6) {
+                        g_warning ("Error parsing line '%s'", lines[n]);
+                        continue;
+                }
+
+                /* ignore mounts where only a subtree of a filesystem is mounted */
+                if (g_strcmp0 (encoded_root, "/") != 0)
                         continue;
 
-                if (stat (m->mnt_fsname, &statbuf) != 0) {
-                        g_warning ("Cannot stat %s: %m", m->mnt_fsname);
-                } else if (statbuf.st_rdev != 0) {
+                mount_point = g_strcompress (encoded_mount_point);
 
-                        mount = _devkit_disks_mount_new (statbuf.st_rdev, m->mnt_dir);
+                dev = makedev (major, minor);
 
-                        g_hash_table_insert (monitor->priv->mounts,
-                                             GINT_TO_POINTER (statbuf.st_rdev),
-                                             mount);
+                /* TODO: we can probably use a hash table or something if this turns out to be slow */
+                if (!have_mount (monitor, dev, mount_point)) {
+                        DevkitDisksMount *mount;
+                        mount = _devkit_disks_mount_new (dev, mount_point);
+                        monitor->priv->mounts = g_list_prepend (monitor->priv->mounts, mount);
+                        g_debug ("SUP ADDING %d:%d on %s", major, minor, mount_point);
                 }
+
+                g_free (mount_point);
         }
-        fclose (f);
 
         monitor->priv->have_data = TRUE;
 
 out:
-        ;
+        g_free (contents);
+        g_strfreev (lines);
 }
 
-DevkitDisksMount *
-devkit_disks_mount_monitor_get_mount_for_dev (DevkitDisksMountMonitor *monitor,
-                                              dev_t                    dev)
+GList *
+devkit_disks_mount_monitor_get_mounts_for_dev (DevkitDisksMountMonitor *monitor,
+                                               dev_t                    dev)
 {
-        DevkitDisksMount *ret;
+        GList *ret;
+        GList *l;
+
+        ret = NULL;
 
         devkit_disks_mount_monitor_ensure (monitor);
 
-        ret = g_hash_table_lookup (monitor->priv->mounts, GINT_TO_POINTER (dev));
+        for (l = monitor->priv->mounts; l != NULL; l = l->next) {
+                DevkitDisksMount *mount = DEVKIT_DISKS_MOUNT (l->data);
+
+                if (devkit_disks_mount_get_dev (mount) == dev) {
+                        ret = g_list_prepend (ret, g_object_ref (mount));
+                }
+        }
+
+        /* Sort the list to ensure that shortest mount paths appear first */
+        ret = g_list_sort (ret, (GCompareFunc) devkit_disks_mount_compare);
 
         return ret;
 }
index ac9a9ee..8727580 100644 (file)
@@ -38,17 +38,26 @@ typedef struct DevkitDisksMountMonitorPrivate DevkitDisksMountMonitorPrivate;
 struct DevkitDisksMountMonitor
 {
         GObject                         parent;
+
+        /*< private >*/
         DevkitDisksMountMonitorPrivate *priv;
 };
 
 struct DevkitDisksMountMonitorClass
 {
         GObjectClass   parent_class;
+
+        /*< public >*/
+        /* signals */
+        void (*mount_added)   (DevkitDisksMountMonitor *monitor,
+                               DevkitDisksMount        *mount);
+        void (*mount_removed) (DevkitDisksMountMonitor *monitor,
+                               DevkitDisksMount        *mount);
 };
 
 GType                    devkit_disks_mount_monitor_get_type                  (void) G_GNUC_CONST;
 DevkitDisksMountMonitor *devkit_disks_mount_monitor_new                       (void);
-DevkitDisksMount        *devkit_disks_mount_monitor_get_mount_for_dev         (DevkitDisksMountMonitor *monitor,
+GList                   *devkit_disks_mount_monitor_get_mounts_for_dev        (DevkitDisksMountMonitor *monitor,
                                                                                dev_t                    dev);
 void                     devkit_disks_mount_monitor_invalidate                (DevkitDisksMountMonitor *monitor);
 
index 3f71375..c4bdbf3 100644 (file)
@@ -110,7 +110,7 @@ devkit_disks_mount_compare (DevkitDisksMount *a,
 {
         gint ret;
 
-        ret = g_strcmp0 (a->priv->mount_path, b->priv->mount_path);
+        ret = g_strcmp0 (b->priv->mount_path, a->priv->mount_path);
         if (ret != 0)
                 goto out;
 
index 24d4e17..7b04ddd 100644 (file)
@@ -225,8 +225,8 @@ task_zero_device (const char *device, guint64 offset, guint64 size, int num_pass
                                 goto out;
 
                         cursor += num;
-
                         percent = 100 * cursor / size;
+
                         if (percent > old_percent) {
                                 g_print ("progress: %d %d %d zeroing\n", cur_task, num_tasks, percent);
                                 old_percent = percent;
index 3a87285..de0163d 100644 (file)
@@ -46,7 +46,7 @@
           Most methods on this interface take an array of strings
           for options that can affect what the method does. Some of
           these options are literal strings (such
-          as <doc:tt>remount</doc:tt>) while some are encoded in the
+          as <doc:tt>noatime</doc:tt>) while some are encoded in the
           form of a key/value pair (such
           as <doc:tt>label=</doc:tt>). While the documentation for
           each method will specify exactly what set of options are
         <doc:doc><doc:summary>File system type to use.</doc:summary></doc:doc>
       </arg>
       <arg name="options" direction="in" type="as">
-        <doc:doc><doc:summary>Mount Options. Valid mount options include 'remount' and other mount options accepted by the native mount program.</doc:summary></doc:doc>
+        <doc:doc><doc:summary>Mount Options. Valid mount options include mount options accepted by the native mount program.</doc:summary></doc:doc>
       </arg>
       <arg name="mount_path" direction="out" type="s">
         <doc:doc><doc:summary>Where the device was mounted.</doc:summary></doc:doc>
           <doc:error name="&ERROR_NOT_FILESYSTEM;">if the device is not a mountable file system</doc:error>
           <doc:error name="&ERROR_INVALID_OPTION;">if an invalid or malformed mount option was given</doc:error>
           <doc:error name="&ERROR_ALREADY_MOUNTED;">if the device is already mounted</doc:error>
-          <doc:error name="&ERROR_NOT_MOUNTED;">if the remount option was given and the device wasn't mounted</doc:error>
         </doc:errors>
       </doc:doc>
     </method>
             TRUE if the device is mounted.
       </doc:para></doc:description></doc:doc>
     </property>
-    <property name="device-mount-path" type="s" access="read">
+    <property name="device-mount-paths" type="as" access="read">
       <doc:doc><doc:description><doc:para>
-            Where the device is mounted.
+            A list of paths in the root namespace where the root of the device is mounted.
             This property is only valid if
             <doc:ref type="property" to="Device:device-is-mounted">device-is-mounted</doc:ref>
             is TRUE.
index 47ebe9f..d85ca86 100644 (file)
@@ -366,7 +366,7 @@ typedef struct
         gboolean device_is_busy;
         gboolean device_is_linux_md_component;
         gboolean device_is_linux_md;
-        char    *device_mount_path;
+        char   **device_mount_paths;
         uid_t    device_mounted_by_uid;
         char    *device_presentation_name;
         char    *device_presentation_icon_name;
@@ -518,8 +518,8 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props)
                 props->device_is_mounted = g_value_get_boolean (value);
         else if (strcmp (key, "device-is-busy") == 0)
                 props->device_is_busy = g_value_get_boolean (value);
-        else if (strcmp (key, "device-mount-path") == 0)
-                props->device_mount_path = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "device-mount-paths") == 0)
+                props->device_mount_paths = g_strdupv (g_value_get_boxed (value));
         else if (strcmp (key, "device-mounted-by-uid") == 0)
                 props->device_mounted_by_uid = g_value_get_uint (value);
         else if (strcmp (key, "device-presentation-name") == 0)
@@ -732,7 +732,7 @@ device_properties_free (DeviceProperties *props)
         g_free (props->device_file);
         g_strfreev (props->device_file_by_id);
         g_strfreev (props->device_file_by_path);
-        g_free (props->device_mount_path);
+        g_strfreev (props->device_mount_paths);
         g_free (props->device_presentation_name);
         g_free (props->device_presentation_icon_name);
         g_free (props->job_id);
@@ -997,7 +997,13 @@ do_show_info (const char *object_path)
         g_print ("  is read only:            %d\n", props->device_is_read_only);
         g_print ("  is mounted:              %d\n", props->device_is_mounted);
         g_print ("  is busy:                 %d\n", props->device_is_busy);
-        g_print ("  mount path:              %s\n", props->device_mount_path);
+        g_print ("  mount paths:             ");
+        for (n = 0; props->device_mount_paths != NULL && props->device_mount_paths[n] != NULL; n++) {
+                if (n != 0)
+                        g_print (", ");
+                g_print ("%s", props->device_mount_paths[n]);
+        }
+        g_print ("\n");
         g_print ("  mounted by uid:          %d\n", props->device_mounted_by_uid);
         g_print ("  presentation name:       %s\n", props->device_presentation_name);
         g_print ("  presentation icon:       %s\n", props->device_presentation_icon_name);
@@ -1185,9 +1191,9 @@ do_show_info (const char *object_path)
                                 val = props->drive_ata_smart_power_on_seconds;
 
                                 if (val > 60 * 60 * 24) {
-                                        power_on_text = g_strdup_printf (_("%.3g days"), val / 60.0 / 60.0 / 24.0);
+                                        power_on_text = g_strdup_printf ("%.3g days", val / 60.0 / 60.0 / 24.0);
                                 } else {
-                                        power_on_text = g_strdup_printf (_("%.3g hours"), val / 60.0 / 60.0);
+                                        power_on_text = g_strdup_printf ("%.3g hours", val / 60.0 / 60.0);
                                 }
 
                                 g_print ("      powered on:          %s\n", power_on_text);