gvfs/gdu-0011-Bug-576083-pre-unmount-signals-not-being-trigger.patch
2009-04-15 19:24:55 +00:00

327 lines
10 KiB
Diff

From 8d9fc8a777fb1b00ae371e276a14416a0987f4af Mon Sep 17 00:00:00 2001
From: David Zeuthen <davidz@redhat.com>
Date: Mon, 13 Apr 2009 14:20:15 -0400
Subject: [PATCH 11/13] =?utf-8?q?Bug=20576083=20=E2=80=93=20pre-unmount=20signals=20not=20being=20triggered?=
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Basically emit GVolumeMonitor::mount-pre-unmount on the volume monitor
and retry unmount operation a couple of times.
---
monitor/gdu/ggdumount.c | 177 +++++++++++++++++++++++++++++++++++++---------
1 files changed, 142 insertions(+), 35 deletions(-)
diff --git a/monitor/gdu/ggdumount.c b/monitor/gdu/ggdumount.c
index e074a20..78088d5 100644
--- a/monitor/gdu/ggdumount.c
+++ b/monitor/gdu/ggdumount.c
@@ -38,6 +38,9 @@
#include "ggdumount.h"
#include "ggduvolume.h"
+#define BUSY_UNMOUNT_NUM_ATTEMPTS 5
+#define BUSY_UNMOUNT_MS_DELAY_BETWEEN_ATTEMPTS 500
+
struct _GGduMount
{
GObject parent;
@@ -451,26 +454,52 @@ g_gdu_mount_can_eject (GMount *_mount)
typedef struct {
GMount *mount;
+
+ gchar **argv;
+ guint num_attempts_left;
+
GAsyncReadyCallback callback;
gpointer user_data;
GCancellable *cancellable;
+
int error_fd;
GIOChannel *error_channel;
guint error_channel_source_id;
GString *error_string;
-} UnmountEjectOp;
+
+} UnmountOp;
+
+static gboolean unmount_attempt (UnmountOp *data);
static void
-eject_unmount_cb (GPid pid, gint status, gpointer user_data)
+unmount_cb (GPid pid, gint status, gpointer user_data)
{
- UnmountEjectOp *data = user_data;
+ UnmountOp *data = user_data;
GSimpleAsyncResult *simple;
+ g_spawn_close_pid (pid);
+
if (WEXITSTATUS (status) != 0)
{
GError *error;
+ gint error_code;
+
+ error_code = G_IO_ERROR_FAILED;
+ /* we may want to add more strstr() checks here depending on what unmount helper is being used etc... */
+ if (data->error_string->str != NULL && strstr (data->error_string->str, "is busy") != NULL)
+ error_code = G_IO_ERROR_BUSY;
+
+ if (error_code == G_IO_ERROR_BUSY && data->num_attempts_left > 0)
+ {
+ g_timeout_add (BUSY_UNMOUNT_MS_DELAY_BETWEEN_ATTEMPTS,
+ (GSourceFunc) unmount_attempt,
+ data);
+ data->num_attempts_left -= 1;
+ goto out;
+ }
+
error = g_error_new_literal (G_IO_ERROR,
- G_IO_ERROR_FAILED,
+ error_code,
data->error_string->str);
simple = g_simple_async_result_new_from_error (G_OBJECT (data->mount),
data->callback,
@@ -493,16 +522,19 @@ eject_unmount_cb (GPid pid, gint status, gpointer user_data)
g_io_channel_unref (data->error_channel);
g_string_free (data->error_string, TRUE);
close (data->error_fd);
- g_spawn_close_pid (pid);
+ g_strfreev (data->argv);
g_free (data);
+
+ out:
+ ;
}
static gboolean
-eject_unmount_read_error (GIOChannel *channel,
- GIOCondition condition,
- gpointer user_data)
+unmount_read_error (GIOChannel *channel,
+ GIOCondition condition,
+ gpointer user_data)
{
- UnmountEjectOp *data = user_data;
+ UnmountOp *data = user_data;
gchar buf[BUFSIZ];
gsize bytes_read;
GError *error;
@@ -532,26 +564,15 @@ read:
return TRUE;
}
-static void
-eject_unmount_do (GMount *mount,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data,
- char **argv)
+static gboolean
+unmount_attempt (UnmountOp *data)
{
- UnmountEjectOp *data;
GPid child_pid;
GError *error;
- data = g_new0 (UnmountEjectOp, 1);
- data->mount = mount;
- data->callback = callback;
- data->user_data = user_data;
- data->cancellable = cancellable;
-
error = NULL;
if (!g_spawn_async_with_pipes (NULL, /* working dir */
- argv,
+ data->argv,
NULL, /* envp */
G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH,
NULL, /* child_setup */
@@ -572,8 +593,8 @@ eject_unmount_do (GMount *mount,
if (error != NULL)
goto handle_error;
- data->error_channel_source_id = g_io_add_watch (data->error_channel, G_IO_IN, eject_unmount_read_error, data);
- g_child_watch_add (child_pid, eject_unmount_cb, data);
+ data->error_channel_source_id = g_io_add_watch (data->error_channel, G_IO_IN, unmount_read_error, data);
+ g_child_watch_add (child_pid, unmount_cb, data);
handle_error:
@@ -596,6 +617,28 @@ handle_error:
g_error_free (error);
g_free (data);
}
+
+ return FALSE;
+}
+
+static void
+unmount_do (GMount *mount,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ char **argv)
+{
+ UnmountOp *data;
+
+ data = g_new0 (UnmountOp, 1);
+ data->mount = mount;
+ data->callback = callback;
+ data->user_data = user_data;
+ data->cancellable = cancellable;
+ data->num_attempts_left = BUSY_UNMOUNT_NUM_ATTEMPTS;
+ data->argv = g_strdupv (argv);
+
+ unmount_attempt (data);
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -619,19 +662,54 @@ luks_lock_cb (GduDevice *device,
g_object_unref (simple);
}
+static gboolean gdu_unmount_attempt (GSimpleAsyncResult *simple);
+
static void
-unmount_cb (GduDevice *device,
- GError *error,
- gpointer user_data)
+gdu_unmount_cb (GduDevice *device,
+ GError *error,
+ gpointer user_data)
{
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
if (error != NULL)
{
+ gint error_code;
+
+ /*g_debug ("domain=%s error_code=%d '%s'", g_quark_to_string (error->domain), error->code, error->message);*/
+
+ error_code = G_IO_ERROR_FAILED;
+ if (error->domain == GDU_ERROR && error->code == GDU_ERROR_BUSY)
+ error_code = G_IO_ERROR_BUSY;
+
/* We could handle PolicyKit integration here but this action is allowed by default
* and this won't be needed when porting to PolicyKit 1.0 anyway
*/
- g_simple_async_result_set_from_error (simple, error);
+
+ if (error_code == G_IO_ERROR_BUSY)
+ {
+ guint num_attempts_left;
+
+ num_attempts_left = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (simple), "num-attempts-left"));
+
+ if (num_attempts_left > 0)
+ {
+ num_attempts_left -= 1;
+ g_object_set_data (G_OBJECT (simple),
+ "num-attempts-left",
+ GUINT_TO_POINTER (num_attempts_left));
+
+ g_timeout_add (BUSY_UNMOUNT_MS_DELAY_BETWEEN_ATTEMPTS,
+ (GSourceFunc) gdu_unmount_attempt,
+ simple);
+ goto out;
+ }
+ }
+
+ g_simple_async_result_set_error (simple,
+ G_IO_ERROR,
+ error_code,
+ "%s",
+ error->message);
g_error_free (error);
g_simple_async_result_complete (simple);
g_object_unref (simple);
@@ -687,6 +765,17 @@ unmount_cb (GduDevice *device,
;
}
+static gboolean
+gdu_unmount_attempt (GSimpleAsyncResult *simple)
+{
+ GduDevice *device;
+
+ /* TODO: honor flags */
+ device = g_object_get_data (G_OBJECT (simple), "gdu-device");
+ gdu_device_op_filesystem_unmount (device, gdu_unmount_cb, simple);
+ return FALSE;
+}
+
static void
g_gdu_mount_unmount (GMount *_mount,
GMountUnmountFlags flags,
@@ -698,6 +787,21 @@ g_gdu_mount_unmount (GMount *_mount,
GSimpleAsyncResult *simple;
GduPresentable *gdu_volume;
+ /* emit the ::mount-pre-unmount signal */
+ g_signal_emit_by_name (mount->volume_monitor, "mount-pre-unmount", mount);
+
+ /* If we end up with G_IO_ERROR_BUSY, try again BUSY_UNMOUNT_NUM_ATTEMPTS times
+ * waiting BUSY_UNMOUNT_MS_DELAY_BETWEEN_ATTEMPTS milliseconds between each
+ * attempt.
+ *
+ * This is to give other processes recieving the ::mount-pre-unmount signal some
+ * time to close file descriptors.
+ *
+ * TODO: Unfortunately this code is a bit messy because we take two different
+ * codepaths depending on whether we use GDU or the native unmount command.
+ * It would be good to clean this up.
+ */
+
gdu_volume = NULL;
if (mount->volume != NULL)
gdu_volume = g_gdu_volume_get_presentable_with_cleartext (mount->volume);
@@ -713,7 +817,7 @@ g_gdu_mount_unmount (GMount *_mount,
else
argv[1] = mount->device_file;
- eject_unmount_do (_mount, cancellable, callback, user_data, argv);
+ unmount_do (_mount, cancellable, callback, user_data, argv);
}
else if (gdu_volume != NULL)
{
@@ -722,6 +826,10 @@ g_gdu_mount_unmount (GMount *_mount,
user_data,
NULL);
+ g_object_set_data (G_OBJECT (simple),
+ "num-attempts-left",
+ GUINT_TO_POINTER (BUSY_UNMOUNT_NUM_ATTEMPTS));
+
if (mount->is_burn_mount)
{
/* burn mounts are really never mounted... */
@@ -731,12 +839,9 @@ g_gdu_mount_unmount (GMount *_mount,
else
{
GduDevice *device;
-
- /* TODO: honor flags */
-
device = gdu_presentable_get_device (gdu_volume);
- gdu_device_op_filesystem_unmount (device, unmount_cb, simple);
- g_object_unref (device);
+ g_object_set_data_full (G_OBJECT (simple), "gdu-device", device, g_object_unref);
+ gdu_unmount_attempt (simple);
}
}
else
@@ -760,6 +865,8 @@ g_gdu_mount_unmount_finish (GMount *mount,
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
}
+/* ---------------------------------------------------------------------------------------------------- */
+
typedef struct {
GObject *object;
GAsyncReadyCallback callback;
--
1.6.2.2