diff options
-rw-r--r-- | ChangeLog | 76 | ||||
-rw-r--r-- | client/gdaemonfile.c | 2 | ||||
-rw-r--r-- | client/gdaemonmount.c | 115 | ||||
-rw-r--r-- | client/gdaemonmount.h | 5 | ||||
-rw-r--r-- | client/gdaemonvolumemonitor.c | 14 | ||||
-rw-r--r-- | client/gvfsfusedaemon.c | 11 | ||||
-rw-r--r-- | common/gmounttracker.c | 8 | ||||
-rw-r--r-- | common/gmounttracker.h | 1 | ||||
-rw-r--r-- | configure.ac | 23 | ||||
-rw-r--r-- | daemon/Makefile.am | 20 | ||||
-rw-r--r-- | daemon/gvfsbackend.c | 38 | ||||
-rw-r--r-- | daemon/gvfsbackend.h | 3 | ||||
-rw-r--r-- | daemon/gvfsjobunmount.c | 15 | ||||
-rw-r--r-- | daemon/mount.c | 18 | ||||
-rw-r--r-- | hal/ghaldrive.c | 180 | ||||
-rw-r--r-- | hal/ghalmount.c | 97 | ||||
-rw-r--r-- | hal/ghalvolume.c | 236 | ||||
-rw-r--r-- | hal/ghalvolume.h | 7 | ||||
-rw-r--r-- | hal/ghalvolumemonitor.c | 109 | ||||
-rw-r--r-- | hal/hal-device.c | 22 | ||||
-rw-r--r-- | hal/hal-device.h | 1 | ||||
-rw-r--r-- | hal/hal-marshal.list | 2 | ||||
-rw-r--r-- | hal/hal-pool.c | 36 | ||||
-rw-r--r-- | hal/hal-pool.h | 1 | ||||
-rw-r--r-- | programs/Makefile.am | 4 | ||||
-rw-r--r-- | programs/gvfs-mount.c | 51 |
26 files changed, 929 insertions, 166 deletions
@@ -1,3 +1,79 @@ +2007-12-19 David Zeuthen <davidz@redhat.com> + + Add the cdda:// backend for Compact Disc Digital Audio discs. + + Allow a backend to specify the fuse name directly instead of + using the display name + + Make GDaemonVolumeMonitor and GDaemonMount use the new + adopt_orphan_mount() function on GVolumeMonitor in gio. Also, + since a GMount now can be associated with a GVolume, implement + eject(). + + Add a new gvfs-less program. + + Make gvfs-mount capable of unmounting as well. + + HAL backend changes: attempt to unmount all mounts from + enclosing volumes and fail the ejection if one of the unmount + operations fails. Use new adopt_orphan_mount() from gio to + adopt cdda:// volumes for audio discs. Emit the 'eject-button' + signal on GDrive. Various other fixes. + + * client/gdaemonfile.c: (g_daemon_file_find_enclosing_mount): + * client/gdaemonmount.c: (g_daemon_mount_finalize), + (g_daemon_mount_new), (g_daemon_mount_get_volume), + (g_daemon_mount_get_drive), (g_daemon_mount_can_eject), + (foreign_volume_removed), (g_daemon_mount_set_foreign_volume), + (eject_wrapper_callback), (g_daemon_mount_eject), + (g_daemon_mount_eject_finish), (g_daemon_mount_mount_iface_init): + * client/gdaemonmount.h: + * client/gdaemonvolumemonitor.c: (mount_added), (mount_removed), + (g_daemon_volume_monitor_init): + * client/gvfsfusedaemon.c: (file_handle_close_stream), + (free_file_handle_for_path), (mount_record_new): + * common/gmounttracker.c: (g_mount_info_dup), (g_mount_info_unref), + (g_mount_info_from_dbus): + * common/gmounttracker.h: + * configure.ac: + * daemon/Makefile.am: + * daemon/gvfsbackend.c: (g_vfs_backend_finalize), + (g_vfs_backend_init), (g_vfs_backend_set_fuse_name), + (g_vfs_backend_get_fuse_name), (g_vfs_backend_register_mount): + * daemon/gvfsbackend.h: + * daemon/gvfsjobunmount.c: (unregister_mount_callback), + (send_reply): + * daemon/mount.c: (vfs_mount_free), (vfs_mount_to_dbus), + (register_mount), (list_mounts): + * hal/ghaldrive.c: (g_hal_drive_finalize), (hal_condition), + (g_hal_drive_new), (spawn_cb), (g_hal_drive_eject_do), + (free_unmount_mounts_op), (_eject_unmount_mounts_cb), + (_eject_unmount_mounts), (g_hal_drive_eject): + * hal/ghalmount.c: (g_hal_mount_finalize), (unmount_cb), + (unmount_read_error), (unmount_do), (g_hal_mount_unmount), + (eject_wrapper_callback), (g_hal_mount_eject), + (g_hal_mount_eject_finish): + * hal/ghalvolume.c: (g_hal_volume_finalize), (do_update_from_hal), + (g_hal_volume_new), (g_hal_volume_get_mount), + (foreign_mount_unmounted), (g_hal_volume_adopt_foreign_mount), + (g_hal_volume_has_foreign_mount_root), (spawn_cb), + (mount_foreign_callback), (g_hal_volume_mount), + (g_hal_volume_mount_finish), (eject_wrapper_callback), + (g_hal_volume_eject), (g_hal_volume_eject_finish): + * hal/ghalvolume.h: + * hal/ghalvolumemonitor.c: (adopt_orphan_mount), + (g_hal_volume_monitor_class_init), (update_volumes), + (update_mounts), (update_discs): + * hal/hal-device.c: (hal_device_class_init), + (_hal_device_hal_condition): + * hal/hal-device.h: + * hal/hal-marshal.list: + * hal/hal-pool.c: (hal_pool_class_init), (_hal_condition), + (hal_pool_new): + * hal/hal-pool.h: + * programs/Makefile.am: + * programs/gvfs-mount.c: (unmount_done_cb), (unmount), (main): + 2007-12-18 Luca Ferretti <elle.uca@libero.it> * configure.ac: Add ALL_LINGUAS and definition for GVFS_LOCALEDIR; diff --git a/client/gdaemonfile.c b/client/gdaemonfile.c index 251ce419..18a58c14 100644 --- a/client/gdaemonfile.c +++ b/client/gdaemonfile.c @@ -1439,7 +1439,7 @@ g_daemon_file_find_enclosing_mount (GFile *file, if (mount_info->user_visible) { - mount = g_daemon_mount_new (mount_info); + mount = g_daemon_mount_new (mount_info, NULL); g_mount_info_unref (mount_info); if (mount) diff --git a/client/gdaemonmount.c b/client/gdaemonmount.c index be5d5136..9e803384 100644 --- a/client/gdaemonmount.c +++ b/client/gdaemonmount.c @@ -37,6 +37,9 @@ struct _GDaemonMount { GObject parent; GMountInfo *mount_info; + + GVolume *foreign_volume; + GVolumeMonitor *volume_monitor; }; static void g_daemon_mount_mount_iface_init (GMountIface *iface); @@ -45,7 +48,6 @@ G_DEFINE_TYPE_WITH_CODE (GDaemonMount, g_daemon_mount, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT, g_daemon_mount_mount_iface_init)) - static void g_daemon_mount_finalize (GObject *object) { @@ -53,6 +55,12 @@ g_daemon_mount_finalize (GObject *object) mount = G_DAEMON_MOUNT (object); + if (mount->foreign_volume != NULL) + g_object_unref (mount->foreign_volume); + + if (mount->volume_monitor != NULL) + g_object_remove_weak_pointer (G_OBJECT (mount->volume_monitor), (gpointer) &(mount->volume_monitor)); + g_mount_info_unref (mount->mount_info); if (G_OBJECT_CLASS (g_daemon_mount_parent_class)->finalize) @@ -73,12 +81,17 @@ g_daemon_mount_init (GDaemonMount *daemon_mount) } GDaemonMount * -g_daemon_mount_new (GMountInfo *mount_info) +g_daemon_mount_new (GMountInfo *mount_info, + GVolumeMonitor *volume_monitor) { GDaemonMount *mount; mount = g_object_new (G_TYPE_DAEMON_MOUNT, NULL); mount->mount_info = g_mount_info_ref (mount_info); + mount->volume_monitor = volume_monitor; + g_object_set_data (G_OBJECT (mount), "g-stable-name", (gpointer) mount_info->stable_name); + if (mount->volume_monitor != NULL) + g_object_add_weak_pointer (G_OBJECT (volume_monitor), (gpointer) &(mount->volume_monitor)); return mount; } @@ -122,12 +135,18 @@ g_daemon_mount_get_uuid (GMount *mount) static GVolume * g_daemon_mount_get_volume (GMount *mount) { + GDaemonMount *daemon_mount = G_DAEMON_MOUNT (mount); + if (daemon_mount->foreign_volume != NULL) + return g_object_ref (daemon_mount->foreign_volume); return NULL; } static GDrive * g_daemon_mount_get_drive (GMount *mount) { + GDaemonMount *daemon_mount = G_DAEMON_MOUNT (mount); + if (daemon_mount->foreign_volume != NULL) + return g_volume_get_drive (daemon_mount->foreign_volume); return NULL; } @@ -140,10 +159,41 @@ g_daemon_mount_can_unmount (GMount *mount) static gboolean g_daemon_mount_can_eject (GMount *mount) { + GDaemonMount *daemon_mount = G_DAEMON_MOUNT (mount); + if (daemon_mount->foreign_volume != NULL) + return g_volume_can_eject (daemon_mount->foreign_volume); return FALSE; } static void +foreign_volume_removed (GVolume *volume, gpointer user_data) +{ + GDaemonMount *daemon_mount = G_DAEMON_MOUNT (user_data); + if (daemon_mount->foreign_volume == volume) + g_daemon_mount_set_foreign_volume (daemon_mount, NULL); +} + +void +g_daemon_mount_set_foreign_volume (GDaemonMount *mount, + GVolume *foreign_volume) +{ + if (mount->foreign_volume != NULL) + g_object_unref (mount->foreign_volume); + + if (foreign_volume != NULL) + { + mount->foreign_volume = foreign_volume; + g_signal_connect_object (foreign_volume, "removed", (GCallback) foreign_volume_removed, mount, 0); + } + else + mount->foreign_volume = NULL; + + g_signal_emit_by_name (mount, "changed"); + if (mount->volume_monitor != NULL) + g_signal_emit_by_name (mount->volume_monitor, "mount_changed", mount); +} + +static void unmount_reply (DBusMessage *reply, DBusConnection *connection, GError *io_error, @@ -196,6 +246,63 @@ g_daemon_mount_unmount_finish (GMount *mount, return TRUE; } +typedef struct { + GObject *object; + GAsyncReadyCallback callback; + gpointer user_data; +} EjectWrapperOp; + +static void +eject_wrapper_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + EjectWrapperOp *data = user_data; + data->callback (data->object, res, data->user_data); + g_free (data); +} + +static void +g_daemon_mount_eject (GMount *mount, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDaemonMount *daemon_mount = G_DAEMON_MOUNT (mount); + GDrive *drive; + + if (daemon_mount->foreign_volume != NULL) + { + drive = g_volume_get_drive (G_VOLUME (daemon_mount->foreign_volume)); + if (drive != NULL) + { + EjectWrapperOp *data; + data = g_new0 (EjectWrapperOp, 1); + data->object = G_OBJECT (mount); + data->callback = callback; + data->user_data = user_data; + g_drive_eject (drive, cancellable, eject_wrapper_callback, data); + } + } +} + +static gboolean +g_daemon_mount_eject_finish (GMount *mount, + GAsyncResult *result, + GError **error) +{ + GDaemonMount *daemon_mount = G_DAEMON_MOUNT (mount); + GDrive *drive; + + if (daemon_mount->foreign_volume != NULL) + { + drive = g_volume_get_drive (G_VOLUME (daemon_mount->foreign_volume)); + if (drive != NULL) + return g_drive_eject_finish (drive, result, error); + } + return TRUE; +} + static void g_daemon_mount_mount_iface_init (GMountIface *iface) { @@ -209,6 +316,6 @@ g_daemon_mount_mount_iface_init (GMountIface *iface) iface->can_eject = g_daemon_mount_can_eject; iface->unmount = g_daemon_mount_unmount; iface->unmount_finish = g_daemon_mount_unmount_finish; - iface->eject = NULL; /* Not supported */ - iface->eject_finish = NULL; /* Not supported */ + iface->eject = g_daemon_mount_eject; + iface->eject_finish = g_daemon_mount_eject_finish; } diff --git a/client/gdaemonmount.h b/client/gdaemonmount.h index e44421cc..bf4ba099 100644 --- a/client/gdaemonmount.h +++ b/client/gdaemonmount.h @@ -45,10 +45,13 @@ struct _GDaemonMountClass { GType g_daemon_mount_get_type (void) G_GNUC_CONST; -GDaemonMount *g_daemon_mount_new (GMountInfo *mount_info); +GDaemonMount *g_daemon_mount_new (GMountInfo *mount_info, + GVolumeMonitor *volume_monitor); GMountInfo *g_daemon_mount_get_mount_info (GDaemonMount *mount); +void g_daemon_mount_set_foreign_volume (GDaemonMount *mount, GVolume *foreign_volume); + G_END_DECLS #endif /* __G_DAEMON_MOUNT_H__ */ diff --git a/client/gdaemonvolumemonitor.c b/client/gdaemonvolumemonitor.c index 0d3bc8a6..77effae2 100644 --- a/client/gdaemonvolumemonitor.c +++ b/client/gdaemonvolumemonitor.c @@ -106,6 +106,7 @@ static void mount_added (GDaemonVolumeMonitor *daemon_monitor, GMountInfo *mount_info) { GDaemonMount *mount; + GVolume *volume; mount = find_mount_by_mount_info (daemon_monitor, mount_info); if (mount) @@ -114,10 +115,12 @@ mount_added (GDaemonVolumeMonitor *daemon_monitor, GMountInfo *mount_info) return; } - if (mount_info->user_visible) { - mount = g_daemon_mount_new (mount_info); + mount = g_daemon_mount_new (mount_info, G_VOLUME_MONITOR (daemon_monitor)); + volume = g_volume_monitor_adopt_orphan_mount (G_MOUNT (mount)); + if (volume != NULL) + g_daemon_mount_set_foreign_volume (mount, volume); daemon_monitor->mounts = g_list_prepend (daemon_monitor->mounts, mount); g_signal_emit_by_name (daemon_monitor, "mount_added", mount); } @@ -137,6 +140,7 @@ mount_removed (GDaemonVolumeMonitor *daemon_monitor, GMountInfo *mount_info) daemon_monitor->mounts = g_list_remove (daemon_monitor->mounts, mount); g_signal_emit_by_name (daemon_monitor, "mount_removed", mount); + g_signal_emit_by_name (mount, "unmounted"); g_object_unref (mount); } @@ -146,6 +150,7 @@ g_daemon_volume_monitor_init (GDaemonVolumeMonitor *daemon_monitor) GList *mounts, *l; GDaemonMount *mount; GMountInfo *info; + GVolume *volume; daemon_monitor->mount_tracker = g_mount_tracker_new (_g_daemon_vfs_get_async_bus ()); @@ -161,7 +166,10 @@ g_daemon_volume_monitor_init (GDaemonVolumeMonitor *daemon_monitor) info = l->data; if (info->user_visible) { - mount = g_daemon_mount_new (info); + mount = g_daemon_mount_new (info, G_VOLUME_MONITOR (daemon_monitor)); + volume = g_volume_monitor_adopt_orphan_mount (G_MOUNT (mount)); + if (volume != NULL) + g_daemon_mount_set_foreign_volume (mount, volume); daemon_monitor->mounts = g_list_prepend (daemon_monitor->mounts, mount); } diff --git a/client/gvfsfusedaemon.c b/client/gvfsfusedaemon.c index 6b2feec2..92ecea35 100644 --- a/client/gvfsfusedaemon.c +++ b/client/gvfsfusedaemon.c @@ -46,6 +46,7 @@ #include <glib/gurifuncs.h> /* stuff from common/ */ +#include <gdaemonmount.h> #include <gvfsdaemonprotocol.h> #include <gdbusutils.h> @@ -196,6 +197,7 @@ file_handle_new (void) static void file_handle_close_stream (FileHandle *file_handle) { + debug_print ("file_handle_close_stream\n"); if (file_handle->stream) { switch (file_handle->op) @@ -285,7 +287,7 @@ free_file_handle_for_path (const gchar *path) if (fh) { g_static_mutex_lock (&global_mutex); - g_hash_table_remove (global_fh_table, fh); + g_hash_table_remove (global_fh_table, path); g_static_mutex_unlock (&global_mutex); return TRUE; } @@ -302,7 +304,12 @@ mount_record_new (GMount *mount) mount_record = g_new (MountRecord, 1); mount_record->root = g_mount_get_root (mount); - name = g_mount_get_name (mount); + name = g_object_get_data (mount, "g-stable-name"); + if (name != NULL) + name = g_strdup (name); + else + name = g_mount_get_name (mount); + /* Keep in sync with gvfs daemon mount tracker */ mount_record->name = g_uri_escape_string (name, "+@#$., ", TRUE); g_free (name); diff --git a/common/gmounttracker.c b/common/gmounttracker.c index c0f1537b..4b3b7717 100644 --- a/common/gmounttracker.c +++ b/common/gmounttracker.c @@ -88,6 +88,7 @@ g_mount_info_dup (GMountInfo *info) copy = g_new (GMountInfo, 1); copy->ref_count = 1; copy->display_name = g_strdup (info->display_name); + copy->stable_name = g_strdup (info->stable_name); copy->icon = g_strdup (info->icon); copy->dbus_id = g_strdup (info->dbus_id); copy->object_path = g_strdup (info->object_path); @@ -112,6 +113,7 @@ g_mount_info_unref (GMountInfo *info) if (g_atomic_int_dec_and_test (&info->ref_count)) { g_free (info->display_name); + g_free (info->stable_name); g_free (info->icon); g_free (info->dbus_id); g_free (info->object_path); @@ -155,6 +157,7 @@ g_mount_info_from_dbus (DBusMessageIter *iter) GMountSpec *mount_spec; dbus_bool_t user_visible; char *display_name; + char *stable_name; char *icon; char *prefered_filename_encoding; char *dbus_id; @@ -170,6 +173,7 @@ g_mount_info_from_dbus (DBusMessageIter *iter) DBUS_TYPE_STRING, &dbus_id, DBUS_TYPE_OBJECT_PATH, &obj_path, DBUS_TYPE_STRING, &display_name, + DBUS_TYPE_STRING, &stable_name, DBUS_TYPE_STRING, &icon, DBUS_TYPE_STRING, &prefered_filename_encoding, DBUS_TYPE_BOOLEAN, &user_visible, @@ -177,9 +181,6 @@ g_mount_info_from_dbus (DBusMessageIter *iter) 0)) return NULL; - g_free (fuse_mountpoint); - - mount_spec = g_mount_spec_from_dbus (&struct_iter); if (mount_spec == NULL) { g_free (fuse_mountpoint); @@ -189,6 +190,7 @@ g_mount_info_from_dbus (DBusMessageIter *iter) info = g_new0 (GMountInfo, 1); info->ref_count = 1; info->display_name = g_strdup (display_name); + info->stable_name = g_strdup (stable_name); info->icon = g_strdup (icon); info->dbus_id = g_strdup (dbus_id); info->object_path = g_strdup (obj_path); diff --git a/common/gmounttracker.h b/common/gmounttracker.h index 13536f04..8ff166ee 100644 --- a/common/gmounttracker.h +++ b/common/gmounttracker.h @@ -41,6 +41,7 @@ typedef struct _GMountTrackerClass GMountTrackerClass; typedef struct { volatile int ref_count; char *display_name; + char *stable_name; char *icon; char *dbus_id; char *object_path; diff --git a/configure.ac b/configure.ac index ab879555..38782961 100644 --- a/configure.ac +++ b/configure.ac @@ -92,6 +92,28 @@ fi AC_SEARCH_LIBS(login_tty, util, [AC_DEFINE([HAVE_LOGIN_TTY],[],[Whether login_tty is available])]) +dnl ************************************************** +dnl *** Check if we should build with CDDA backend *** +dnl ************************************************** +AC_ARG_ENABLE(cdda, [ --disable-cdda build without CDDA backend]) +msg_cdda=no +CDDA_LIBS= +CDDA_CFLAGS= + +if test "x$enable_cdda" != "xno"; then + PKG_CHECK_EXISTS(libcdio_paranoia >= 0.78.2, msg_cdda=yes) + + if test "x$msg_cdda" == "xyes"; then + PKG_CHECK_MODULES(CDDA, libcdio_paranoia) + AC_DEFINE(HAVE_CDDA, 1, [Define to 1 if CDDA is going to be built]) + fi +fi + +AC_SUBST(CDDA_LIBS) +AC_SUBST(CDDA_CFLAGS) + +AM_CONDITIONAL(USE_CDDA, [test "$msg_cdda" = "yes"]) + dnl ********************** dnl *** Check for FUSE *** dnl ********************** @@ -280,5 +302,6 @@ echo "gvfs configuration summary:" echo " Samba support: $msg_samba FUSE support: $msg_fuse + CDDA support: $msg_cdda Use HAL for volume monitor: $msg_hal (with fast init path: $have_hal_fast_init) " diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 3e5ceab6..7e503188 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -30,10 +30,10 @@ service_DATA = gvfs-daemon.service mountdir = $(sysconfdir)/gvfs/mounts -libexec_PROGRAMS=gvfsd gvfsd-ftp gvfsd-sftp gvfsd-trash +libexec_PROGRAMS=gvfsd gvfsd-ftp gvfsd-sftp gvfsd-trash gvfsd-cdda -mount_in_files = ftp.mount.in sftp.mount.in trash.mount.in -mount_DATA = ftp.mount sftp.mount trash.mount +mount_in_files = ftp.mount.in sftp.mount.in trash.mount.in cdda.mount.in +mount_DATA = ftp.mount sftp.mount trash.mount cdda.mount if HAVE_SAMBA mount_in_files += smb.mount.in smb-browse.mount.in @@ -173,3 +173,17 @@ gvfsd_trash_CPPFLAGS = \ -DBACKEND_TYPES='"trash", G_VFS_TYPE_BACKEND_TRASH,' gvfsd_trash_LDADD = $(libraries) + +gvfsd_cdda_SOURCES = \ + gvfsbackendcdda.c gvfsbackendcdda.h \ + daemon-main.c daemon-main.h \ + daemon-main-generic.c + +gvfsd_cdda_CPPFLAGS = \ + -DBACKEND_HEADER=gvfsbackendcdda.h \ + -DDEFAULT_BACKEND_TYPE=cdda \ + -DMAX_JOB_THREADS=1 \ + $(CDDA_CFLAGS) \ + -DBACKEND_TYPES='"cdda", G_VFS_TYPE_BACKEND_CDDA,' + +gvfsd_cdda_LDADD = $(libraries) $(CDDA_LIBS) diff --git a/daemon/gvfsbackend.c b/daemon/gvfsbackend.c index 8093efab..c116698d 100644 --- a/daemon/gvfsbackend.c +++ b/daemon/gvfsbackend.c @@ -65,6 +65,7 @@ struct _GVfsBackendPrivate char *object_path; char *display_name; + char *stable_name; char *icon; char *prefered_filename_encoding; gboolean user_visible; @@ -138,6 +139,7 @@ g_vfs_backend_finalize (GObject *object) g_free (backend->priv->object_path); g_free (backend->priv->display_name); + g_free (backend->priv->stable_name); g_free (backend->priv->icon); g_free (backend->priv->prefered_filename_encoding); if (backend->priv->mount_spec) @@ -189,6 +191,7 @@ g_vfs_backend_init (GVfsBackend *backend) backend->priv->icon = g_strdup (""); backend->priv->prefered_filename_encoding = g_strdup (""); backend->priv->display_name = g_strdup (""); + backend->priv->stable_name = g_strdup (""); backend->priv->user_visible = TRUE; } @@ -272,6 +275,28 @@ g_vfs_backend_set_display_name (GVfsBackend *backend, backend->priv->display_name = g_strdup (display_name); } +/** + * g_vfs_backend_set_stable_name: + * @backend: backend + * @stable_name: the stable name + * + * For filesystems that can change the name during the lifetime + * of the filesystem this can be uses to set a separate stable + * name. This is used for instance as the directory representing + * the mounted file system in the standard UNIX file system + * namespace. + * + * If this function isn't called, the value passed to + * g_vfs_backend_set_display_name() will be used instead. + **/ +void +g_vfs_backend_set_stable_name (GVfsBackend *backend, + const char *stable_name) +{ + g_free (backend->priv->stable_name); + backend->priv->stable_name = g_strdup (stable_name); +} + void g_vfs_backend_set_icon_name (GVfsBackend *backend, const char *icon) @@ -319,6 +344,12 @@ g_vfs_backend_get_display_name (GVfsBackend *backend) } const char * +g_vfs_backend_get_stable_name (GVfsBackend *backend) +{ + return backend->priv->stable_name; +} + +const char * g_vfs_backend_get_icon_name (GVfsBackend *backend) { return backend->priv->icon; @@ -438,6 +469,7 @@ g_vfs_backend_register_mount (GVfsBackend *backend, GAsyncDBusCallback callback, gpointer user_data) { + const char *stable_name; DBusMessage *message; DBusMessageIter iter; dbus_bool_t user_visible; @@ -449,10 +481,16 @@ g_vfs_backend_register_mount (GVfsBackend *backend, if (message == NULL) _g_dbus_oom (); + if (backend->priv->stable_name != NULL) + stable_name = backend->priv->stable_name; + else + stable_name = backend->priv->display_name; + user_visible = backend->priv->user_visible; if (!dbus_message_append_args (message, DBUS_TYPE_OBJECT_PATH, &backend->priv->object_path, DBUS_TYPE_STRING, &backend->priv->display_name, + DBUS_TYPE_STRING, &stable_name, DBUS_TYPE_STRING, &backend->priv->icon, DBUS_TYPE_STRING, &backend->priv->prefered_filename_encoding, DBUS_TYPE_BOOLEAN, &user_visible, diff --git a/daemon/gvfsbackend.h b/daemon/gvfsbackend.h index aaff61ff..744e3a1b 100644 --- a/daemon/gvfsbackend.h +++ b/daemon/gvfsbackend.h @@ -345,6 +345,8 @@ GType g_vfs_lookup_backend (const char *type); void g_vfs_backend_set_display_name (GVfsBackend *backend, const char *display_name); +void g_vfs_backend_set_stable_name (GVfsBackend *backend, + const char *stable_name); void g_vfs_backend_set_icon_name (GVfsBackend *backend, const char *icon); void g_vfs_backend_set_prefered_filename_encoding (GVfsBackend *backend, @@ -361,6 +363,7 @@ void g_vfs_backend_unregister_mount (GVfsBackend *ba gpointer user_data); const char *g_vfs_backend_get_backend_type (GVfsBackend *backend); const char *g_vfs_backend_get_display_name (GVfsBackend *backend); +const char *g_vfs_backend_get_stable_name (GVfsBackend *backend); const char *g_vfs_backend_get_icon_name (GVfsBackend *backend); GMountSpec *g_vfs_backend_get_mount_spec (GVfsBackend *backend); GVfsDaemon *g_vfs_backend_get_daemon (GVfsBackend *backend); diff --git a/daemon/gvfsjobunmount.c b/daemon/gvfsjobunmount.c index 5731e3d3..a23b9be8 100644 --- a/daemon/gvfsjobunmount.c +++ b/daemon/gvfsjobunmount.c @@ -129,14 +129,16 @@ unregister_mount_callback (DBusMessage *unmount_reply, GError *error, gpointer user_data) { + GVfsBackend *backend; GVfsJobUnmount *op_job = G_VFS_JOB_UNMOUNT (user_data); g_print ("unregister_mount_callback, unmount_reply: %p, error: %p\n", unmount_reply, error); + backend = op_job->backend; (*G_VFS_JOB_CLASS (g_vfs_job_unmount_parent_class)->send_reply) (G_VFS_JOB (op_job)); /* Unlink job source from daemon */ - g_vfs_job_source_closed (G_VFS_JOB_SOURCE (op_job->backend)); + g_vfs_job_source_closed (G_VFS_JOB_SOURCE (backend)); } /* Might be called on an i/o thread */ @@ -146,10 +148,13 @@ send_reply (GVfsJob *job) GVfsJobUnmount *op_job = G_VFS_JOB_UNMOUNT (job); g_print ("send_reply, failed: %d\n", job->failed); - - g_vfs_backend_unregister_mount (op_job->backend, - unregister_mount_callback, - job); + + if (job->failed) + (*G_VFS_JOB_CLASS (g_vfs_job_unmount_parent_class)->send_reply) (G_VFS_JOB (op_job)); + else + g_vfs_backend_unregister_mount (op_job->backend, + unregister_mount_callback, + job); } /* Might be called on an i/o thread */ diff --git a/daemon/mount.c b/daemon/mount.c index 12d3ff81..cfda6779 100644 --- a/daemon/mount.c +++ b/daemon/mount.c @@ -36,6 +36,7 @@ typedef struct { char *display_name; + char *stable_name; char *icon; char *prefered_filename_encoding; gboolean user_visible; @@ -138,6 +139,7 @@ static void vfs_mount_free (VfsMount *mount) { g_free (mount->display_name); + g_free (mount->stable_name); g_free (mount->icon); g_free (mount->prefered_filename_encoding); g_free (mount->dbus_id); @@ -175,6 +177,11 @@ vfs_mount_to_dbus (VfsMount *mount, DBUS_TYPE_STRING, &mount->display_name)) _g_dbus_oom (); + + if (!dbus_message_iter_append_basic (&struct_iter, + DBUS_TYPE_STRING, + &mount->stable_name)) + _g_dbus_oom (); if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, @@ -199,14 +206,14 @@ vfs_mount_to_dbus (VfsMount *mount, char *fs_name; /* Keep in sync with fuse daemon */ - fs_name = g_uri_escape_string (mount->display_name, "+@#$., ", TRUE); + fs_name = g_uri_escape_string (mount->stable_name, "+@#$., ", TRUE); fuse_mountpoint = g_build_filename (g_get_home_dir(), ".gvfs", fs_name, NULL); } if (fuse_mountpoint == NULL) fuse_mountpoint = g_strdup (""); - + _g_dbus_message_iter_append_cstring (&struct_iter, fuse_mountpoint); g_mount_spec_to_dbus (&struct_iter, mount->mount_spec); @@ -551,7 +558,7 @@ register_mount (DBusConnection *connection, VfsMount *mount; DBusMessage *reply; DBusError error; - const char *display_name, *icon, *obj_path, *id, *prefered_filename_encoding; + const char *display_name, *stable_name, *icon, *obj_path, *id, *prefered_filename_encoding; dbus_bool_t user_visible; DBusMessageIter iter; GMountSpec *mount_spec; @@ -565,6 +572,7 @@ register_mount (DBusConnection *connection, &error, DBUS_TYPE_OBJECT_PATH, &obj_path, DBUS_TYPE_STRING, &display_name, + DBUS_TYPE_STRING, &stable_name, DBUS_TYPE_STRING, &icon, DBUS_TYPE_STRING, &prefered_filename_encoding, DBUS_TYPE_BOOLEAN, &user_visible, @@ -577,7 +585,7 @@ register_mount (DBusConnection *connection, else if ((mount_spec = g_mount_spec_from_dbus (&iter)) == NULL) reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS, - "Error in mount spec"); + "Error in mount spec"); else if (match_vfs_mount (mount_spec) != NULL) reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS, @@ -586,6 +594,7 @@ register_mount (DBusConnection *connection, { mount = g_new0 (VfsMount, 1); mount->display_name = g_strdup (display_name); + mount->stable_name = g_strdup (stable_name); mount->icon = g_strdup (icon); mount->prefered_filename_encoding = g_strdup (prefered_filename_encoding); mount->user_visible = user_visible; @@ -751,6 +760,7 @@ list_mounts (DBusConnection *connection, DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_BOOLEAN_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING G_MOUNT_SPEC_TYPE_AS_STRING diff --git a/hal/ghaldrive.c b/hal/ghaldrive.c index df3a8fbd..5071e1bd 100644 --- a/hal/ghaldrive.c +++ b/hal/ghaldrive.c @@ -91,6 +91,9 @@ g_hal_drive_finalize (GObject *object) g_free (drive->name); g_free (drive->icon); + + if (drive->volume_monitor != NULL) + g_object_remove_weak_pointer (G_OBJECT (drive->volume_monitor), (gpointer) &(drive->volume_monitor)); if (G_OBJECT_CLASS (g_hal_drive_parent_class)->finalize) (*G_OBJECT_CLASS (g_hal_drive_parent_class)->finalize) (object); @@ -331,6 +334,15 @@ _update_from_hal (GHalDrive *d, gboolean emit_changed) } static void +hal_condition (HalDevice *device, const char *name, const char *detail, gpointer user_data) +{ + GHalDrive *hal_drive = G_HAL_DRIVE (user_data); + + if (strcmp (name, "EjectPressed") == 0) + g_signal_emit_by_name (hal_drive, "eject-button"); +} + +static void hal_changed (HalDevice *device, const char *key, gpointer user_data) { GHalDrive *hal_drive = G_HAL_DRIVE (user_data); @@ -357,6 +369,7 @@ g_hal_drive_new (GVolumeMonitor *volume_monitor, drive->icon = g_strdup_printf ("drive-removable-media"); g_signal_connect_object (device, "hal_property_changed", (GCallback) hal_changed, drive, 0); + g_signal_connect_object (device, "hal_condition", (GCallback) hal_condition, drive, 0); _update_from_hal (drive, FALSE); @@ -498,27 +511,37 @@ spawn_cb (GPid pid, gint status, gpointer user_data) { SpawnOp *data = user_data; GSimpleAsyncResult *simple; - - /* TODO: how do we report an error back to the caller while telling - * him that we already have shown an error dialog to the user? - * - * G_IO_ERROR_FAILED_NO_UI or something? - */ - simple = g_simple_async_result_new (data->object, - data->callback, - data->user_data, - NULL); + + if (WEXITSTATUS (status) != 0) + { + GError *error; + error = g_error_new_literal (G_IO_ERROR, + G_IO_ERROR_FAILED_HANDLED, + "You are not supposed to show G_IO_ERROR_FAILED_HANDLED in the UI"); + simple = g_simple_async_result_new_from_error (data->object, + data->callback, + data->user_data, + error); + g_error_free (error); + } + else + { + simple = g_simple_async_result_new (data->object, + data->callback, + data->user_data, + NULL); + } g_simple_async_result_complete (simple); g_object_unref (simple); g_free (data); } static void -g_hal_drive_eject (GDrive *drive, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +g_hal_drive_eject_do (GDrive *drive, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GHalDrive *hal_drive = G_HAL_DRIVE (drive); SpawnOp *data; @@ -558,6 +581,135 @@ g_hal_drive_eject (GDrive *drive, g_child_watch_add (child_pid, spawn_cb, data); } + +typedef struct { + GDrive *drive; + GAsyncReadyCallback callback; + gpointer user_data; + GCancellable *cancellable; + + GList *pending_mounts; +} UnmountMountsOp; + +static void +free_unmount_mounts_op (UnmountMountsOp *data) +{ + GList *l; + + for (l = data->pending_mounts; l != NULL; l = l->next) + { + GMount *mount = l->data; + g_object_unref (mount); + } + g_list_free (data->pending_mounts); +} + +static void _eject_unmount_mounts (UnmountMountsOp *data); + +static void +_eject_unmount_mounts_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + UnmountMountsOp *data = user_data; + GMount *mount = G_MOUNT (source_object); + GSimpleAsyncResult *simple; + GError *error = NULL; + + if (!g_mount_unmount_finish (mount, res, &error)) + { + /* make the error dialog more targeted to the drive.. unless the user has already seen a dialog */ + if (error->code != G_IO_ERROR_FAILED_HANDLED) + { + g_error_free (error); + error = g_error_new (G_IO_ERROR, G_IO_ERROR_BUSY, + _("Failed to eject media; one or more volumes on the media are busy.")); + } + + /* unmount failed; need to fail the whole eject operation */ + simple = g_simple_async_result_new_from_error (G_OBJECT (data->drive), + data->callback, + data->user_data, + error); + g_error_free (error); + g_simple_async_result_complete (simple); + g_object_unref (simple); + + free_unmount_mounts_op (data); + } + else + { + + g_warning ("successfully unmount %p", mount); + + /* move on to the next mount.. */ + _eject_unmount_mounts (data); + } + + g_object_unref (mount); +} + +static void +_eject_unmount_mounts (UnmountMountsOp *data) +{ + GMount *mount; + + if (data->pending_mounts == NULL) + { + + g_warning ("all pending mounts done; ejecting drive"); + + g_hal_drive_eject_do (data->drive, + data->cancellable, + data->callback, + data->user_data); + g_free (data); + } + else + { + mount = data->pending_mounts->data; + data->pending_mounts = g_list_remove (data->pending_mounts, mount); + + g_warning ("unmounting %p", mount); + + g_mount_unmount (mount, + data->cancellable, + _eject_unmount_mounts_cb, + data); + } +} + +static void +g_hal_drive_eject (GDrive *drive, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GHalDrive *hal_drive = G_HAL_DRIVE (drive); + UnmountMountsOp *data; + GList *l; + + /* first we need to go through all the volumes and unmount their assoicated mounts (if any) */ + + data = g_new0 (UnmountMountsOp, 1); + data->drive = drive; + data->cancellable = cancellable; + data->callback = callback; + data->user_data = user_data; + + for (l = hal_drive->volumes; l != NULL; l = l->next) + { + GHalVolume *volume = l->data; + GMount *mount; /* the mount may be foreign; cannot assume GHalMount */ + + mount = g_volume_get_mount (G_VOLUME (volume)); + if (mount != NULL) + data->pending_mounts = g_list_prepend (data->pending_mounts, g_object_ref (mount)); + } + + _eject_unmount_mounts (data); +} + static gboolean g_hal_drive_eject_finish (GDrive *drive, GAsyncResult *result, diff --git a/hal/ghalmount.c b/hal/ghalmount.c index 0224cda7..d6729e4a 100644 --- a/hal/ghalmount.c +++ b/hal/ghalmount.c @@ -99,6 +99,9 @@ g_hal_mount_finalize (GObject *object) if (mount->override_root != NULL) g_object_unref (mount->override_root); + + if (mount->volume_monitor != NULL) + g_object_remove_weak_pointer (G_OBJECT (mount->volume_monitor), (gpointer) &(mount->volume_monitor)); if (G_OBJECT_CLASS (g_hal_mount_parent_class)->finalize) (*G_OBJECT_CLASS (g_hal_mount_parent_class)->finalize) (object); @@ -689,12 +692,12 @@ typedef struct { guint error_channel_source_id; GString *error_string; gboolean using_legacy; -} EjectUnmountOp; +} UnmountOp; static void -eject_unmount_cb (GPid pid, gint status, gpointer user_data) +unmount_cb (GPid pid, gint status, gpointer user_data) { - EjectUnmountOp *data = user_data; + UnmountOp *data = user_data; GSimpleAsyncResult *simple; if (WEXITSTATUS (status) != 0) @@ -713,15 +716,15 @@ eject_unmount_cb (GPid pid, gint status, gpointer user_data) } else { - /* TODO: how do we report an error back to the caller while telling - * him that we already have shown an error dialog to the user? - * - * G_IO_ERROR_FAILED_NO_UI or something? - */ - simple = g_simple_async_result_new (data->object, - data->callback, - data->user_data, - NULL); + GError *error; + error = g_error_new_literal (G_IO_ERROR, + G_IO_ERROR_FAILED_HANDLED, + "You are not supposed to show G_IO_ERROR_FAILED_HANDLED in the UI"); + simple = g_simple_async_result_new_from_error (data->object, + data->callback, + data->user_data, + error); + g_error_free (error); } } else @@ -744,13 +747,13 @@ eject_unmount_cb (GPid pid, gint status, gpointer user_data) } static gboolean -eject_unmount_read_error (GIOChannel *channel, +unmount_read_error (GIOChannel *channel, GIOCondition condition, gpointer user_data) { char *str; gsize str_len; - EjectUnmountOp *data = user_data; + UnmountOp *data = user_data; g_io_channel_read_to_end (channel, &str, &str_len, NULL); g_string_append (data->error_string, str); @@ -759,18 +762,18 @@ eject_unmount_read_error (GIOChannel *channel, } static void -eject_unmount_do (GMount *mount, +unmount_do (GMount *mount, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, char **argv, gboolean using_legacy) { - EjectUnmountOp *data; + UnmountOp *data; GPid child_pid; GError *error; - data = g_new0 (EjectUnmountOp, 1); + data = g_new0 (UnmountOp, 1); data->object = G_OBJECT (mount); data->callback = callback; data->user_data = user_data; @@ -802,8 +805,8 @@ eject_unmount_do (GMount *mount, } data->error_string = g_string_new (""); data->error_channel = g_io_channel_unix_new (data->error_fd); - 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); } @@ -829,17 +832,33 @@ g_hal_mount_unmount (GMount *mount, argv[2] = NULL; } - eject_unmount_do (mount, cancellable, callback, user_data, argv, using_legacy); + unmount_do (mount, cancellable, callback, user_data, argv, using_legacy); } static gboolean g_hal_mount_unmount_finish (GMount *mount, - GAsyncResult *result, - GError **error) + GAsyncResult *result, + GError **error) { return TRUE; } +typedef struct { + GObject *object; + GAsyncReadyCallback callback; + gpointer user_data; +} EjectWrapperOp; + +static void +eject_wrapper_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + EjectWrapperOp *data = user_data; + data->callback (data->object, res, data->user_data); + g_free (data); +} + static void g_hal_mount_eject (GMount *mount, GCancellable *cancellable, @@ -847,22 +866,21 @@ g_hal_mount_eject (GMount *mount, gpointer user_data) { GHalMount *hal_mount = G_HAL_MOUNT (mount); - char *argv[] = {"gnome-mount", "-e", "-b", "-d", NULL, NULL}; - gboolean using_legacy = FALSE; + GDrive *drive; - if (hal_mount->device != NULL) - { - argv[4] = hal_mount->device_path; - } - else + if (hal_mount->volume != NULL) { - using_legacy = TRUE; - argv[0] = "eject"; - argv[1] = hal_mount->mount_path; - argv[2] = NULL; + drive = g_volume_get_drive (G_VOLUME (hal_mount->volume)); + if (drive != NULL) + { + EjectWrapperOp *data; + data = g_new0 (EjectWrapperOp, 1); + data->object = G_OBJECT (mount); + data->callback = callback; + data->user_data = user_data; + g_drive_eject (drive, cancellable, eject_wrapper_callback, data); + } } - - eject_unmount_do (mount, cancellable, callback, user_data, argv, using_legacy); } static gboolean @@ -870,6 +888,15 @@ g_hal_mount_eject_finish (GMount *mount, GAsyncResult *result, GError **error) { + GHalMount *hal_mount = G_HAL_MOUNT (mount); + GDrive *drive; + + if (hal_mount->volume != NULL) + { + drive = g_volume_get_drive (G_VOLUME (hal_mount->volume)); + if (drive != NULL) + return g_drive_eject_finish (drive, result, error); + } return TRUE; } diff --git a/hal/ghalvolume.c b/hal/ghalvolume.c index f839e1f8..b1863958 100644 --- a/hal/ghalvolume.c +++ b/hal/ghalvolume.c @@ -49,6 +49,13 @@ struct _GHalVolume { HalDevice *device; HalDevice *drive_device; + /* set on creation if we won't create a GHalMount object ourselves + * and instead except to adopt one, with the given mount root, + * via adopt_orphan_mount() + */ + GFile *foreign_mount_root; + GMount *foreign_mount; + char *name; char *icon; }; @@ -87,6 +94,15 @@ g_hal_volume_finalize (GObject *object) if (volume->drive_device != NULL) g_object_unref (volume->drive_device); + if (volume->foreign_mount_root != NULL) + g_object_unref (volume->foreign_mount_root); + + if (volume->foreign_mount != NULL) + g_object_unref (volume->foreign_mount); + + if (volume->volume_monitor != NULL) + g_object_remove_weak_pointer (G_OBJECT (volume->volume_monitor), (gpointer) &(volume->volume_monitor)); + g_free (volume->name); g_free (volume->icon); @@ -216,20 +232,27 @@ do_update_from_hal (GHalVolume *mv) volume_disc_is_blank = hal_device_get_property_bool (volume, "volume.disc.is_blank"); volume_disc_type = hal_device_get_property_string (volume, "volume.disc.type"); - if (volume_fs_label != NULL && strlen (volume_fs_label) > 0) { - name = g_strdup (volume_fs_label); - } else if (volume_is_disc) { - if (volume_disc_has_audio) { - if (volume_disc_has_data) - name = g_strdup (_("Mixed Audio/Data Disc")); - else - name = g_strdup (_("Audio Disc")); - } else { - name = g_strdup (get_disc_name (volume_disc_type, volume_disc_is_blank)); + if (volume_is_disc && volume_disc_has_audio && mv->foreign_mount_root != NULL) + { + name = g_strdup (_("Audio Disc")); + } + else + { + if (volume_fs_label != NULL && strlen (volume_fs_label) > 0) { + name = g_strdup (volume_fs_label); + } else if (volume_is_disc) { + if (volume_disc_has_audio) { + if (volume_disc_has_data) + name = g_strdup (_("Mixed Audio/Data Disc")); + else + name = g_strdup (_("Audio Disc")); + } else { + name = g_strdup (get_disc_name (volume_disc_type, volume_disc_is_blank)); + } + } else { + name = format_size_for_display (volume_size); + } } - } else { - name = format_size_for_display (volume_size); - } mv->name = name; mv->icon = _drive_get_icon (drive); /* use the drive icon since we're unmounted */ @@ -321,10 +344,11 @@ compute_uuid (GHalVolume *volume) } GHalVolume * -g_hal_volume_new (GVolumeMonitor *volume_monitor, - HalDevice *device, - HalPool *pool, - GHalDrive *drive) +g_hal_volume_new (GVolumeMonitor *volume_monitor, + HalDevice *device, + HalPool *pool, + GFile *foreign_mount_root, + GHalDrive *drive) { GHalVolume *volume; HalDevice *drive_device; @@ -345,6 +369,7 @@ g_hal_volume_new (GVolumeMonitor *volume_monitor, volume->device_path = g_strdup (hal_device_get_property_string (device, "block.device")); volume->device = g_object_ref (device); volume->drive_device = g_object_ref (drive_device); + volume->foreign_mount_root = foreign_mount_root != NULL ? g_object_ref (foreign_mount_root) : NULL; g_signal_connect_object (device, "hal_property_changed", (GCallback) hal_changed, volume, 0); g_signal_connect_object (drive_device, "hal_property_changed", (GCallback) hal_changed, volume, 0); @@ -495,6 +520,9 @@ g_hal_volume_get_mount (GVolume *volume) { GHalVolume *hal_volume = G_HAL_VOLUME (volume); + if (hal_volume->foreign_mount != NULL) + return g_object_ref (hal_volume->foreign_mount); + if (hal_volume->mount != NULL) return g_object_ref (hal_volume->mount); @@ -530,6 +558,44 @@ g_hal_volume_has_uuid (GHalVolume *volume, return FALSE; } +static void +foreign_mount_unmounted (GMount *mount, gpointer user_data) +{ + GHalVolume *volume = G_HAL_VOLUME (user_data); + if (volume->foreign_mount == mount) + g_hal_volume_adopt_foreign_mount (volume, NULL); +} + +void +g_hal_volume_adopt_foreign_mount (GHalVolume *volume, GMount *foreign_mount) +{ + if (volume->foreign_mount != NULL) + g_object_unref (volume->foreign_mount); + + if (foreign_mount != NULL) + { + volume->foreign_mount = g_object_ref (foreign_mount); + g_signal_connect_object (foreign_mount, "unmounted", (GCallback) foreign_mount_unmounted, volume, 0); + } + else + volume->foreign_mount = NULL; + + g_signal_emit_by_name (volume, "changed"); + if (volume->volume_monitor != NULL) + g_signal_emit_by_name (volume->volume_monitor, "volume_changed", volume); +} + +gboolean +g_hal_volume_has_foreign_mount_root (GHalVolume *volume, + GFile *mount_root) +{ + GHalVolume *hal_volume = G_HAL_VOLUME (volume); + if (hal_volume->foreign_mount_root != NULL) + return g_file_equal (hal_volume->foreign_mount_root, mount_root); + return FALSE; +} + + typedef struct { GObject *object; GAsyncReadyCallback callback; @@ -547,17 +613,27 @@ spawn_cb (GPid pid, gint status, gpointer user_data) * mounted is made available before returning to the user */ g_hal_volume_monitor_force_update (G_HAL_VOLUME_MONITOR (G_HAL_VOLUME (data->object)->volume_monitor)); - - /* TODO: how do we report an error back to the caller while telling - * him that we already have shown an error dialog to the user? - * - * G_IO_ERROR_FAILED_NO_UI or something? - */ - simple = g_simple_async_result_new (data->object, - data->callback, - data->user_data, - NULL); + if (WEXITSTATUS (status) != 0) + { + GError *error; + error = g_error_new_literal (G_IO_ERROR, + G_IO_ERROR_FAILED_HANDLED, + "You are not supposed to show G_IO_ERROR_FAILED_HANDLED in the UI"); + simple = g_simple_async_result_new_from_error (data->object, + data->callback, + data->user_data, + error); + g_error_free (error); + } + else + { + simple = g_simple_async_result_new (data->object, + data->callback, + data->user_data, + NULL); + } + g_simple_async_result_complete (simple); g_object_unref (simple); g_free (data); @@ -604,48 +680,120 @@ spawn_do (GVolume *volume, g_child_watch_add (child_pid, spawn_cb, data); } +typedef struct +{ + GHalVolume *enclosing_volume; + GAsyncReadyCallback callback; + gpointer user_data; +} ForeignMountOp; + +static void +mount_foreign_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + ForeignMountOp *data = user_data; + data->callback (G_OBJECT (data->enclosing_volume), res, data->user_data); + g_free (data); +} + static void -g_hal_volume_mount (GVolume *volume, +g_hal_volume_mount (GVolume *volume, GMountOperation *mount_operation, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GHalVolume *hal_volume = G_HAL_VOLUME (volume); - char *argv[] = {"gnome-mount", "-b", "-d", NULL, NULL}; - - argv[3] = hal_volume->device_path; - spawn_do (volume, cancellable, callback, user_data, argv); + if (hal_volume->foreign_mount_root != NULL) + { + ForeignMountOp *data; + + data = g_new0 (ForeignMountOp, 1); + data->enclosing_volume = hal_volume; + data->callback = callback; + data->user_data = user_data; + + g_file_mount_enclosing_volume (hal_volume->foreign_mount_root, + mount_operation, + cancellable, + mount_foreign_callback, + data); + } + else + { + char *argv[] = {"gnome-mount", "-b", "-d", NULL, NULL}; + argv[3] = hal_volume->device_path; + spawn_do (volume, cancellable, callback, user_data, argv); + } } static gboolean -g_hal_volume_mount_finish (GVolume *volume, +g_hal_volume_mount_finish (GVolume *volume, GAsyncResult *result, GError **error) { - return TRUE; + GHalVolume *hal_volume = G_HAL_VOLUME (volume); + + if (hal_volume->foreign_mount_root != NULL) + { + return g_file_mount_enclosing_volume_finish (hal_volume->foreign_mount_root, result, error); + } + else + { + return TRUE; + } } +typedef struct { + GObject *object; + GAsyncReadyCallback callback; + gpointer user_data; +} EjectWrapperOp; + static void -g_hal_volume_eject (GVolume *volume, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +eject_wrapper_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + EjectWrapperOp *data = user_data; + data->callback (data->object, res, data->user_data); + g_free (data); +} + +static void +g_hal_volume_eject (GVolume *volume, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GHalVolume *hal_volume = G_HAL_VOLUME (volume); - char *argv[] = {"gnome-mount", "-e", "-b", "-d", NULL, NULL}; - argv[4] = hal_volume->device_path; + g_warning ("hal_volume_eject"); - spawn_do (volume, cancellable, callback, user_data, argv); + if (hal_volume->drive != NULL) + { + EjectWrapperOp *data; + data = g_new0 (EjectWrapperOp, 1); + data->object = G_OBJECT (volume); + data->callback = callback; + data->user_data = user_data; + g_drive_eject (G_DRIVE (hal_volume->drive), cancellable, eject_wrapper_callback, data); + } } static gboolean -g_hal_volume_eject_finish (GVolume *volume, - GAsyncResult *result, - GError **error) +g_hal_volume_eject_finish (GVolume *volume, + GAsyncResult *result, + GError **error) { + GHalVolume *hal_volume = G_HAL_VOLUME (volume); + + if (hal_volume->drive != NULL) + { + return g_drive_eject_finish (G_DRIVE (hal_volume->drive), result, error); + } return TRUE; } diff --git a/hal/ghalvolume.h b/hal/ghalvolume.h index c8376b1a..7bff3421 100644 --- a/hal/ghalvolume.h +++ b/hal/ghalvolume.h @@ -49,6 +49,7 @@ void g_hal_volume_register (GIOModule *module); GHalVolume *g_hal_volume_new (GVolumeMonitor *volume_monitor, HalDevice *device, HalPool *pool, + GFile *foreign_mount_root, GHalDrive *drive); gboolean g_hal_volume_has_mount_path (GHalVolume *volume, @@ -58,6 +59,12 @@ gboolean g_hal_volume_has_udi (GHalVolume *volume, gboolean g_hal_volume_has_uuid (GHalVolume *volume, const char *uuid); +gboolean g_hal_volume_has_foreign_mount_root (GHalVolume *volume, + GFile *mount_root); + +void g_hal_volume_adopt_foreign_mount (GHalVolume *volume, + GMount *foreign_mount); + void g_hal_volume_set_mount (GHalVolume *volume, GHalMount *mount); void g_hal_volume_unset_mount (GHalVolume *volume, diff --git a/hal/ghalvolumemonitor.c b/hal/ghalvolumemonitor.c index 4336ef13..9bc8fdee 100644 --- a/hal/ghalvolumemonitor.c +++ b/hal/ghalvolumemonitor.c @@ -420,6 +420,35 @@ is_supported (void) return pool != NULL; } +static GVolume * +adopt_orphan_mount (GMount *mount) +{ + GList *l; + GFile *mount_root; + GVolume *ret; + + if (the_volume_monitor == NULL) + return NULL; + + ret = NULL; + mount_root = g_mount_get_root (mount); + + /* right now we only support discs as foreign mounts */ + for (l = the_volume_monitor->disc_volumes; l != NULL; l = l->next) + { + GHalVolume *volume = l->data; + if (g_hal_volume_has_foreign_mount_root (volume, mount_root)) + { + g_hal_volume_adopt_foreign_mount (volume, mount); + ret = g_object_ref (volume); + break; + } + } + + g_object_unref (mount_root); + return ret; +} + static void g_hal_volume_monitor_class_init (GHalVolumeMonitorClass *klass) { @@ -435,6 +464,7 @@ g_hal_volume_monitor_class_init (GHalVolumeMonitorClass *klass) monitor_class->get_connected_drives = get_connected_drives; monitor_class->get_volume_for_uuid = get_volume_for_uuid; monitor_class->get_mount_for_uuid = get_mount_for_uuid; + monitor_class->adopt_orphan_mount = adopt_orphan_mount; native_class->priority = 1; native_class->name = "hal"; @@ -735,6 +765,7 @@ update_volumes (GHalVolumeMonitor *monitor) g_hal_volume_removed (volume); monitor->volumes = g_list_remove (monitor->volumes, volume); g_signal_emit_by_name (monitor, "volume_removed", volume); + g_signal_emit_by_name (volume, "removed"); g_object_unref (volume); } } @@ -749,7 +780,7 @@ update_volumes (GHalVolumeMonitor *monitor) drive = find_drive_by_udi (monitor, hal_device_get_property_string (d, "block.storage_device")); //g_warning ("hal adding vol %s (drive %p)", hal_device_get_property_string (d, "block.device"), drive); - volume = g_hal_volume_new (G_VOLUME_MONITOR (monitor), d, monitor->pool, drive); + volume = g_hal_volume_new (G_VOLUME_MONITOR (monitor), d, monitor->pool, NULL, drive); if (volume != NULL) { monitor->volumes = g_list_prepend (monitor->volumes, volume); @@ -794,6 +825,7 @@ update_mounts (GHalVolumeMonitor *monitor) g_hal_mount_unmounted (mount); monitor->mounts = g_list_remove (monitor->mounts, mount); g_signal_emit_by_name (monitor, "mount_removed", mount); + g_signal_emit_by_name (mount, "unmounted"); g_object_unref (mount); } } @@ -874,6 +906,7 @@ update_discs (GHalVolumeMonitor *monitor) g_hal_mount_unmounted (mount); monitor->disc_mounts = g_list_remove (monitor->disc_mounts, mount); g_signal_emit_by_name (monitor, "mount_removed", mount); + g_signal_emit_by_name (mount, "unmounted"); g_object_unref (mount); } @@ -883,6 +916,7 @@ update_discs (GHalVolumeMonitor *monitor) g_hal_volume_removed (volume); monitor->disc_volumes = g_list_remove (monitor->disc_volumes, volume); g_signal_emit_by_name (monitor, "volume_removed", volume); + g_signal_emit_by_name (volume, "removed"); g_object_unref (volume); } } @@ -898,74 +932,59 @@ update_discs (GHalVolumeMonitor *monitor) drive = find_drive_by_udi (monitor, drive_udi); if (drive != NULL) { - volume = g_hal_volume_new (G_VOLUME_MONITOR (monitor), d, monitor->pool, drive); - if (volume != NULL) + if (hal_device_get_property_bool (d, "volume.disc.is_blank")) { - if (hal_device_get_property_bool (d, "volume.disc.is_blank")) + volume = g_hal_volume_new (G_VOLUME_MONITOR (monitor), d, monitor->pool, NULL, drive); + if (volume != NULL) { GFile *file; - file = g_file_new_for_uri ("burn:///"); - - mount = g_hal_mount_new_for_hal_device (G_VOLUME_MONITOR (monitor), - d, - file, - NULL, - NULL, - TRUE, - monitor->pool, - volume); - - g_object_unref (file); - } - else - { - char *uri; - char *encoded_device_file; - GFile *file; - GIcon *icon; - - encoded_device_file = g_uri_escape_string (hal_device_get_property_string (d, "block.device"), - NULL, FALSE); - uri = g_strdup_printf ("cdda://%s/", encoded_device_file); - file = g_file_new_for_uri (uri); - - icon = g_themed_icon_new ("media-optical-audio"); - mount = g_hal_mount_new_for_hal_device (G_VOLUME_MONITOR (monitor), d, file, - /* TODO: Connect to web service to get disc name */ - _("Audio Disc"), - icon, + NULL, + NULL, TRUE, monitor->pool, volume); - g_object_unref (icon); g_object_unref (file); - g_free (encoded_device_file); - g_free (uri); } + } + else + { + char *uri; + char *device_basename; + GFile *foreign_mount_root; + + /* the gvfsd-cdda backend uses URI's like these */ + device_basename = g_path_get_basename (hal_device_get_property_string (d, "block.device")); + uri = g_strdup_printf ("cdda://%s", device_basename); + foreign_mount_root = g_file_new_for_uri (uri); + g_free (device_basename); + g_free (uri); + + volume = g_hal_volume_new (G_VOLUME_MONITOR (monitor), d, monitor->pool, foreign_mount_root, drive); + g_object_unref (foreign_mount_root); + mount = NULL; + } + if (volume != NULL) + { + monitor->disc_volumes = g_list_prepend (monitor->disc_volumes, volume); + g_signal_emit_by_name (monitor, "volume_added", volume); + if (mount != NULL) { - monitor->disc_volumes = g_list_prepend (monitor->disc_volumes, volume); - g_signal_emit_by_name (monitor, "volume_added", volume); monitor->disc_mounts = g_list_prepend (monitor->disc_mounts, mount); g_signal_emit_by_name (monitor, "mount_added", mount); } - else - { - g_object_unref (volume); - } } } } g_list_free (added); g_list_free (removed); - g_list_foreach (monitor->last_optical_disc_devices, - (GFunc)g_object_unref, NULL); + g_list_foreach (monitor->last_optical_disc_devices, (GFunc)g_object_unref, NULL); g_list_free (monitor->last_optical_disc_devices); monitor->last_optical_disc_devices = new_optical_disc_devices; } diff --git a/hal/hal-device.c b/hal/hal-device.c index 42ad99a9..dcb375f6 100644 --- a/hal/hal-device.c +++ b/hal/hal-device.c @@ -23,6 +23,7 @@ #include <config.h> #include <glib/gi18n.h> #include "hal-device.h" +#include "hal-marshal.h" struct _HalDevicePrivate { @@ -33,6 +34,7 @@ struct _HalDevicePrivate enum { HAL_PROPERTY_CHANGED, + HAL_CONDITION, LAST_SIGNAL }; @@ -67,6 +69,17 @@ hal_device_class_init (HalDeviceClass *klass) g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); + + signals[HAL_CONDITION] = + g_signal_new ("hal_condition", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (HalDeviceClass, hal_condition), + NULL, NULL, + hal_marshal_VOID__STRING_STRING, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_STRING); } static void @@ -233,6 +246,9 @@ void _hal_device_hal_property_changed (HalDevice *device, const char *key); void +_hal_device_hal_condition (HalDevice *device, const char *name, const char *detail); + +void _hal_device_hal_property_changed (HalDevice *device, const char *key) { LibHalPropertySet *new_props; @@ -246,6 +262,12 @@ _hal_device_hal_property_changed (HalDevice *device, const char *key) } } +void +_hal_device_hal_condition (HalDevice *device, const char *name, const char *detail) +{ + g_signal_emit (device, signals[HAL_CONDITION], 0, name, detail); +} + const char * hal_device_get_udi (HalDevice *device) { diff --git a/hal/hal-device.h b/hal/hal-device.h index 70f063fc..6a5dd705 100644 --- a/hal/hal-device.h +++ b/hal/hal-device.h @@ -55,6 +55,7 @@ struct _HalDeviceClass /* signals */ void (*hal_property_changed) (HalDevice *device, const char *key); + void (*hal_condition) (HalDevice *device, const char *name, const char *detail); }; diff --git a/hal/hal-marshal.list b/hal/hal-marshal.list index 2093925d..52c82338 100644 --- a/hal/hal-marshal.list +++ b/hal/hal-marshal.list @@ -1 +1,3 @@ VOID:OBJECT,STRING +VOID:OBJECT,STRING,STRING +VOID:STRING,STRING diff --git a/hal/hal-pool.c b/hal/hal-pool.c index eab17cb9..2377e09b 100644 --- a/hal/hal-pool.c +++ b/hal/hal-pool.c @@ -34,6 +34,7 @@ enum { DEVICE_ADDED, DEVICE_REMOVED, DEVICE_PROPERTY_CHANGED, + DEVICE_CONDITION, LAST_SIGNAL }; @@ -105,6 +106,18 @@ hal_pool_class_init (HalPoolClass *klass) G_TYPE_NONE, 2, HAL_TYPE_DEVICE, G_TYPE_STRING); + + signals[DEVICE_CONDITION] = + g_signal_new ("device_condition", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (HalPoolClass, device_condition), + NULL, NULL, + hal_marshal_VOID__OBJECT_STRING_STRING, + G_TYPE_NONE, 3, + HAL_TYPE_DEVICE, + G_TYPE_STRING, + G_TYPE_STRING); } static void @@ -197,6 +210,9 @@ _hal_device_removed (LibHalContext *hal_ctx, const char *udi) void _hal_device_hal_property_changed (HalDevice *device, const char *key); +void +_hal_device_hal_condition (HalDevice *device, const char *name, const char *detail); + static void _hal_property_modified (LibHalContext *ctx, const char *udi, @@ -217,6 +233,25 @@ _hal_property_modified (LibHalContext *ctx, } } +static void +_hal_condition (LibHalContext *ctx, + const char *udi, + const char *condition_name, + const char *condition_detail) +{ + HalPool *pool; + HalDevice *device; + + pool = HAL_POOL (libhal_ctx_get_user_data (ctx)); + + device = hal_pool_get_device_by_udi (pool, udi); + if (device != NULL) + { + _hal_device_hal_condition (device, condition_name, condition_detail); + g_signal_emit (pool, signals[DEVICE_CONDITION], 0, device, condition_name, condition_detail); + } +} + LibHalContext * hal_pool_get_hal_ctx (HalPool *pool) { @@ -287,6 +322,7 @@ hal_pool_new (const char *cap_only) libhal_ctx_set_device_added (hal_ctx, _hal_device_added); libhal_ctx_set_device_removed (hal_ctx, _hal_device_removed); libhal_ctx_set_device_property_modified (hal_ctx, _hal_property_modified); + libhal_ctx_set_device_condition (hal_ctx, _hal_condition); libhal_ctx_set_user_data (hal_ctx, pool); #ifdef HAVE_HAL_FAST_INIT diff --git a/hal/hal-pool.h b/hal/hal-pool.h index ddfa21c2..0b441f36 100644 --- a/hal/hal-pool.h +++ b/hal/hal-pool.h @@ -57,6 +57,7 @@ struct _HalPoolClass void (*device_added) (HalPool *pool, HalDevice *device); void (*device_removed) (HalPool *pool, HalDevice *device); void (*device_property_changed) (HalPool *pool, HalDevice *device, const char *key); + void (*device_condition) (HalPool *pool, HalDevice *device, const char *name, const char *detail); }; GType hal_pool_get_type (void); diff --git a/programs/Makefile.am b/programs/Makefile.am index 5f8bf118..ee12c56a 100644 --- a/programs/Makefile.am +++ b/programs/Makefile.am @@ -23,6 +23,10 @@ bin_PROGRAMS = \ gvfs-monitor-dir \ $(NULL) +bin_SCRIPTS = \ + gvfs-less \ + $(NULL) + gvfs_cat_SOURCES = gvfs-cat.c gvfs_cat_LDADD = $(libraries) diff --git a/programs/gvfs-mount.c b/programs/gvfs-mount.c index 86bd0ed5..7b9c29ef 100644 --- a/programs/gvfs-mount.c +++ b/programs/gvfs-mount.c @@ -34,10 +34,12 @@ static GMainLoop *main_loop; static gboolean mount_mountable = FALSE; +static gboolean mount_unmount = FALSE; static GOptionEntry entries[] = { { "mountable", 'm', 0, G_OPTION_ARG_NONE, &mount_mountable, "Mount as mountable", NULL }, + { "unmount", 'u', 0, G_OPTION_ARG_NONE, &mount_unmount, "Unmount", NULL}, { NULL } }; @@ -170,6 +172,48 @@ mount (GFile *file) outstanding_mounts++; } +static void +unmount_done_cb (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + gboolean succeeded; + GError *error = NULL; + + succeeded = g_mount_unmount_finish (G_MOUNT (object), res, &error); + + g_object_unref (G_MOUNT (object)); + + if (!succeeded) + g_print ("Error unmounting mount: %s\n", error->message); + + outstanding_mounts--; + + if (outstanding_mounts == 0) + g_main_loop_quit (main_loop); +} + +static void +unmount (GFile *file) +{ + GMount *mount; + GError *error = NULL; + + if (file == NULL) + return; + + mount = g_file_find_enclosing_mount (file, NULL, &error); + if (mount == NULL) + { + g_print ("Error finding enclosing mount: %s\n", error->message); + return; + } + + g_mount_unmount (mount, NULL, unmount_done_cb, NULL); + + outstanding_mounts++; +} + int main (int argc, char *argv[]) { @@ -193,8 +237,11 @@ main (int argc, char *argv[]) for (i = 1; i < argc; i++) { file = g_file_new_for_commandline_arg (argv[i]); - mount (file); - g_object_unref (file); + if (mount_unmount) + unmount (file); + else + mount (file); + g_object_unref (file); } } |