forcibly unmount devices mounted by ourselves on unsafe media removal
authorDavid Zeuthen <davidz@redhat.com>
Tue, 25 Mar 2008 07:35:22 +0000 (03:35 -0400)
committerDavid Zeuthen <davidz@redhat.com>
Tue, 25 Mar 2008 07:35:22 +0000 (03:35 -0400)
src/devkit-disks-daemon.c
src/devkit-disks-daemon.h
src/devkit-disks-device.c
src/devkit-disks-device.h
src/mounts-file.c

index 78b6129..d4143d8 100644 (file)
@@ -167,8 +167,8 @@ devkit_disks_daemon_reset_killtimer (DevkitDisksDaemon *daemon)
 
 /*--------------------------------------------------------------------------------------------------------------*/
 
-static void
-update_mount_state  (DevkitDisksDaemon *daemon, GList *devices)
+void
+devkit_disks_daemon_local_update_mount_state (DevkitDisksDaemon *daemon, GList *devices, gboolean emit_changed)
 {
         GList *l;
         GList *mounts;
@@ -206,7 +206,7 @@ update_mount_state  (DevkitDisksDaemon *daemon, GList *devices)
                                         /* same mount path; no changes */
                                 } else {
                                         /* device was just mounted... or the mount path changed */
-                                        devkit_disks_device_local_set_mounted (device, mount_path);
+                                        devkit_disks_device_local_set_mounted (device, mount_path, emit_changed);
                                 }
 
                                 /* we're mounted so remove from list of devices (see below) */
@@ -236,7 +236,7 @@ update_mount_state  (DevkitDisksDaemon *daemon, GList *devices)
 
                 device_mount_path = devkit_disks_device_local_get_mount_path (device);
                 if (device_mount_path != NULL) {
-                        devkit_disks_device_local_set_unmounted (device);
+                        devkit_disks_device_local_set_unmounted (device, emit_changed);
                 }
         }
 
@@ -564,7 +564,6 @@ static void
 device_add (DevkitDisksDaemon *daemon, const char *native_path, gboolean emit_event)
 {
         DevkitDisksDevice *device;
-        GList *l;
 
         device = g_hash_table_lookup (daemon->priv->map_native_path_to_device, native_path);
         if (device != NULL) {
@@ -575,11 +574,6 @@ device_add (DevkitDisksDaemon *daemon, const char *native_path, gboolean emit_ev
                 device = devkit_disks_device_new (daemon, native_path);
 
                 if (device != NULL) {
-                        /* update whether device is mounted */
-                        l = g_list_prepend (NULL, device);
-                        update_mount_state (daemon, l);
-                        g_list_free (l);
-
                         /* only take a weak ref; the device will stay on the bus until
                          * it's unreffed. So if we ref it, it'll never go away. Stupid
                          * dbus-glib, no cookie for you.
@@ -742,7 +736,7 @@ mounts_changed (GUnixMountMonitor *monitor, gpointer user_data)
         GList *devices;
 
         devices = g_hash_table_get_values (daemon->priv->map_native_path_to_device);
-        update_mount_state (daemon, devices);
+        devkit_disks_daemon_local_update_mount_state (daemon, devices, TRUE);
         g_list_free (devices);
 }
 
index 075ce81..d22696f 100644 (file)
@@ -84,6 +84,11 @@ gboolean           devkit_disks_damon_local_check_auth             (DevkitDisksD
                                                                     PolKitCaller          *pk_caller,
                                                                     const char            *action_id,
                                                                     DBusGMethodInvocation *context);
+
+void               devkit_disks_daemon_local_update_mount_state  (DevkitDisksDaemon       *daemon,
+                                                                  GList                   *devices,
+                                                                  gboolean                 emit_changed);
+
 /* exported methods */
 
 gboolean devkit_disks_daemon_inhibit_shutdown (DevkitDisksDaemon     *daemon,
index 203c8cd..56cfacf 100644 (file)
@@ -93,6 +93,8 @@ devkit_disks_device_create_filesystem_internal (DevkitDisksDevice       *device,
                                                 gpointer                 hook_user_data,
                                                 DBusGMethodInvocation *context);
 
+static void force_unmount (DevkitDisksDevice *device, gboolean remove_dir_on_unmount);
+
 enum
 {
         PROP_0,
@@ -1121,6 +1123,7 @@ update_info (DevkitDisksDevice *device)
         char *path;
         GDir *dir;
         const char *name;
+        GList *l;
 
         ret = FALSE;
         info = NULL;
@@ -1235,6 +1238,11 @@ update_info (DevkitDisksDevice *device)
 
         update_slaves (device);
 
+        /* update whether device is mounted */
+        l = g_list_prepend (NULL, device);
+        devkit_disks_daemon_local_update_mount_state (device->priv->daemon, l, FALSE);
+        g_list_free (l);
+
         ret = TRUE;
 
 out:
@@ -1272,6 +1280,20 @@ void
 devkit_disks_device_removed (DevkitDisksDevice *device)
 {
         update_slaves (device);
+
+        /* if this device was mounted by us, then forcibly unmount it
+         *
+         * This is the normally the path where the enclosing device is
+         * removed. Compare with devkit_disks_device_changed() for the
+         * other path.
+         */
+        if (device->priv->info.device_is_mounted && device->priv->info.device_mount_path != NULL) {
+                gboolean remove_dir_on_unmount;
+                if (mounts_file_has_device (device, NULL, &remove_dir_on_unmount)) {
+                        g_warning ("Force unmounting device %s", device->priv->info.device_file);
+                        force_unmount (device, remove_dir_on_unmount);
+                }
+        }
 }
 
 DevkitDisksDevice *
@@ -1343,6 +1365,48 @@ devkit_disks_device_changed (DevkitDisksDevice *device)
         /* TODO: fix up update_info to return TRUE iff something has changed */
         if (update_info (device))
                 emit_changed (device);
+
+        /* Check if media was removed. If so, we need to forcibly unmount the device
+         * or all the partitions of the device
+         *
+         * This is the normally the path where the media is removed but the enclosing
+         * device is still present. Compare with devkit_disks_device_removed() for
+         * the other path.
+         */
+        if (!device->priv->info.device_is_media_available) {
+                gboolean remove_dir_on_unmount;
+
+                if (device->priv->info.device_is_mounted && device->priv->info.device_mount_path != NULL) {
+                        if (mounts_file_has_device (device, NULL, &remove_dir_on_unmount)) {
+                                g_warning ("Force unmounting device %s", device->priv->info.device_file);
+                                force_unmount (device, remove_dir_on_unmount);
+                        }
+                } else {
+                        GList *l;
+                        GList *devices;
+
+                        /* check all partitions */
+                        devices = devkit_disks_daemon_local_get_all_devices (device->priv->daemon);
+                        for (l = devices; l != NULL; l = l->next) {
+                                DevkitDisksDevice *d = DEVKIT_DISKS_DEVICE (l->data);
+
+                                if (d->priv->info.device_is_partition &&
+                                    d->priv->info.partition_slave != NULL &&
+                                    strcmp (d->priv->info.partition_slave, device->priv->object_path) == 0) {
+
+                                        if (d->priv->info.device_is_mounted &&
+                                            d->priv->info.device_mount_path != NULL) {
+                                                if (mounts_file_has_device (d, NULL, &remove_dir_on_unmount)) {
+                                                        g_warning ("Force unmounting device %s",
+                                                                   d->priv->info.device_file);
+                                                        force_unmount (d, remove_dir_on_unmount);
+                                                }
+                                        }
+
+                                }
+                        } /* for all devices */
+                }
+        }
 }
 
 /*--------------------------------------------------------------------------------------------------------------*/
@@ -1444,16 +1508,20 @@ devkit_disks_device_local_get_mount_path (DevkitDisksDevice *device)
 }
 
 void
-devkit_disks_device_local_set_mounted (DevkitDisksDevice *device, const char *mount_path)
+devkit_disks_device_local_set_mounted (DevkitDisksDevice *device,
+                                       const char        *mount_path,
+                                       gboolean           emit_changed_signal)
 {
         g_free (device->priv->info.device_mount_path);
         device->priv->info.device_mount_path = g_strdup (mount_path);
         device->priv->info.device_is_mounted = TRUE;
-        emit_changed (device);
+        if (emit_changed_signal)
+                emit_changed (device);
 }
 
 void
-devkit_disks_device_local_set_unmounted (DevkitDisksDevice *device)
+devkit_disks_device_local_set_unmounted (DevkitDisksDevice *device,
+                                         gboolean           emit_changed_signal)
 {
         char *mount_path;
         gboolean remove_dir_on_unmount;
@@ -1479,7 +1547,8 @@ devkit_disks_device_local_set_unmounted (DevkitDisksDevice *device)
                 }
         }
 
-        emit_changed (device);
+        if (emit_changed_signal)
+                emit_changed (device);
 
         g_free (mount_path);
 }
@@ -2218,7 +2287,7 @@ mount_completed_cb (DBusGMethodInvocation *context,
 
         if (WEXITSTATUS (status) == 0 && !job_was_cancelled) {
                 if (!data->is_remount) {
-                        devkit_disks_device_local_set_mounted (device, data->mount_point);
+                        devkit_disks_device_local_set_mounted (device, data->mount_point, TRUE);
                         mounts_file_add (device, uid, data->remove_dir_on_unmount);
                 }
                 dbus_g_method_return (context, data->mount_point);
@@ -2487,7 +2556,7 @@ unmount_completed_cb (DBusGMethodInvocation *context,
         char *mount_path = user_data;
 
         if (WEXITSTATUS (status) == 0 && !job_was_cancelled) {
-                devkit_disks_device_local_set_unmounted (device);
+                devkit_disks_device_local_set_unmounted (device, TRUE);
                 mounts_file_remove (device, mount_path);
                 dbus_g_method_return (context);
         } else {
@@ -4275,3 +4344,65 @@ out:
 }
 
 /*--------------------------------------------------------------------------------------------------------------*/
+
+static void
+force_unmount_completed_cb (DBusGMethodInvocation *context,
+                            DevkitDisksDevice *device,
+                            PolKitCaller *pk_caller,
+                            gboolean job_was_cancelled,
+                            int status,
+                            const char *stderr,
+                            const char *stdout,
+                            gpointer user_data)
+{
+        const char *mount_path = user_data;
+        char *touch_str;
+
+        if (WEXITSTATUS (status) == 0 && !job_was_cancelled) {
+
+                g_warning ("Successfully force unmounted device %s", device->priv->info.device_file);
+                devkit_disks_device_local_set_unmounted (device, TRUE);
+                mounts_file_remove (device, mount_path);
+
+                /* TODO: when we add polling, this can probably be removed. I have no idea why hal's
+                 *       poller don't cause the kernel to revalidate the (missing) media
+                 */
+                touch_str = g_strdup_printf ("touch %s", device->priv->info.device_file);
+                g_spawn_command_line_async (touch_str, NULL);
+                g_free (touch_str);
+        } else {
+                g_warning ("force unmount failed: %s", stderr);
+        }
+}
+
+static void
+force_unmount (DevkitDisksDevice *device, gboolean remove_dir_on_unmount)
+{
+        int n;
+        char *argv[16];
+        GError *error;
+
+        n = 0;
+        argv[n++] = "umount";
+        /* on Linux, we only have lazy unmount for now */
+        argv[n++] = "-l";
+        argv[n++] = device->priv->info.device_mount_path;
+        argv[n++] = NULL;
+
+        error = NULL;
+        if (!job_new (NULL,
+                      "ForceUnmount",
+                      FALSE,
+                      device,
+                      NULL,
+                      argv,
+                      NULL,
+                      force_unmount_completed_cb,
+                      g_strdup (device->priv->info.device_mount_path),
+                      g_free)) {
+                g_warning ("Couldn't spawn unmount for force unmounting: %s", error->message);
+                g_error_free (error);
+        }
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
index 198fbf3..36bd74c 100644 (file)
@@ -90,16 +90,19 @@ GList *devkit_disks_enumerate_native_paths (void);
 
 /* local methods */
 
-const char        *devkit_disks_device_local_get_object_path (DevkitDisksDevice *device);
-const char        *devkit_disks_device_local_get_native_path (DevkitDisksDevice *device);
+const char        *devkit_disks_device_local_get_object_path     (DevkitDisksDevice *device);
+const char        *devkit_disks_device_local_get_native_path     (DevkitDisksDevice *device);
 
-const char        *devkit_disks_device_local_get_device_file (DevkitDisksDevice *device);
-const char        *devkit_disks_device_local_get_mount_path (DevkitDisksDevice *device);
+const char        *devkit_disks_device_local_get_device_file     (DevkitDisksDevice *device);
+const char        *devkit_disks_device_local_get_mount_path      (DevkitDisksDevice *device);
 
-void               devkit_disks_device_local_set_mounted (DevkitDisksDevice *device, const char *mount_path);
-void               devkit_disks_device_local_set_unmounted (DevkitDisksDevice *device);
+void               devkit_disks_device_local_set_mounted         (DevkitDisksDevice *device,
+                                                                  const char        *mount_path,
+                                                                  gboolean           emit_changed_signal);
+void               devkit_disks_device_local_set_unmounted       (DevkitDisksDevice *device,
+                                                                  gboolean           emit_changed_signal);
 
-gboolean           devkit_disks_device_local_is_busy (DevkitDisksDevice *device);
+gboolean           devkit_disks_device_local_is_busy             (DevkitDisksDevice *device);
 gboolean           devkit_disks_device_local_partitions_are_busy (DevkitDisksDevice *device);
 
 /* exported methods */
index 4fdb035..4d3814d 100644 (file)
@@ -191,9 +191,6 @@ mounts_file_remove (DevkitDisksDevice *device, const char *mount_path)
         device_file_escaped = NULL;
         mount_path_escaped = NULL;
 
-        g_return_if_fail (!device->priv->info.device_is_mounted);
-        g_return_if_fail (device->priv->info.device_mount_path == NULL);
-
         device_file_escaped = g_uri_escape_string (device->priv->info.device_file, NULL, TRUE);
         mount_path_escaped = g_uri_escape_string (mount_path, NULL, TRUE);