diff options
author | David Zeuthen <davidz@redhat.com> | 2009-06-30 23:19:01 -0400 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2009-07-08 13:53:04 +0200 |
commit | 8af5a5581e11d9c07e8ba3a17eb2cc93b5c70631 (patch) | |
tree | 47206e3fdc7532a97deacb836ec5c79042661421 /daemon | |
parent | c33e26d3914e0659d4a317fda7fe9acbb6269dc9 (diff) | |
download | gvfs-8af5a5581e11d9c07e8ba3a17eb2cc93b5c70631.tar.gz |
Bug 587484 – Interaction when unmounting mounts and misc fixes
- Port everything to use _with_operation() variants of unmount/eject
methods
- Add support for g_file_poll_mountable()
- new job class: GVfsJobPollMountable
- Pass mount operation for unmount/eject ops on GDaemonFile and
GDaemonMount
- receive in the appropriate GVfsJob classes
- also pass unmount flags where it was missing
- port all backends to use this
- Teach GMountSource and GMountOperationDBus about the new
GMountOperation::show-processes signal
- also provide new API
- g_mount_source_is_dummy() - e.g. when the client didn't
passed NULL for the GMountOperation
- g_mount_source_abort() - to send the ::abort signal to the
client-side GMountOperation
- make the client-side of GMountSource return ::reply with
NOT_HANDLED when we do an abort
- Refactor the mount operation handling in GProxyVolumeMonitor
- Pass mount operation for unmount/ejects in GProxyVolumeMonitor
- Pass the process id of the actual reader/writer in OpenForRead
and OpenForWrite daemon methods
- add some private API for making the FUSE client set the
pid of the POSIX client (otherwise it looks like the FUSE
client is blocking) and pass the right pid.
This is because the FUSE client is basically impersonating the
POSIX processes.
- Make the process id mentioned above available in appropriate
GVfsJob classes
- GVfsJobOpenForRead
- GVfsJobOpenForWrite
- GVfsChannel
- Provide API to get a list of all blocking clients, e.g. an array
of GPid
- g_vfs_daemon_get_blocking_processes()
- Provide convenience API to easily doing the right thing on unmount;
e.g. interact with the user about blocking processes - see the gphoto2
backend for example usage
- g_vfs_backend_has_blocking_processes()
- g_vfs_backend_unmount_with_operation() and
g_vfs_backend_unmount_with_operation_finish()
- Only the gphoto2 backend supports ::show-processes right now. Support
for other backends will be added shortly.
- Implement support for ::show-processes in the GDU volume monitor
- right now we don't support "Unmount Anyway" since it requires
ABI changes in libgdu.so - this will be changed as soon as there's
a new gnome-disk-utility release
Diffstat (limited to 'daemon')
30 files changed, 910 insertions, 85 deletions
diff --git a/daemon/Makefile.am b/daemon/Makefile.am index ae247712..1c0d7256 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -126,6 +126,7 @@ libdaemon_la_SOURCES = \ gvfsjobunmountmountable.c gvfsjobunmountmountable.h \ gvfsjobstartmountable.c gvfsjobstartmountable.h \ gvfsjobstopmountable.c gvfsjobstopmountable.h \ + gvfsjobpollmountable.c gvfsjobpollmountable.h \ gvfsjobopenforread.c gvfsjobopenforread.h \ gvfsjobopeniconforread.c gvfsjobopeniconforread.h \ gvfsjobread.c gvfsjobread.h \ diff --git a/daemon/gvfsbackend.c b/daemon/gvfsbackend.c index 3be3cdbf..3bc877e5 100644 --- a/daemon/gvfsbackend.c +++ b/daemon/gvfsbackend.c @@ -48,6 +48,7 @@ #include <gvfsjobunmountmountable.h> #include <gvfsjobstartmountable.h> #include <gvfsjobstopmountable.h> +#include <gvfsjobpollmountable.h> #include <gvfsjobmakedirectory.h> #include <gvfsjobmakesymlink.h> #include <gvfsjobcreatemonitor.h> @@ -543,6 +544,10 @@ backend_dbus_handler (DBusConnection *connection, job = g_vfs_job_stop_mountable_new (connection, message, backend); else if (dbus_message_is_method_call (message, G_VFS_DBUS_MOUNT_INTERFACE, + G_VFS_DBUS_MOUNT_OP_POLL_MOUNTABLE)) + job = g_vfs_job_poll_mountable_new (connection, message, backend); + else if (dbus_message_is_method_call (message, + G_VFS_DBUS_MOUNT_INTERFACE, G_VFS_DBUS_MOUNT_OP_SET_DISPLAY_NAME)) job = g_vfs_job_set_display_name_new (connection, message, backend); else if (dbus_message_is_method_call (message, @@ -693,3 +698,248 @@ g_vfs_backend_unregister_mount (GVfsBackend *backend, callback, user_data); dbus_message_unref (message); } + +/* ------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GVfsBackend *backend; + GMountSource *mount_source; + + gboolean ret; + gboolean aborted; + gint choice; + + const gchar *message; + const gchar *choices[3]; + + gboolean no_more_processes; + + GAsyncReadyCallback callback; + gpointer user_data; + + guint timeout_id; +} UnmountWithOpData; + +static void +complete_unmount_with_op (UnmountWithOpData *data) +{ + gboolean ret; + GSimpleAsyncResult *simple; + + g_source_remove (data->timeout_id); + + ret = TRUE; + + if (data->no_more_processes) + { + /* do nothing, e.g. return TRUE to signal we should unmount */ + } + else + { + if (data->aborted || data->choice == 1) + { + ret = FALSE; + } + } + + simple = g_simple_async_result_new (G_OBJECT (data->backend), + data->callback, + data->user_data, + NULL); + g_simple_async_result_set_op_res_gboolean (simple, ret); + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static void +on_show_processes_reply (GMountSource *mount_source, + GAsyncResult *res, + gpointer user_data) +{ + UnmountWithOpData *data = user_data; + + /* Do nothing if we've handled this already */ + if (data->no_more_processes) + return; + + data->ret = g_mount_source_show_processes_finish (mount_source, + res, + &data->aborted, + &data->choice); + + complete_unmount_with_op (data); +} + +static gboolean +on_update_processes_timeout (gpointer user_data) +{ + UnmountWithOpData *data = user_data; + GArray *processes; + + processes = g_vfs_daemon_get_blocking_processes (g_vfs_backend_get_daemon (data->backend)); + if (processes->len == 0) + { + /* no more processes, abort mount op */ + g_mount_source_abort (data->mount_source); + data->no_more_processes = TRUE; + complete_unmount_with_op (data); + } + else + { + /* ignore reply */ + g_mount_source_show_processes_async (data->mount_source, + data->message, + processes, + data->choices, + g_strv_length ((gchar **) data->choices), + NULL, + NULL); + } + + g_array_unref (processes); + + /* keep timeout around */ + return TRUE; +} + +static void +unmount_with_op_data_free (UnmountWithOpData *data) +{ + g_free (data); +} + + +/** + * g_vfs_backend_unmount_with_operation_finish: + * @backend: A #GVfsBackend. + * @res: A #GAsyncResult obtained from the @callback function passed + * to g_vfs_backend_unmount_with_operation(). + * + * Gets the result of the operation started by + * gvfs_backend_unmount_with_operation_sync(). + * + * Returns: %TRUE if the backend should be unmounted (either no blocking + * processes or the user decided to unmount anyway), %FALSE if + * no action should be taken. + */ +gboolean +g_vfs_backend_unmount_with_operation_finish (GVfsBackend *backend, + GAsyncResult *res) +{ + gboolean ret; + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + + if (g_simple_async_result_propagate_error (simple, NULL)) + { + ret = FALSE; + } + else + { + ret = g_simple_async_result_get_op_res_gboolean (simple); + } + + return ret; +} + +/** + * gvfs_backend_unmount_with_operation: + * @backend: A #GVfsBackend. + * @callback: A #GAsyncReadyCallback. + * @user_data: User data to pass to @callback. + * + * Utility function to checks if there are pending operations on + * @backend preventing unmount. If not, then @callback is invoked + * immediately. + * + * Otherwise, a dialog will be shown (using @mount_source) to interact + * with the user about blocking processes (e.g. using the + * #GMountOperation::show-processes signal). The list of blocking + * processes is continuously updated. + * + * Once the user has decided (or if it's not possible to interact with + * the user), @callback will be invoked. You can then call + * g_vfs_backend_unmount_with_operation_finish() to get the result + * of the operation. + */ +void +g_vfs_backend_unmount_with_operation (GVfsBackend *backend, + GMountSource *mount_source, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GArray *processes; + UnmountWithOpData *data; + + g_return_if_fail (G_VFS_IS_BACKEND (backend)); + g_return_if_fail (G_IS_MOUNT_SOURCE (mount_source)); + g_return_if_fail (callback != NULL); + + processes = g_vfs_daemon_get_blocking_processes (g_vfs_backend_get_daemon (backend)); + /* if no processes are blocking, complete immediately */ + if (processes->len == 0) + { + GSimpleAsyncResult *simple; + simple = g_simple_async_result_new (G_OBJECT (backend), + callback, + user_data, + NULL); + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete (simple); + g_object_unref (simple); + goto out; + } + + data = g_new0 (UnmountWithOpData, 1); + data->backend = backend; + data->mount_source = mount_source; + data->callback = callback; + data->user_data = user_data; + + data->choices[0] = _("Unmount Anyway"); + data->choices[1] = _("Cancel"); + data->choices[2] = NULL; + data->message = _("Volume is busy\n" + "One or more applications are keeping the volume busy."); + + /* free data when the mount source goes away */ + g_object_set_data_full (G_OBJECT (mount_source), + "unmount-op-data", + data, + (GDestroyNotify) unmount_with_op_data_free); + + /* show processes */ + g_mount_source_show_processes_async (mount_source, + data->message, + processes, + data->choices, + g_strv_length ((gchar **) data->choices), + (GAsyncReadyCallback) on_show_processes_reply, + data); + + /* update these processes every two secs */ + data->timeout_id = g_timeout_add_seconds (2, + on_update_processes_timeout, + data); + + out: + g_array_unref (processes); + +} + +gboolean +g_vfs_backend_has_blocking_processes (GVfsBackend *backend) +{ + gboolean ret; + GArray *processes; + + ret = FALSE; + processes = g_vfs_daemon_get_blocking_processes (g_vfs_backend_get_daemon (backend)); + if (processes->len > 0) + ret = TRUE; + + g_array_unref (processes); + + return ret; +} + diff --git a/daemon/gvfsbackend.h b/daemon/gvfsbackend.h index ee68bd86..137001e2 100644 --- a/daemon/gvfsbackend.h +++ b/daemon/gvfsbackend.h @@ -49,6 +49,7 @@ typedef struct _GVfsJobMountMountable GVfsJobMountMountable; typedef struct _GVfsJobUnmountMountable GVfsJobUnmountMountable; typedef struct _GVfsJobStartMountable GVfsJobStartMountable; typedef struct _GVfsJobStopMountable GVfsJobStopMountable; +typedef struct _GVfsJobPollMountable GVfsJobPollMountable; typedef struct _GVfsJobOpenForRead GVfsJobOpenForRead; typedef struct _GVfsJobOpenIconForRead GVfsJobOpenIconForRead; typedef struct _GVfsJobSeekRead GVfsJobSeekRead; @@ -100,9 +101,13 @@ struct _GVfsBackendClass */ void (*unmount) (GVfsBackend *backend, - GVfsJobUnmount *job); + GVfsJobUnmount *job, + GMountUnmountFlags flags, + GMountSource *mount_source); gboolean (*try_unmount) (GVfsBackend *backend, - GVfsJobUnmount *job); + GVfsJobUnmount *job, + GMountUnmountFlags flags, + GMountSource *mount_source); void (*mount) (GVfsBackend *backend, GVfsJobMount *job, GMountSpec *mount_spec, @@ -124,19 +129,23 @@ struct _GVfsBackendClass void (*unmount_mountable) (GVfsBackend *backend, GVfsJobUnmountMountable *job, const char *filename, - GMountUnmountFlags flags); + GMountUnmountFlags flags, + GMountSource *mount_source); gboolean (*try_unmount_mountable)(GVfsBackend *backend, GVfsJobUnmountMountable *job, const char *filename, - GMountUnmountFlags flags); + GMountUnmountFlags flags, + GMountSource *mount_source); void (*eject_mountable) (GVfsBackend *backend, GVfsJobUnmountMountable *job, const char *filename, - GMountUnmountFlags flags); + GMountUnmountFlags flags, + GMountSource *mount_source); gboolean (*try_eject_mountable)(GVfsBackend *backend, GVfsJobUnmountMountable *job, const char *filename, - GMountUnmountFlags flags); + GMountUnmountFlags flags, + GMountSource *mount_source); void (*open_for_read) (GVfsBackend *backend, GVfsJobOpenForRead *job, const char *filename); @@ -429,11 +438,19 @@ struct _GVfsBackendClass void (*stop_mountable) (GVfsBackend *backend, GVfsJobStopMountable *job, const char *filename, - GMountUnmountFlags flags); + GMountUnmountFlags flags, + GMountSource *mount_source); gboolean (*try_stop_mountable) (GVfsBackend *backend, GVfsJobStopMountable *job, const char *filename, - GMountUnmountFlags flags); + GMountUnmountFlags flags, + GMountSource *mount_source); + void (*poll_mountable) (GVfsBackend *backend, + GVfsJobPollMountable *job, + const char *filename); + gboolean (*try_poll_mountable) (GVfsBackend *backend, + GVfsJobPollMountable *job, + const char *filename); }; GType g_vfs_backend_get_type (void) G_GNUC_CONST; @@ -478,6 +495,16 @@ void g_vfs_backend_add_auto_info (GVfsBackend GFileInfo *info, const char *uri); +gboolean g_vfs_backend_has_blocking_processes (GVfsBackend *backend); + +gboolean g_vfs_backend_unmount_with_operation_finish (GVfsBackend *backend, + GAsyncResult *res); + +void g_vfs_backend_unmount_with_operation (GVfsBackend *backend, + GMountSource *mount_source, + GAsyncReadyCallback callback, + gpointer user_data); + G_END_DECLS #endif /* __G_VFS_BACKEND_H__ */ diff --git a/daemon/gvfsbackendarchive.c b/daemon/gvfsbackendarchive.c index bbca3cad..17f3fc48 100644 --- a/daemon/gvfsbackendarchive.c +++ b/daemon/gvfsbackendarchive.c @@ -583,7 +583,9 @@ do_mount (GVfsBackend *backend, static void do_unmount (GVfsBackend *backend, - GVfsJobUnmount *job) + GVfsJobUnmount *job, + GMountUnmountFlags flags, + GMountSource *mount_source) { GVfsBackendArchive *ba = G_VFS_BACKEND_ARCHIVE (backend); diff --git a/daemon/gvfsbackendcdda.c b/daemon/gvfsbackendcdda.c index 9b30753d..e1871c6a 100644 --- a/daemon/gvfsbackendcdda.c +++ b/daemon/gvfsbackendcdda.c @@ -428,7 +428,9 @@ try_mount (GVfsBackend *backend, static void do_unmount (GVfsBackend *backend, - GVfsJobUnmount *job) + GVfsJobUnmount *job, + GMountUnmountFlags flags, + GMountSource *mount_source) { GError *error; GVfsBackendCdda *cdda_backend = G_VFS_BACKEND_CDDA (backend); diff --git a/daemon/gvfsbackendcomputer.c b/daemon/gvfsbackendcomputer.c index 9783df6c..4f4b9cad 100644 --- a/daemon/gvfsbackendcomputer.c +++ b/daemon/gvfsbackendcomputer.c @@ -62,11 +62,15 @@ typedef struct { GIcon *icon; GFile *root; int prio; + gchar *unix_device_file; gboolean can_mount; gboolean can_unmount; gboolean can_eject; gboolean can_start; + gboolean can_start_degraded; gboolean can_stop; + gboolean can_poll_for_media; + gboolean is_media_check_automatic; GDriveStartStopType start_stop_type; GDrive *drive; @@ -96,6 +100,7 @@ G_DEFINE_TYPE (GVfsBackendComputer, g_vfs_backend_computer, G_VFS_TYPE_BACKEND) static void computer_file_free (ComputerFile *file) { + g_free (file->unix_device_file); g_free (file->filename); g_free (file->display_name); if (file->icon) @@ -137,7 +142,10 @@ computer_file_equal (ComputerFile *a, a->can_unmount != b->can_unmount || a->can_eject != b->can_eject || a->can_start != b->can_start || + a->can_start_degraded != b->can_start_degraded || a->can_stop != b->can_stop || + a->can_poll_for_media != b->can_poll_for_media || + a->is_media_check_automatic != b->is_media_check_automatic || a->start_stop_type != b->start_stop_type) return FALSE; @@ -427,6 +435,9 @@ recompute_files (GVfsBackendComputer *backend) { display_name = g_mount_get_name (file->mount); } + if (file->volume != NULL) + file->unix_device_file = g_volume_get_identifier (file->volume, + G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); file->icon = g_mount_get_icon (file->mount); file->display_name = display_name; file->root = g_mount_get_root (file->mount); @@ -447,6 +458,8 @@ recompute_files (GVfsBackendComputer *backend) { display_name = g_volume_get_name (file->volume); } + file->unix_device_file = g_volume_get_identifier (file->volume, + G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); file->icon = g_volume_get_icon (file->volume); file->display_name = display_name; file->can_mount = g_volume_can_mount (file->volume); @@ -455,6 +468,8 @@ recompute_files (GVfsBackendComputer *backend) } else /* drive */ { + file->unix_device_file = g_drive_get_identifier (file->drive, + G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); file->icon = g_drive_get_icon (file->drive); file->display_name = g_drive_get_name (file->drive); file->can_eject = g_drive_can_eject (file->drive); @@ -464,7 +479,10 @@ recompute_files (GVfsBackendComputer *backend) if (file->drive) { file->can_start = g_drive_can_start (file->drive); + file->can_start_degraded = g_drive_can_start_degraded (file->drive); file->can_stop = g_drive_can_stop (file->drive); + file->can_poll_for_media = g_drive_can_poll_for_media (file->drive); + file->is_media_check_automatic = g_drive_is_media_check_automatic (file->drive); file->start_stop_type = g_drive_get_start_stop_type (file->drive); if (file->can_start) file->can_mount = FALSE; @@ -657,11 +675,16 @@ file_info_from_file (ComputerFile *file, g_file_info_set_sort_order (info, file->prio); g_file_info_set_file_type (info, G_FILE_TYPE_MOUNTABLE); + if (file->unix_device_file != NULL) + g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE_FILE, file->unix_device_file); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, file->can_mount); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT, file->can_unmount); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT, file->can_eject); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START, file->can_start); + g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START_DEGRADED, file->can_start_degraded); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP, file->can_stop); + g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_POLL, file->can_poll_for_media); + g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_IS_MEDIA_CHECK_AUTOMATIC, file->is_media_check_automatic); g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_MOUNTABLE_START_STOP_TYPE, file->start_stop_type); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE); @@ -984,7 +1007,7 @@ unmount_mount_cb (GObject *source_object, mount = G_MOUNT (source_object); error = NULL; - if (g_mount_unmount_finish (mount, res, &error)) + if (g_mount_unmount_with_operation_finish (mount, res, &error)) g_vfs_job_succeeded (G_VFS_JOB (job)); else { @@ -998,9 +1021,11 @@ static gboolean try_unmount_mountable (GVfsBackend *backend, GVfsJobUnmountMountable *job, const char *filename, - GMountUnmountFlags flags) + GMountUnmountFlags flags, + GMountSource *mount_source) { ComputerFile *file; + GMountOperation *mount_op; file = lookup (G_VFS_BACKEND_COMPUTER (backend), G_VFS_JOB (job), filename); @@ -1013,11 +1038,15 @@ try_unmount_mountable (GVfsBackend *backend, { if (file->mount) { - g_mount_unmount (file->mount, - flags, - G_VFS_JOB (job)->cancellable, - unmount_mount_cb, - job); + mount_op = g_mount_source_get_operation (mount_source); + /* free mount_op when job is completed */ + g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-mount-op", mount_op, g_object_unref); + g_mount_unmount_with_operation (file->mount, + flags, + mount_op, + G_VFS_JOB (job)->cancellable, + unmount_mount_cb, + job); } else { @@ -1042,7 +1071,7 @@ eject_mount_cb (GObject *source_object, mount = G_MOUNT (source_object); error = NULL; - if (g_mount_eject_finish (mount, res, &error)) + if (g_mount_eject_with_operation_finish (mount, res, &error)) g_vfs_job_succeeded (G_VFS_JOB (job)); else { @@ -1063,7 +1092,7 @@ eject_volume_cb (GObject *source_object, volume = G_VOLUME (source_object); error = NULL; - if (g_volume_eject_finish (volume, res, &error)) + if (g_volume_eject_with_operation_finish (volume, res, &error)) g_vfs_job_succeeded (G_VFS_JOB (job)); else { @@ -1085,7 +1114,7 @@ eject_drive_cb (GObject *source_object, drive = G_DRIVE (source_object); error = NULL; - if (g_drive_eject_finish (drive, res, &error)) + if (g_drive_eject_with_operation_finish (drive, res, &error)) g_vfs_job_succeeded (G_VFS_JOB (job)); else { @@ -1098,9 +1127,11 @@ static gboolean try_eject_mountable (GVfsBackend *backend, GVfsJobUnmountMountable *job, const char *filename, - GMountUnmountFlags flags) + GMountUnmountFlags flags, + GMountSource *mount_source) { ComputerFile *file; + GMountOperation *mount_op; file = lookup (G_VFS_BACKEND_COMPUTER (backend), G_VFS_JOB (job), filename); @@ -1113,27 +1144,39 @@ try_eject_mountable (GVfsBackend *backend, { if (file->mount) { - g_mount_eject (file->mount, - flags, - G_VFS_JOB (job)->cancellable, - eject_mount_cb, - job); + mount_op = g_mount_source_get_operation (mount_source); + /* free mount_op when job is completed */ + g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-mount-op", mount_op, g_object_unref); + g_mount_eject_with_operation (file->mount, + flags, + mount_op, + G_VFS_JOB (job)->cancellable, + eject_mount_cb, + job); } else if (file->volume) { - g_volume_eject (file->volume, - flags, - G_VFS_JOB (job)->cancellable, - eject_volume_cb, - job); + mount_op = g_mount_source_get_operation (mount_source); + /* free mount_op when job is completed */ + g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-mount-op", mount_op, g_object_unref); + g_volume_eject_with_operation (file->volume, + flags, + mount_op, + G_VFS_JOB (job)->cancellable, + eject_volume_cb, + job); } else if (file->drive) { - g_drive_eject (file->drive, - flags, - G_VFS_JOB (job)->cancellable, - eject_drive_cb, - job); + mount_op = g_mount_source_get_operation (mount_source); + /* free mount_op when job is completed */ + g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-mount-op", mount_op, g_object_unref); + g_drive_eject_with_operation (file->drive, + flags, + mount_op, + G_VFS_JOB (job)->cancellable, + eject_drive_cb, + job); } else { @@ -1177,7 +1220,7 @@ try_start_mountable (GVfsBackend *backend, GMountSource *mount_source) { ComputerFile *file; - GMountOperation *start_op; + GMountOperation *mount_op; file = lookup (G_VFS_BACKEND_COMPUTER (backend), G_VFS_JOB (job), filename); @@ -1192,12 +1235,12 @@ try_start_mountable (GVfsBackend *backend, { if (file->drive != NULL) { - start_op = g_mount_source_get_operation (mount_source); - /* free start_op when job is completed */ - g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-start-op", start_op, g_object_unref); + mount_op = g_mount_source_get_operation (mount_source); + /* free mount_op when job is completed */ + g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-start-op", mount_op, g_object_unref); g_drive_start (file->drive, 0, - start_op, + mount_op, G_VFS_JOB (job)->cancellable, drive_start_cb, job); @@ -1246,9 +1289,11 @@ static gboolean try_stop_mountable (GVfsBackend *backend, GVfsJobStopMountable *job, const char *filename, - GMountUnmountFlags flags) + GMountUnmountFlags flags, + GMountSource *mount_source) { ComputerFile *file; + GMountOperation *mount_op; file = lookup (G_VFS_BACKEND_COMPUTER (backend), G_VFS_JOB (job), filename); @@ -1263,8 +1308,12 @@ try_stop_mountable (GVfsBackend *backend, { if (file->drive != NULL) { + mount_op = g_mount_source_get_operation (mount_source); + /* free mount_op when job is completed */ + g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-start-op", mount_op, g_object_unref); g_drive_stop (file->drive, flags, + mount_op, G_VFS_JOB (job)->cancellable, drive_stop_cb, job); @@ -1286,6 +1335,70 @@ try_stop_mountable (GVfsBackend *backend, } static void +drive_poll_for_media_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GVfsJobPollMountable *job = user_data; + GError *error; + GDrive *drive; + + drive = G_DRIVE (source_object); + + error = NULL; + if (g_drive_poll_for_media_finish (drive, res, &error)) + { + g_vfs_job_succeeded (G_VFS_JOB (job)); + } + else + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + } +} + +static gboolean +try_poll_mountable (GVfsBackend *backend, + GVfsJobPollMountable *job, + const char *filename) +{ + ComputerFile *file; + + file = lookup (G_VFS_BACKEND_COMPUTER (backend), + G_VFS_JOB (job), filename); + + if (file == &root) + { + g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, + G_IO_ERROR_NOT_MOUNTABLE_FILE, + _("Not a mountable file")); + } + else if (file != NULL) + { + if (file->drive != NULL) + { + g_drive_poll_for_media (file->drive, + G_VFS_JOB (job)->cancellable, + drive_poll_for_media_cb, + job); + } + else + { + g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + _("Can't poll file")); + } + } + else + { + g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + _("Can't poll file")); + } + return TRUE; +} + +static void g_vfs_backend_computer_class_init (GVfsBackendComputerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); @@ -1303,4 +1416,5 @@ g_vfs_backend_computer_class_init (GVfsBackendComputerClass *klass) backend_class->try_eject_mountable = try_eject_mountable; backend_class->try_start_mountable = try_start_mountable; backend_class->try_stop_mountable = try_stop_mountable; + backend_class->try_poll_mountable = try_poll_mountable; } diff --git a/daemon/gvfsbackenddav.c b/daemon/gvfsbackenddav.c index ef3b55d4..95dc4284 100644 --- a/daemon/gvfsbackenddav.c +++ b/daemon/gvfsbackenddav.c @@ -2306,7 +2306,9 @@ do_set_display_name (GVfsBackend *backend, static gboolean try_unmount (GVfsBackend *backend, - GVfsJobUnmount *job) + GVfsJobUnmount *job, + GMountUnmountFlags flags, + GMountSource *mount_source) { _exit (0); } diff --git a/daemon/gvfsbackendftp.c b/daemon/gvfsbackendftp.c index aef56875..d76e7ad0 100644 --- a/daemon/gvfsbackendftp.c +++ b/daemon/gvfsbackendftp.c @@ -528,7 +528,9 @@ try_mount (GVfsBackend *backend, static void do_unmount (GVfsBackend * backend, - GVfsJobUnmount *job) + GVfsJobUnmount *job, + GMountUnmountFlags flags, + GMountSource *mount_source) { GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend); GVfsFtpConnection *conn; diff --git a/daemon/gvfsbackendgphoto2.c b/daemon/gvfsbackendgphoto2.c index d6d6520a..9ef15a69 100644 --- a/daemon/gvfsbackendgphoto2.c +++ b/daemon/gvfsbackendgphoto2.c @@ -51,12 +51,13 @@ #include "gvfsjobwrite.h" #include "gvfsjobclosewrite.h" #include "gvfsjobcreatemonitor.h" +#include "gvfsjobunmount.h" #include "gvfsmonitor.h" #include "gvfsjobseekwrite.h" #include "gvfsicon.h" /* showing debug traces */ -#if 0 +#if 1 #define DEBUG_SHOW_TRACES 1 #endif @@ -175,9 +176,6 @@ struct _GVfsBackendGphoto2 /* see comment in ensure_ignore_prefix() */ char *ignore_prefix; - /* list of open files */ - int num_open_files_for_reading; - DBusConnection *dbus_connection; LibHalContext *hal_ctx; char *hal_udi; @@ -220,6 +218,9 @@ struct _GVfsBackendGphoto2 GList *dir_monitor_proxies; GList *file_monitor_proxies; + /* list of open read handles (only used on the IO thread) */ + GList *open_read_handles; + /* list of open write handles (only used on the IO thread) */ GList *open_write_handles; }; @@ -1630,31 +1631,76 @@ try_mount (GVfsBackend *backend, /* ------------------------------------------------------------------------------------------------- */ static void -do_unmount (GVfsBackend *backend, - GVfsJobUnmount *job) +unmount_with_op_cb (GVfsBackend *backend, + GAsyncResult *res, + gpointer user_data) { - GError *error; - GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend); - int num_open_files; + GVfsJobUnmount *job = G_VFS_JOB_UNMOUNT (user_data); + gboolean should_unmount; - num_open_files = gphoto2_backend->num_open_files_for_reading + g_list_length (gphoto2_backend->open_write_handles); + DEBUG ("In unmount_with_op_cb"); - if (num_open_files > 0) + should_unmount = g_vfs_backend_unmount_with_operation_finish (backend, + res); + + DEBUG ("should_unmount=%d", should_unmount); + + if (should_unmount) + { + + DEBUG ("unmounted %p", backend); + g_vfs_job_succeeded (G_VFS_JOB (job)); + } + else { - error = g_error_new (G_IO_ERROR, G_IO_ERROR_BUSY, - ngettext("File system is busy: %d open file", - "File system is busy: %d open files", - num_open_files), - num_open_files); + GError *error; + error = g_error_new (G_IO_ERROR, + G_IO_ERROR_FAILED_HANDLED, + _("Filesystem is busy")); g_vfs_job_failed_from_error (G_VFS_JOB (job), error); g_error_free (error); - return; } +} - release_device (gphoto2_backend); - g_vfs_job_succeeded (G_VFS_JOB (job)); +static gboolean +try_unmount (GVfsBackend *backend, + GVfsJobUnmount *job, + GMountUnmountFlags flags, + GMountSource *mount_source) +{ + DEBUG ("In try_unmount, unmount_flags=%d", flags, mount_source); + + if (flags & G_MOUNT_UNMOUNT_FORCE) + { + DEBUG ("forcibly unmounted %p", backend); + g_vfs_job_succeeded (G_VFS_JOB (job)); + } + else if (g_mount_source_is_dummy (mount_source)) + { + if (g_vfs_backend_has_blocking_processes (backend)) + { + GError *error; + error = g_error_new (G_IO_ERROR, + G_IO_ERROR_BUSY, + _("Filesystem is busy")); + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + } + else + { + DEBUG ("unmounted %p", backend); + g_vfs_job_succeeded (G_VFS_JOB (job)); + } + } + else + { + g_vfs_backend_unmount_with_operation (backend, + mount_source, + (GAsyncReadyCallback) unmount_with_op_cb, + job); + } - DEBUG ("unmounted %p", backend); + return TRUE; } /* ------------------------------------------------------------------------------------------------- */ @@ -1742,7 +1788,7 @@ do_open_for_read_real (GVfsBackend *backend, read_handle->data, read_handle->size, read_handle, get_preview); g_mutex_lock (gphoto2_backend->lock); - gphoto2_backend->num_open_files_for_reading++; + gphoto2_backend->open_read_handles = g_list_prepend (gphoto2_backend->open_read_handles, read_handle); g_mutex_unlock (gphoto2_backend->lock); read_handle->cursor = 0; @@ -1889,11 +1935,11 @@ do_close_read (GVfsBackend *backend, DEBUG ("close_read() handle=%p", handle); - free_read_handle (read_handle); - g_mutex_lock (gphoto2_backend->lock); - gphoto2_backend->num_open_files_for_reading--; + gphoto2_backend->open_read_handles = g_list_remove (gphoto2_backend->open_read_handles, read_handle); g_mutex_unlock (gphoto2_backend->lock); + + free_read_handle (read_handle); g_vfs_job_succeeded (G_VFS_JOB (job)); } @@ -3403,7 +3449,7 @@ g_vfs_backend_gphoto2_class_init (GVfsBackendGphoto2Class *klass) backend_class->try_mount = try_mount; backend_class->mount = do_mount; - backend_class->unmount = do_unmount; + backend_class->try_unmount = try_unmount; backend_class->open_icon_for_read = do_open_icon_for_read; backend_class->open_for_read = do_open_for_read; backend_class->try_read = try_read; diff --git a/daemon/gvfsbackendlocaltest.c b/daemon/gvfsbackendlocaltest.c index ac30adcd..d36c8c5d 100644 --- a/daemon/gvfsbackendlocaltest.c +++ b/daemon/gvfsbackendlocaltest.c @@ -253,7 +253,9 @@ do_mount (GVfsBackend *backend, static void -do_unmount (GVfsBackend *backend, GVfsJobUnmount *job) +do_unmount (GVfsBackend *backend, GVfsJobUnmount *job, + GMountUnmountFlags flags, + GMountSource *mount_source) { GVfsBackendLocalTest *op_backend; diff --git a/daemon/gvfschannel.c b/daemon/gvfschannel.c index 0a2b5848..efe2376b 100644 --- a/daemon/gvfschannel.c +++ b/daemon/gvfschannel.c @@ -53,7 +53,8 @@ G_DEFINE_TYPE_WITH_CODE (GVfsChannel, g_vfs_channel, G_TYPE_OBJECT, enum { PROP_0, - PROP_BACKEND + PROP_BACKEND, + PROP_ACTUAL_CONSUMER }; typedef struct @@ -85,6 +86,7 @@ struct _GVfsChannelPrivate GInputStream *command_stream; GOutputStream *reply_stream; int remote_fd; + GPid actual_consumer; GVfsBackendHandle backend_handle; GVfsJob *current_job; @@ -172,6 +174,17 @@ g_vfs_channel_class_init (GVfsChannelClass *klass) G_VFS_TYPE_BACKEND, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB)); + + g_object_class_install_property (gobject_class, + PROP_ACTUAL_CONSUMER, + g_param_spec_int ("actual-consumer", + P_("Actual Consumer"), + P_("The process id of the remote end"), + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB)); } static void @@ -213,6 +226,11 @@ g_vfs_channel_set_property (GObject *object, g_object_unref (channel->priv->backend); channel->priv->backend = G_VFS_BACKEND (g_value_dup_object (value)); break; + + case PROP_ACTUAL_CONSUMER: + channel->priv->actual_consumer = g_value_get_int (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -232,6 +250,9 @@ g_vfs_channel_get_property (GObject *object, case PROP_BACKEND: g_value_set_object (value, channel->priv->backend); break; + case PROP_ACTUAL_CONSUMER: + g_value_set_int (value, channel->priv->actual_consumer); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -718,3 +739,10 @@ g_vfs_channel_get_current_seq_nr (GVfsChannel *channel) { return channel->priv->current_job_seq_nr; } + +GPid +g_vfs_channel_get_actual_consumer (GVfsChannel *channel) +{ + return channel->priv->actual_consumer; +} + diff --git a/daemon/gvfschannel.h b/daemon/gvfschannel.h index 0dc92fce..60b29af5 100644 --- a/daemon/gvfschannel.h +++ b/daemon/gvfschannel.h @@ -83,6 +83,7 @@ void g_vfs_channel_send_reply (GVfsChannel const void *data, gsize data_len); guint32 g_vfs_channel_get_current_seq_nr (GVfsChannel *channel); +GPid g_vfs_channel_get_actual_consumer (GVfsChannel *channel); /* TODO: i/o priority? */ diff --git a/daemon/gvfsdaemon.c b/daemon/gvfsdaemon.c index cf4264eb..8b7d521b 100644 --- a/daemon/gvfsdaemon.c +++ b/daemon/gvfsdaemon.c @@ -40,6 +40,8 @@ #include <gvfsdaemonprotocol.h> #include <gvfsdaemonutils.h> #include <gvfsjobmount.h> +#include <gvfsjobopenforread.h> +#include <gvfsjobopenforwrite.h> #include <gdbusutils.h> enum { @@ -1065,3 +1067,33 @@ g_vfs_daemon_initiate_mount (GVfsDaemon *daemon, job = g_vfs_job_mount_new (mount_spec, mount_source, is_automount, request, backend); g_vfs_daemon_queue_job (daemon, job); } + +/** + * g_vfs_daemon_get_blocking_processes: + * @daemon: A #GVfsDaemon. + * + * Gets all processes that blocks unmounting, e.g. processes with open + * file handles. + * + * Returns: An array of #GPid. Free with g_array_unref(). + */ +GArray * +g_vfs_daemon_get_blocking_processes (GVfsDaemon *daemon) +{ + GArray *processes; + GList *l; + + processes = g_array_new (FALSE, FALSE, sizeof (GPid)); + for (l = daemon->job_sources; l != NULL; l = l->next) + { + if (G_VFS_IS_CHANNEL (l->data)) + { + GPid pid; + pid = g_vfs_channel_get_actual_consumer (G_VFS_CHANNEL (l->data)); + g_array_append_val (processes, pid); + } + } + + return processes; +} + diff --git a/daemon/gvfsdaemon.h b/daemon/gvfsdaemon.h index f40f2166..805b75f6 100644 --- a/daemon/gvfsdaemon.h +++ b/daemon/gvfsdaemon.h @@ -70,6 +70,7 @@ void g_vfs_daemon_initiate_mount (GVfsDaemon *daemon, GMountSource *mount_source, gboolean is_automount, DBusMessage *request); +GArray *g_vfs_daemon_get_blocking_processes (GVfsDaemon *daemon); G_END_DECLS diff --git a/daemon/gvfsjobopenforread.c b/daemon/gvfsjobopenforread.c index 0775db46..a2abbeb7 100644 --- a/daemon/gvfsjobopenforread.c +++ b/daemon/gvfsjobopenforread.c @@ -91,11 +91,13 @@ g_vfs_job_open_for_read_new (DBusConnection *connection, DBusError derror; int path_len; const char *path_data; + guint32 pid; dbus_error_init (&derror); if (!dbus_message_get_args (message, &derror, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &path_data, &path_len, + DBUS_TYPE_UINT32, &pid, 0)) { reply = dbus_message_new_error (message, @@ -114,6 +116,7 @@ g_vfs_job_open_for_read_new (DBusConnection *connection, job->filename = g_strndup (path_data, path_len); job->backend = backend; + job->pid = pid; return G_VFS_JOB (job); } @@ -182,7 +185,8 @@ create_reply (GVfsJob *job, g_assert (open_job->backend_handle != NULL); error = NULL; - channel = g_vfs_read_channel_new (open_job->backend); + channel = g_vfs_read_channel_new (open_job->backend, + open_job->pid); remote_fd = g_vfs_channel_steal_remote_fd (G_VFS_CHANNEL (channel)); if (!dbus_connection_send_fd (connection, @@ -217,3 +221,9 @@ static void finished (GVfsJob *job) { } + +GPid +g_vfs_job_open_for_read_get_pid (GVfsJobOpenForRead *job) +{ + return job->pid; +} diff --git a/daemon/gvfsjobopenforread.h b/daemon/gvfsjobopenforread.h index a15e2fe8..af7cd4a4 100644 --- a/daemon/gvfsjobopenforread.h +++ b/daemon/gvfsjobopenforread.h @@ -48,6 +48,8 @@ struct _GVfsJobOpenForRead GVfsBackendHandle backend_handle; gboolean can_seek; GVfsReadChannel *read_channel; + + GPid pid; }; struct _GVfsJobOpenForReadClass @@ -64,6 +66,7 @@ void g_vfs_job_open_for_read_set_handle (GVfsJobOpenForRead *job, GVfsBackendHandle handle); void g_vfs_job_open_for_read_set_can_seek (GVfsJobOpenForRead *job, gboolean can_seek); +GPid g_vfs_job_open_for_read_get_pid (GVfsJobOpenForRead *job); G_END_DECLS diff --git a/daemon/gvfsjobopenforwrite.c b/daemon/gvfsjobopenforwrite.c index f456f839..48ec026b 100644 --- a/daemon/gvfsjobopenforwrite.c +++ b/daemon/gvfsjobopenforwrite.c @@ -96,6 +96,7 @@ g_vfs_job_open_for_write_new (DBusConnection *connection, dbus_bool_t make_backup; const char *etag; guint32 flags; + guint32 pid; path = NULL; dbus_error_init (&derror); @@ -106,6 +107,7 @@ g_vfs_job_open_for_write_new (DBusConnection *connection, DBUS_TYPE_STRING, &etag, DBUS_TYPE_BOOLEAN, &make_backup, DBUS_TYPE_UINT32, &flags, + DBUS_TYPE_UINT32, &pid, 0)) { reply = dbus_message_new_error (message, @@ -130,6 +132,7 @@ g_vfs_job_open_for_write_new (DBusConnection *connection, job->make_backup = make_backup; job->flags = flags; job->backend = backend; + job->pid = pid; return G_VFS_JOB (job); } @@ -273,7 +276,8 @@ create_reply (GVfsJob *job, g_assert (open_job->backend_handle != NULL); error = NULL; - channel = g_vfs_write_channel_new (open_job->backend); + channel = g_vfs_write_channel_new (open_job->backend, + open_job->pid); remote_fd = g_vfs_channel_steal_remote_fd (G_VFS_CHANNEL (channel)); if (!dbus_connection_send_fd (connection, @@ -310,3 +314,9 @@ static void finished (GVfsJob *job) { } + +GPid +g_vfs_job_open_for_write_get_pid (GVfsJobOpenForWrite *job) +{ + return job->pid; +} diff --git a/daemon/gvfsjobopenforwrite.h b/daemon/gvfsjobopenforwrite.h index 4f6469d1..2cc76554 100644 --- a/daemon/gvfsjobopenforwrite.h +++ b/daemon/gvfsjobopenforwrite.h @@ -61,6 +61,8 @@ struct _GVfsJobOpenForWrite gboolean can_seek; goffset initial_offset; GVfsWriteChannel *write_channel; + + GPid pid; }; struct _GVfsJobOpenForWriteClass @@ -79,6 +81,7 @@ void g_vfs_job_open_for_write_set_can_seek (GVfsJobOpenForWrite *job, gboolean can_seek); void g_vfs_job_open_for_write_set_initial_offset (GVfsJobOpenForWrite *job, goffset initial_offset); +GPid g_vfs_job_open_for_write_get_pid (GVfsJobOpenForWrite *job); G_END_DECLS diff --git a/daemon/gvfsjobpollmountable.c b/daemon/gvfsjobpollmountable.c new file mode 100644 index 00000000..f4c98987 --- /dev/null +++ b/daemon/gvfsjobpollmountable.c @@ -0,0 +1,159 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#include <config.h> + +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <glib.h> +#include <dbus/dbus.h> +#include <glib/gi18n.h> +#include "gvfsjobpollmountable.h" +#include "gdbusutils.h" +#include "gvfsdaemonutils.h" + +G_DEFINE_TYPE (GVfsJobPollMountable, g_vfs_job_poll_mountable, G_VFS_TYPE_JOB_DBUS) + +static void run (GVfsJob *job); +static gboolean try (GVfsJob *job); +static DBusMessage *create_reply (GVfsJob *job, + DBusConnection *connection, + DBusMessage *message); + +static void +g_vfs_job_poll_mountable_finalize (GObject *object) +{ + GVfsJobPollMountable *job; + + job = G_VFS_JOB_POLL_MOUNTABLE (object); + + g_free (job->filename); + + if (G_OBJECT_CLASS (g_vfs_job_poll_mountable_parent_class)->finalize) + (*G_OBJECT_CLASS (g_vfs_job_poll_mountable_parent_class)->finalize) (object); +} + +static void +g_vfs_job_poll_mountable_class_init (GVfsJobPollMountableClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GVfsJobClass *job_class = G_VFS_JOB_CLASS (klass); + GVfsJobDBusClass *job_dbus_class = G_VFS_JOB_DBUS_CLASS (klass); + + gobject_class->finalize = g_vfs_job_poll_mountable_finalize; + job_class->run = run; + job_class->try = try; + job_dbus_class->create_reply = create_reply; +} + +static void +g_vfs_job_poll_mountable_init (GVfsJobPollMountable *job) +{ +} + +GVfsJob * +g_vfs_job_poll_mountable_new (DBusConnection *connection, + DBusMessage *message, + GVfsBackend *backend) +{ + GVfsJobPollMountable *job; + DBusMessage *reply; + DBusMessageIter iter; + DBusError derror; + char *path; + + dbus_error_init (&derror); + dbus_message_iter_init (message, &iter); + + path = NULL; + if (!_g_dbus_message_iter_get_args (&iter, &derror, + G_DBUS_TYPE_CSTRING, &path, + 0)) + { + g_free (path); + reply = dbus_message_new_error (message, + derror.name, + derror.message); + dbus_error_free (&derror); + + dbus_connection_send (connection, reply, NULL); + return NULL; + } + + job = g_object_new (G_VFS_TYPE_JOB_POLL_MOUNTABLE, + "message", message, + "connection", connection, + NULL); + + job->filename = path; + job->backend = backend; + + return G_VFS_JOB (job); +} + +static void +run (GVfsJob *job) +{ + GVfsJobPollMountable *op_job = G_VFS_JOB_POLL_MOUNTABLE (job); + GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend); + + if (class->poll_mountable == NULL) + { + g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Operation not supported by backend")); + return; + } + + class->poll_mountable (op_job->backend, + op_job, + op_job->filename); +} + +static gboolean +try (GVfsJob *job) +{ + GVfsJobPollMountable *op_job = G_VFS_JOB_POLL_MOUNTABLE (job); + GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend); + + if (class->try_poll_mountable == NULL) + return FALSE; + + return class->try_poll_mountable (op_job->backend, + op_job, + op_job->filename); +} + +/* Might be called on an i/o thread */ +static DBusMessage * +create_reply (GVfsJob *job, + DBusConnection *connection, + DBusMessage *message) +{ + DBusMessage *reply; + + reply = dbus_message_new_method_return (message); + + return reply; +} diff --git a/daemon/gvfsjobpollmountable.h b/daemon/gvfsjobpollmountable.h new file mode 100644 index 00000000..4c96d559 --- /dev/null +++ b/daemon/gvfsjobpollmountable.h @@ -0,0 +1,63 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#ifndef __G_VFS_JOB_POLL_MOUNTABLE_H__ +#define __G_VFS_JOB_POLL_MOUNTABLE_H__ + +#include <gio/gio.h> +#include <gvfsjob.h> +#include <gvfsjobdbus.h> +#include <gvfsbackend.h> + +G_BEGIN_DECLS + +#define G_VFS_TYPE_JOB_POLL_MOUNTABLE (g_vfs_job_poll_mountable_get_type ()) +#define G_VFS_JOB_POLL_MOUNTABLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_JOB_POLL_MOUNTABLE, GVfsJobPollMountable)) +#define G_VFS_JOB_POLL_MOUNTABLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_JOB_POLL_MOUNTABLE, GVfsJobPollMountableClass)) +#define G_VFS_IS_JOB_POLL_MOUNTABLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_JOB_POLL_MOUNTABLE)) +#define G_VFS_IS_JOB_POLL_MOUNTABLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_JOB_POLL_MOUNTABLE)) +#define G_VFS_JOB_POLL_MOUNTABLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_JOB_POLL_MOUNTABLE, GVfsJobPollMountableClass)) + +typedef struct _GVfsJobPollMountableClass GVfsJobPollMountableClass; + +struct _GVfsJobPollMountable +{ + GVfsJobDBus parent_instance; + + GVfsBackend *backend; + char *filename; +}; + +struct _GVfsJobPollMountableClass +{ + GVfsJobDBusClass parent_class; +}; + +GType g_vfs_job_poll_mountable_get_type (void) G_GNUC_CONST; + +GVfsJob *g_vfs_job_poll_mountable_new (DBusConnection *connection, + DBusMessage *message, + GVfsBackend *backend); + +G_END_DECLS + +#endif /* __G_VFS_JOB_POLL_MOUNTABLE_H__ */ diff --git a/daemon/gvfsjobstopmountable.c b/daemon/gvfsjobstopmountable.c index 63df2afc..d2a2c098 100644 --- a/daemon/gvfsjobstopmountable.c +++ b/daemon/gvfsjobstopmountable.c @@ -49,6 +49,9 @@ g_vfs_job_stop_mountable_finalize (GObject *object) job = G_VFS_JOB_STOP_MOUNTABLE (object); + if (job->mount_source) + g_object_unref (job->mount_source); + g_free (job->filename); if (G_OBJECT_CLASS (g_vfs_job_stop_mountable_parent_class)->finalize) @@ -83,6 +86,7 @@ g_vfs_job_stop_mountable_new (DBusConnection *connection, DBusMessageIter iter; DBusError derror; char *path; + const char *dbus_id, *obj_path; guint32 flags; dbus_error_init (&derror); @@ -92,6 +96,8 @@ g_vfs_job_stop_mountable_new (DBusConnection *connection, if (!_g_dbus_message_iter_get_args (&iter, &derror, G_DBUS_TYPE_CSTRING, &path, DBUS_TYPE_UINT32, &flags, + DBUS_TYPE_STRING, &dbus_id, + DBUS_TYPE_OBJECT_PATH, &obj_path, 0)) { g_free (path); @@ -111,6 +117,7 @@ g_vfs_job_stop_mountable_new (DBusConnection *connection, job->filename = path; job->backend = backend; + job->mount_source = g_mount_source_new (dbus_id, obj_path); job->flags = flags; return G_VFS_JOB (job); @@ -132,7 +139,8 @@ run (GVfsJob *job) class->stop_mountable (op_job->backend, op_job, op_job->filename, - op_job->flags); + op_job->flags, + op_job->mount_source); } static gboolean @@ -147,7 +155,8 @@ try (GVfsJob *job) return class->try_stop_mountable (op_job->backend, op_job, op_job->filename, - op_job->flags); + op_job->flags, + op_job->mount_source); } /* Might be called on an i/o thread */ diff --git a/daemon/gvfsjobstopmountable.h b/daemon/gvfsjobstopmountable.h index e584ce53..cdc5be4a 100644 --- a/daemon/gvfsjobstopmountable.h +++ b/daemon/gvfsjobstopmountable.h @@ -46,6 +46,7 @@ struct _GVfsJobStopMountable GVfsBackend *backend; char *filename; GMountUnmountFlags flags; + GMountSource *mount_source; }; struct _GVfsJobStopMountableClass diff --git a/daemon/gvfsjobunmount.c b/daemon/gvfsjobunmount.c index 1d7d0a6c..142a5a66 100644 --- a/daemon/gvfsjobunmount.c +++ b/daemon/gvfsjobunmount.c @@ -46,6 +46,13 @@ static DBusMessage *create_reply (GVfsJob *job, static void g_vfs_job_unmount_finalize (GObject *object) { + GVfsJobUnmount *job; + + job = G_VFS_JOB_UNMOUNT (object); + + if (job->mount_source) + g_object_unref (job->mount_source); + if (G_OBJECT_CLASS (g_vfs_job_unmount_parent_class)->finalize) (*G_OBJECT_CLASS (g_vfs_job_unmount_parent_class)->finalize) (object); } @@ -77,6 +84,30 @@ g_vfs_job_unmount_new (DBusConnection *connection, GVfsBackend *backend) { GVfsJobUnmount *job; + DBusMessage *reply; + DBusMessageIter iter; + DBusError derror; + const char *dbus_id, *obj_path; + guint32 flags; + + + dbus_error_init (&derror); + dbus_message_iter_init (message, &iter); + + if (!_g_dbus_message_iter_get_args (&iter, &derror, + DBUS_TYPE_STRING, &dbus_id, + DBUS_TYPE_OBJECT_PATH, &obj_path, + DBUS_TYPE_UINT32, &flags, + 0)) + { + reply = dbus_message_new_error (message, + derror.name, + derror.message); + dbus_error_free (&derror); + + dbus_connection_send (connection, reply, NULL); + return NULL; + } g_debug ("g_vfs_job_unmount_new request: %p\n", message); @@ -86,6 +117,8 @@ g_vfs_job_unmount_new (DBusConnection *connection, NULL); job->backend = backend; + job->flags = flags; + job->mount_source = g_mount_source_new (dbus_id, obj_path); return G_VFS_JOB (job); } @@ -97,7 +130,9 @@ run (GVfsJob *job) GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend); class->unmount (op_job->backend, - op_job); + op_job, + op_job->flags, + op_job->mount_source); } static gboolean @@ -119,7 +154,9 @@ try (GVfsJob *job) } return class->try_unmount (op_job->backend, - op_job); + op_job, + op_job->flags, + op_job->mount_source); } static void diff --git a/daemon/gvfsjobunmount.h b/daemon/gvfsjobunmount.h index aaa18db2..ca8a23e5 100644 --- a/daemon/gvfsjobunmount.h +++ b/daemon/gvfsjobunmount.h @@ -44,6 +44,8 @@ struct _GVfsJobUnmount GVfsJobDBus parent_instance; GVfsBackend *backend; + GMountUnmountFlags flags; + GMountSource *mount_source; }; struct _GVfsJobUnmountClass diff --git a/daemon/gvfsjobunmountmountable.c b/daemon/gvfsjobunmountmountable.c index cb9eb22e..2599fcdd 100644 --- a/daemon/gvfsjobunmountmountable.c +++ b/daemon/gvfsjobunmountmountable.c @@ -49,6 +49,9 @@ g_vfs_job_unmount_mountable_finalize (GObject *object) job = G_VFS_JOB_UNMOUNT_MOUNTABLE (object); + if (job->mount_source) + g_object_unref (job->mount_source); + g_free (job->filename); if (G_OBJECT_CLASS (g_vfs_job_unmount_mountable_parent_class)->finalize) @@ -85,6 +88,7 @@ g_vfs_job_unmount_mountable_new (DBusConnection *connection, DBusError derror; char *path; guint32 flags; + const char *dbus_id, *obj_path; dbus_error_init (&derror); dbus_message_iter_init (message, &iter); @@ -93,6 +97,8 @@ g_vfs_job_unmount_mountable_new (DBusConnection *connection, if (!_g_dbus_message_iter_get_args (&iter, &derror, G_DBUS_TYPE_CSTRING, &path, DBUS_TYPE_UINT32, &flags, + DBUS_TYPE_STRING, &dbus_id, + DBUS_TYPE_OBJECT_PATH, &obj_path, 0)) { g_free (path); @@ -114,6 +120,7 @@ g_vfs_job_unmount_mountable_new (DBusConnection *connection, job->backend = backend; job->eject = eject; job->flags = flags; + job->mount_source = g_mount_source_new (dbus_id, obj_path); return G_VFS_JOB (job); } @@ -136,7 +143,8 @@ run (GVfsJob *job) class->eject_mountable (op_job->backend, op_job, op_job->filename, - op_job->flags); + op_job->flags, + op_job->mount_source); } else { @@ -150,7 +158,8 @@ run (GVfsJob *job) class->unmount_mountable (op_job->backend, op_job, op_job->filename, - op_job->flags); + op_job->flags, + op_job->mount_source); } } @@ -168,7 +177,8 @@ try (GVfsJob *job) return class->try_eject_mountable (op_job->backend, op_job, op_job->filename, - op_job->flags); + op_job->flags, + op_job->mount_source); } else { @@ -178,7 +188,8 @@ try (GVfsJob *job) return class->try_unmount_mountable (op_job->backend, op_job, op_job->filename, - op_job->flags); + op_job->flags, + op_job->mount_source); } } diff --git a/daemon/gvfsjobunmountmountable.h b/daemon/gvfsjobunmountmountable.h index 8a1a49d4..74e101fd 100644 --- a/daemon/gvfsjobunmountmountable.h +++ b/daemon/gvfsjobunmountmountable.h @@ -47,6 +47,7 @@ struct _GVfsJobUnmountMountable char *filename; gboolean eject; GMountUnmountFlags flags; + GMountSource *mount_source; }; struct _GVfsJobUnmountMountableClass diff --git a/daemon/gvfsreadchannel.c b/daemon/gvfsreadchannel.c index 1f043b85..e4834ec7 100644 --- a/daemon/gvfsreadchannel.c +++ b/daemon/gvfsreadchannel.c @@ -282,9 +282,11 @@ g_vfs_read_channel_send_data (GVfsReadChannel *read_channel, GVfsReadChannel * -g_vfs_read_channel_new (GVfsBackend *backend) +g_vfs_read_channel_new (GVfsBackend *backend, + GPid actual_consumer) { return g_object_new (G_VFS_TYPE_READ_CHANNEL, "backend", backend, + "actual-consumer", actual_consumer, NULL); } diff --git a/daemon/gvfsreadchannel.h b/daemon/gvfsreadchannel.h index a9a18bbf..458e65fb 100644 --- a/daemon/gvfsreadchannel.h +++ b/daemon/gvfsreadchannel.h @@ -47,7 +47,8 @@ struct _GVfsReadChannelClass GType g_vfs_read_channel_get_type (void) G_GNUC_CONST; -GVfsReadChannel *g_vfs_read_channel_new (GVfsBackend *backend); +GVfsReadChannel *g_vfs_read_channel_new (GVfsBackend *backend, + GPid actual_consumer); void g_vfs_read_channel_send_data (GVfsReadChannel *read_channel, char *buffer, gsize count); diff --git a/daemon/gvfswritechannel.c b/daemon/gvfswritechannel.c index 1646aceb..aae7f388 100644 --- a/daemon/gvfswritechannel.c +++ b/daemon/gvfswritechannel.c @@ -218,9 +218,11 @@ g_vfs_write_channel_send_written (GVfsWriteChannel *write_channel, GVfsWriteChannel * -g_vfs_write_channel_new (GVfsBackend *backend) +g_vfs_write_channel_new (GVfsBackend *backend, + GPid actual_consumer) { return g_object_new (G_VFS_TYPE_WRITE_CHANNEL, "backend", backend, + "actual-consumer", actual_consumer, NULL); } diff --git a/daemon/gvfswritechannel.h b/daemon/gvfswritechannel.h index ce4dc14e..65ed9d0c 100644 --- a/daemon/gvfswritechannel.h +++ b/daemon/gvfswritechannel.h @@ -47,7 +47,8 @@ struct _GVfsWriteChannelClass GType g_vfs_write_channel_get_type (void) G_GNUC_CONST; -GVfsWriteChannel *g_vfs_write_channel_new (GVfsBackend *backend); +GVfsWriteChannel *g_vfs_write_channel_new (GVfsBackend *backend, + GPid actual_consumer); void g_vfs_write_channel_send_written (GVfsWriteChannel *write_channel, gsize bytes_written); void g_vfs_write_channel_send_closed (GVfsWriteChannel *write_channel, |