5160 lines
156 KiB
Diff
5160 lines
156 KiB
Diff
Index: configure.ac
|
|
===================================================================
|
|
--- configure.ac (revision 2286)
|
|
+++ configure.ac (working copy)
|
|
@@ -198,6 +198,31 @@
|
|
|
|
AM_CONDITIONAL(USE_GCONF, [test "$msg_gconf" = "yes"])
|
|
|
|
+dnl ************************************
|
|
+dnl *** Check for gnome-disk-utility ***
|
|
+dnl ************************************
|
|
+
|
|
+dnl TODO: when there is a more stable version of gdu available, turn this on by default
|
|
+AC_ARG_ENABLE(gdu, [ --enable-gdu build with gdu support])
|
|
+msg_gdu=no
|
|
+GDU_LIBS=
|
|
+GDU_CFLAGS=
|
|
+GDU_REQUIRED=0.2
|
|
+
|
|
+if test "x$enable_gdu" = "xyes"; then
|
|
+ PKG_CHECK_EXISTS([gdu >= $GDU_REQUIRED], msg_gdu=yes)
|
|
+
|
|
+ if test "x$msg_gdu" == "xyes"; then
|
|
+ PKG_CHECK_MODULES([GDU],[gdu >= $GDU_REQUIRED])
|
|
+ AC_DEFINE(HAVE_GDU, 1, [Define to 1 if gnome-disk-utility is available])
|
|
+ fi
|
|
+fi
|
|
+
|
|
+AC_SUBST(GDU_LIBS)
|
|
+AC_SUBST(GDU_CFLAGS)
|
|
+
|
|
+AM_CONDITIONAL(USE_GDU, [test "$msg_gdu" = "yes"])
|
|
+
|
|
dnl **********************
|
|
dnl *** Check for HAL ***
|
|
dnl **********************
|
|
@@ -558,6 +583,7 @@
|
|
monitor/Makefile
|
|
monitor/proxy/Makefile
|
|
monitor/hal/Makefile
|
|
+monitor/gdu/Makefile
|
|
monitor/gphoto2/Makefile
|
|
gconf/Makefile
|
|
programs/Makefile
|
|
@@ -579,7 +605,8 @@
|
|
archive support: $msg_archive
|
|
GConf support: $msg_gconf
|
|
DNS-SD support: $msg_avahi
|
|
- Use HAL for volume monitor: $msg_hal (with fast init path: $have_hal_fast_init)
|
|
+ Build HAL volume monitor: $msg_hal (with fast init path: $have_hal_fast_init)
|
|
+ Build GDU volume monitor: $msg_gdu
|
|
GNOME Keyring support: $msg_keyring
|
|
Bash-completion support: $msg_bash_completion
|
|
"
|
|
Index: monitor/Makefile.am
|
|
===================================================================
|
|
--- monitor/Makefile.am (revision 2286)
|
|
+++ monitor/Makefile.am (working copy)
|
|
@@ -5,6 +5,10 @@
|
|
SUBDIRS += hal
|
|
endif
|
|
|
|
+if USE_GDU
|
|
+SUBDIRS += gdu
|
|
+endif
|
|
+
|
|
if USE_GPHOTO2
|
|
SUBDIRS += gphoto2
|
|
endif
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/gdu-volume-monitor-daemon.c 2009-03-01 23:47:15.000000000 -0500
|
|
@@ -0,0 +1,46 @@
|
|
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
+/* gvfs - extensions for gio
|
|
+ *
|
|
+ * Copyright (C) 2006-2009 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: David Zeuthen <davidz@redhat.com>
|
|
+ */
|
|
+
|
|
+#include <config.h>
|
|
+
|
|
+#include <glib.h>
|
|
+#include <glib/gi18n-lib.h>
|
|
+#include <gmodule.h>
|
|
+#include <gio/gio.h>
|
|
+
|
|
+#include <gvfsproxyvolumemonitordaemon.h>
|
|
+
|
|
+#include "ggduvolumemonitor.h"
|
|
+
|
|
+int
|
|
+main (int argc, char *argv[])
|
|
+{
|
|
+ g_vfs_proxy_volume_monitor_daemon_init ();
|
|
+
|
|
+ g_set_application_name (_("GVfs GDU Volume Monitor"));
|
|
+
|
|
+ return g_vfs_proxy_volume_monitor_daemon_main (argc,
|
|
+ argv,
|
|
+ "org.gtk.Private.GduVolumeMonitor",
|
|
+ G_TYPE_GDU_VOLUME_MONITOR);
|
|
+}
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/ggdudrive.c 2009-03-01 23:48:33.000000000 -0500
|
|
@@ -0,0 +1,691 @@
|
|
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
+/* gvfs - extensions for gio
|
|
+ *
|
|
+ * Copyright (C) 2006-2009 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: David Zeuthen <davidz@redhat.com>
|
|
+ */
|
|
+
|
|
+#include <config.h>
|
|
+
|
|
+#include <string.h>
|
|
+#include <sys/wait.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include <glib.h>
|
|
+#include <glib/gi18n-lib.h>
|
|
+
|
|
+#include "ggduvolumemonitor.h"
|
|
+#include "ggdudrive.h"
|
|
+#include "ggduvolume.h"
|
|
+
|
|
+struct _GGduDrive {
|
|
+ GObject parent;
|
|
+
|
|
+ GVolumeMonitor *volume_monitor; /* owned by volume monitor */
|
|
+ GList *volumes; /* entries in list are owned by volume_monitor */
|
|
+
|
|
+ GduPresentable *presentable;
|
|
+
|
|
+ /* the following members need to be set upon construction */
|
|
+ GIcon *icon;
|
|
+ gchar *name;
|
|
+ gchar *device_file;
|
|
+ gboolean is_media_removable;
|
|
+ gboolean has_media;
|
|
+ gboolean can_eject;
|
|
+ gboolean can_poll_for_media;
|
|
+ gboolean is_media_check_automatic;
|
|
+ time_t time_of_last_media_insertion;
|
|
+};
|
|
+
|
|
+static void g_gdu_drive_drive_iface_init (GDriveIface *iface);
|
|
+
|
|
+G_DEFINE_TYPE_EXTENDED (GGduDrive, g_gdu_drive, G_TYPE_OBJECT, 0,
|
|
+ G_IMPLEMENT_INTERFACE (G_TYPE_DRIVE,
|
|
+ g_gdu_drive_drive_iface_init))
|
|
+
|
|
+static void presentable_changed (GduPresentable *presentable,
|
|
+ GGduDrive *drive);
|
|
+
|
|
+static void presentable_job_changed (GduPresentable *presentable,
|
|
+ GGduDrive *drive);
|
|
+
|
|
+static void
|
|
+g_gdu_drive_finalize (GObject *object)
|
|
+{
|
|
+ GList *l;
|
|
+ GGduDrive *drive;
|
|
+
|
|
+ drive = G_GDU_DRIVE (object);
|
|
+
|
|
+ for (l = drive->volumes; l != NULL; l = l->next)
|
|
+ {
|
|
+ GGduVolume *volume = l->data;
|
|
+ g_gdu_volume_unset_drive (volume, drive);
|
|
+ }
|
|
+
|
|
+ if (drive->presentable != NULL)
|
|
+ {
|
|
+ g_signal_handlers_disconnect_by_func (drive->presentable, presentable_changed, drive);
|
|
+ g_signal_handlers_disconnect_by_func (drive->presentable, presentable_job_changed, drive);
|
|
+ g_object_unref (drive->presentable);
|
|
+ }
|
|
+
|
|
+ if (drive->icon != NULL)
|
|
+ g_object_unref (drive->icon);
|
|
+ g_free (drive->name);
|
|
+ g_free (drive->device_file);
|
|
+
|
|
+ if (G_OBJECT_CLASS (g_gdu_drive_parent_class)->finalize)
|
|
+ (*G_OBJECT_CLASS (g_gdu_drive_parent_class)->finalize) (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_drive_class_init (GGduDriveClass *klass)
|
|
+{
|
|
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
+
|
|
+ gobject_class->finalize = g_gdu_drive_finalize;
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_drive_init (GGduDrive *gdu_drive)
|
|
+{
|
|
+}
|
|
+
|
|
+static void
|
|
+emit_changed (GGduDrive *drive)
|
|
+{
|
|
+ g_signal_emit_by_name (drive, "changed");
|
|
+ g_signal_emit_by_name (drive->volume_monitor, "drive_changed", drive);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+update_drive (GGduDrive *drive)
|
|
+{
|
|
+ GduDevice *device;
|
|
+ gboolean changed;
|
|
+ GIcon *old_icon;
|
|
+ gchar *old_name;
|
|
+ gchar *old_device_file;
|
|
+ gboolean old_is_media_removable;
|
|
+ gboolean old_has_media;
|
|
+ gboolean old_can_eject;
|
|
+ gboolean old_is_media_check_automatic;
|
|
+ gboolean old_can_poll_for_media;
|
|
+
|
|
+ /* save old values */
|
|
+ old_is_media_removable = drive->is_media_removable;
|
|
+ old_has_media = drive->has_media;
|
|
+ old_can_eject = drive->can_eject;
|
|
+ old_can_poll_for_media = drive->can_poll_for_media;
|
|
+ old_is_media_check_automatic = drive->is_media_check_automatic;
|
|
+
|
|
+ old_name = g_strdup (drive->name);
|
|
+ old_device_file = g_strdup (drive->device_file);
|
|
+ old_icon = drive->icon != NULL ? g_object_ref (drive->icon) : NULL;
|
|
+
|
|
+ /* in with the new */
|
|
+ device = gdu_presentable_get_device (drive->presentable);
|
|
+
|
|
+ if (drive->icon != NULL)
|
|
+ g_object_unref (drive->icon);
|
|
+ drive->icon = gdu_presentable_get_icon (drive->presentable);
|
|
+
|
|
+ g_free (drive->name);
|
|
+ drive->name = gdu_presentable_get_name (drive->presentable);
|
|
+
|
|
+ /* the GduDevice for an activatable drive (such as RAID) is NULL if the drive is not activated */
|
|
+ if (device == NULL)
|
|
+ {
|
|
+ g_free (drive->device_file);
|
|
+ drive->device_file = NULL;
|
|
+ drive->is_media_removable = TRUE;
|
|
+ drive->has_media = TRUE;
|
|
+ drive->can_eject = FALSE;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_free (drive->device_file);
|
|
+ drive->device_file = g_strdup (gdu_device_get_device_file (device));
|
|
+ drive->is_media_removable = gdu_device_is_removable (device);
|
|
+ drive->has_media = gdu_device_is_media_available (device);
|
|
+ drive->can_eject = gdu_device_drive_get_is_media_ejectable (device) || gdu_device_drive_get_requires_eject (device);
|
|
+ drive->is_media_check_automatic = gdu_device_is_media_change_detected (device);
|
|
+ drive->can_poll_for_media = TRUE;
|
|
+ }
|
|
+
|
|
+ if (device != NULL)
|
|
+ g_object_unref (device);
|
|
+
|
|
+ if (drive->has_media != old_has_media)
|
|
+ drive->time_of_last_media_insertion = time (NULL);
|
|
+
|
|
+ /* compute whether something changed */
|
|
+ changed = !((old_is_media_removable == drive->is_media_removable) &&
|
|
+ (old_has_media == drive->has_media) &&
|
|
+ (old_can_eject == drive->can_eject) &&
|
|
+ (old_is_media_check_automatic == drive->is_media_check_automatic) &&
|
|
+ (old_can_poll_for_media == drive->can_poll_for_media) &&
|
|
+ (g_strcmp0 (old_name, drive->name) == 0) &&
|
|
+ (g_strcmp0 (old_device_file, drive->device_file) == 0) &&
|
|
+ g_icon_equal (old_icon, drive->icon)
|
|
+ );
|
|
+
|
|
+ /* free old values */
|
|
+ g_free (old_name);
|
|
+ g_free (old_device_file);
|
|
+ if (old_icon != NULL)
|
|
+ g_object_unref (old_icon);
|
|
+
|
|
+ /*g_debug ("in update_drive(); has_media=%d changed=%d", drive->has_media, changed);*/
|
|
+
|
|
+ return changed;
|
|
+}
|
|
+
|
|
+static void
|
|
+presentable_changed (GduPresentable *presentable,
|
|
+ GGduDrive *drive)
|
|
+{
|
|
+ /*g_debug ("drive: presentable_changed: %p: %s", drive, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/
|
|
+ if (update_drive (drive))
|
|
+ emit_changed (drive);
|
|
+}
|
|
+
|
|
+static void
|
|
+presentable_job_changed (GduPresentable *presentable,
|
|
+ GGduDrive *drive)
|
|
+{
|
|
+ /*g_debug ("drive: presentable_job_changed: %p: %s", drive, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/
|
|
+ if (update_drive (drive))
|
|
+ emit_changed (drive);
|
|
+}
|
|
+
|
|
+GGduDrive *
|
|
+g_gdu_drive_new (GVolumeMonitor *volume_monitor,
|
|
+ GduPresentable *presentable)
|
|
+{
|
|
+ GGduDrive *drive;
|
|
+
|
|
+ drive = g_object_new (G_TYPE_GDU_DRIVE, NULL);
|
|
+ drive->volume_monitor = volume_monitor;
|
|
+ g_object_add_weak_pointer (G_OBJECT (volume_monitor), (gpointer) &(drive->volume_monitor));
|
|
+
|
|
+ drive->presentable = g_object_ref (presentable);
|
|
+
|
|
+ drive->time_of_last_media_insertion = time (NULL);
|
|
+
|
|
+ g_signal_connect (drive->presentable, "changed", G_CALLBACK (presentable_changed), drive);
|
|
+ g_signal_connect (drive->presentable, "job-changed", G_CALLBACK (presentable_job_changed), drive);
|
|
+
|
|
+ update_drive (drive);
|
|
+
|
|
+ return drive;
|
|
+}
|
|
+
|
|
+void
|
|
+g_gdu_drive_disconnected (GGduDrive *drive)
|
|
+{
|
|
+ GList *l, *volumes;
|
|
+
|
|
+ volumes = drive->volumes;
|
|
+ drive->volumes = NULL;
|
|
+
|
|
+ for (l = volumes; l != NULL; l = l->next)
|
|
+ {
|
|
+ GGduVolume *volume = l->data;
|
|
+ g_gdu_volume_unset_drive (volume, drive);
|
|
+ }
|
|
+
|
|
+ g_list_free (volumes);
|
|
+}
|
|
+
|
|
+void
|
|
+g_gdu_drive_set_volume (GGduDrive *drive,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ if (g_list_find (drive->volumes, volume) == NULL)
|
|
+ {
|
|
+ drive->volumes = g_list_prepend (drive->volumes, volume);
|
|
+ emit_changed (drive);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+g_gdu_drive_unset_volume (GGduDrive *drive,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ GList *l;
|
|
+
|
|
+ l = g_list_find (drive->volumes, volume);
|
|
+ if (l != NULL)
|
|
+ {
|
|
+ drive->volumes = g_list_delete_link (drive->volumes, l);
|
|
+ emit_changed (drive);
|
|
+ }
|
|
+}
|
|
+
|
|
+static GIcon *
|
|
+g_gdu_drive_get_icon (GDrive *_drive)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ return drive->icon != NULL ? g_object_ref (drive->icon) : NULL;
|
|
+}
|
|
+
|
|
+static char *
|
|
+g_gdu_drive_get_name (GDrive *_drive)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ return g_strdup (drive->name);
|
|
+}
|
|
+
|
|
+static GList *
|
|
+g_gdu_drive_get_volumes (GDrive *_drive)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ GList *l;
|
|
+
|
|
+ l = g_list_copy (drive->volumes);
|
|
+ g_list_foreach (l, (GFunc) g_object_ref, NULL);
|
|
+
|
|
+ return l;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_drive_has_volumes (GDrive *_drive)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (drive);
|
|
+ gboolean res;
|
|
+
|
|
+ res = drive->volumes != NULL;
|
|
+
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_drive_is_media_removable (GDrive *_drive)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ return drive->is_media_removable;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_drive_has_media (GDrive *_drive)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ return drive->has_media;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_drive_is_media_check_automatic (GDrive *_drive)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ return drive->is_media_check_automatic;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_drive_can_eject (GDrive *_drive)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ return drive->can_eject;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_drive_can_poll_for_media (GDrive *_drive)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ return drive->can_poll_for_media;
|
|
+}
|
|
+
|
|
+static void
|
|
+eject_cb (GduDevice *device,
|
|
+ GError *error,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
|
|
+
|
|
+ if (error != NULL)
|
|
+ {
|
|
+ /* We could handle PolicyKit integration here but this action is allowed by default
|
|
+ * and this won't be needed when porting to PolicyKit 1.0 anyway
|
|
+ */
|
|
+ g_simple_async_result_set_from_error (simple, error);
|
|
+ g_error_free (error);
|
|
+ }
|
|
+
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+g_gdu_drive_eject_do (GDrive *_drive,
|
|
+ GCancellable *cancellable,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ GSimpleAsyncResult *simple;
|
|
+ GduDevice *device;
|
|
+
|
|
+ device = gdu_presentable_get_device (drive->presentable);
|
|
+ if (device == NULL)
|
|
+ {
|
|
+ simple = g_simple_async_result_new_error (G_OBJECT (drive),
|
|
+ callback,
|
|
+ user_data,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED,
|
|
+ "Drive is activatable and not running");
|
|
+ g_simple_async_result_complete_in_idle (simple);
|
|
+ g_object_unref (simple);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ simple = g_simple_async_result_new (G_OBJECT (drive),
|
|
+ callback,
|
|
+ user_data,
|
|
+ NULL);
|
|
+
|
|
+ gdu_device_op_drive_eject (device, eject_cb, simple);
|
|
+ g_object_unref (device);
|
|
+ }
|
|
+}
|
|
+
|
|
+typedef struct {
|
|
+ GDrive *drive;
|
|
+ GAsyncReadyCallback callback;
|
|
+ gpointer user_data;
|
|
+ GCancellable *cancellable;
|
|
+ GMountUnmountFlags flags;
|
|
+
|
|
+ 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 unmounted %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_gdu_drive_eject_do (data->drive,
|
|
+ data->cancellable,
|
|
+ data->callback,
|
|
+ data->user_data);
|
|
+
|
|
+ g_object_unref (data->drive);
|
|
+ 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->flags,
|
|
+ data->cancellable,
|
|
+ _eject_unmount_mounts_cb,
|
|
+ data);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_drive_eject (GDrive *drive,
|
|
+ GMountUnmountFlags flags,
|
|
+ GCancellable *cancellable,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduDrive *gdu_drive = G_GDU_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 = g_object_ref (drive);
|
|
+ data->cancellable = cancellable;
|
|
+ data->callback = callback;
|
|
+ data->user_data = user_data;
|
|
+ data->flags = flags;
|
|
+
|
|
+ for (l = gdu_drive->volumes; l != NULL; l = l->next)
|
|
+ {
|
|
+ GGduVolume *volume = l->data;
|
|
+ GMount *mount;
|
|
+
|
|
+ mount = g_volume_get_mount (G_VOLUME (volume));
|
|
+ if (mount != NULL && g_mount_can_unmount (mount))
|
|
+ data->pending_mounts = g_list_prepend (data->pending_mounts, g_object_ref (mount));
|
|
+ }
|
|
+
|
|
+ _eject_unmount_mounts (data);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_drive_eject_finish (GDrive *drive,
|
|
+ GAsyncResult *result,
|
|
+ GError **error)
|
|
+{
|
|
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
|
|
+}
|
|
+
|
|
+static void
|
|
+poll_media_cb (GduDevice *device,
|
|
+ GError *error,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
|
|
+
|
|
+ if (error != NULL)
|
|
+ {
|
|
+ /* We could handle PolicyKit integration here but this action is allowed by default
|
|
+ * and this won't be needed when porting to PolicyKit 1.0 anyway
|
|
+ */
|
|
+ g_simple_async_result_set_from_error (simple, error);
|
|
+ g_error_free (error);
|
|
+ }
|
|
+
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_drive_poll_for_media (GDrive *_drive,
|
|
+ GCancellable *cancellable,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ GSimpleAsyncResult *simple;
|
|
+ GduDevice *device;
|
|
+
|
|
+ device = gdu_presentable_get_device (drive->presentable);
|
|
+ if (device == NULL)
|
|
+ {
|
|
+ simple = g_simple_async_result_new_error (G_OBJECT (drive),
|
|
+ callback,
|
|
+ user_data,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED,
|
|
+ "Device is not active");
|
|
+ g_simple_async_result_complete_in_idle (simple);
|
|
+ g_object_unref (simple);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ simple = g_simple_async_result_new (G_OBJECT (drive),
|
|
+ callback,
|
|
+ user_data,
|
|
+ NULL);
|
|
+
|
|
+ gdu_device_op_drive_poll_media (device, poll_media_cb, simple);
|
|
+ g_object_unref (device);
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_drive_poll_for_media_finish (GDrive *drive,
|
|
+ GAsyncResult *result,
|
|
+ GError **error)
|
|
+{
|
|
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
|
|
+}
|
|
+
|
|
+static char *
|
|
+g_gdu_drive_get_identifier (GDrive *_drive,
|
|
+ const char *kind)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ gchar *id;
|
|
+
|
|
+ id = NULL;
|
|
+
|
|
+ if (drive->device_file != NULL)
|
|
+ {
|
|
+ if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == 0)
|
|
+ id = g_strdup (drive->device_file);
|
|
+ }
|
|
+
|
|
+ return id;
|
|
+}
|
|
+
|
|
+static char **
|
|
+g_gdu_drive_enumerate_identifiers (GDrive *_drive)
|
|
+{
|
|
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
|
|
+ GPtrArray *p;
|
|
+
|
|
+ p = g_ptr_array_new ();
|
|
+ if (drive->device_file != NULL)
|
|
+ g_ptr_array_add (p, g_strdup (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE));
|
|
+ g_ptr_array_add (p, NULL);
|
|
+
|
|
+ return (gchar **) g_ptr_array_free (p, FALSE);
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_drive_drive_iface_init (GDriveIface *iface)
|
|
+{
|
|
+ iface->get_name = g_gdu_drive_get_name;
|
|
+ iface->get_icon = g_gdu_drive_get_icon;
|
|
+ iface->has_volumes = g_gdu_drive_has_volumes;
|
|
+ iface->get_volumes = g_gdu_drive_get_volumes;
|
|
+ iface->is_media_removable = g_gdu_drive_is_media_removable;
|
|
+ iface->has_media = g_gdu_drive_has_media;
|
|
+ iface->is_media_check_automatic = g_gdu_drive_is_media_check_automatic;
|
|
+ iface->can_eject = g_gdu_drive_can_eject;
|
|
+ iface->can_poll_for_media = g_gdu_drive_can_poll_for_media;
|
|
+ iface->eject = g_gdu_drive_eject;
|
|
+ iface->eject_finish = g_gdu_drive_eject_finish;
|
|
+ iface->poll_for_media = g_gdu_drive_poll_for_media;
|
|
+ iface->poll_for_media_finish = g_gdu_drive_poll_for_media_finish;
|
|
+ iface->get_identifier = g_gdu_drive_get_identifier;
|
|
+ iface->enumerate_identifiers = g_gdu_drive_enumerate_identifiers;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+g_gdu_drive_has_device_file (GGduDrive *drive,
|
|
+ const gchar *device_file)
|
|
+{
|
|
+ return g_strcmp0 (drive->device_file, device_file) == 0;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+g_gdu_drive_has_presentable (GGduDrive *drive,
|
|
+ GduPresentable *presentable)
|
|
+{
|
|
+ return gdu_presentable_get_id (drive->presentable) == gdu_presentable_get_id (presentable);
|
|
+}
|
|
+
|
|
+time_t
|
|
+g_gdu_drive_get_time_of_last_media_insertion (GGduDrive *drive)
|
|
+{
|
|
+ return drive->time_of_last_media_insertion;
|
|
+}
|
|
+
|
|
+GduPresentable *
|
|
+g_gdu_drive_get_presentable (GGduDrive *drive)
|
|
+{
|
|
+ return drive->presentable;
|
|
+}
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/ggdudrive.h 2009-03-01 23:48:26.000000000 -0500
|
|
@@ -0,0 +1,69 @@
|
|
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
+/* gvfs - extensions for gio
|
|
+ *
|
|
+ * Copyright (C) 2006-2009 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: David Zeuthen <davidz@redhat.com>
|
|
+ */
|
|
+
|
|
+#ifndef __G_GDU_DRIVE_H__
|
|
+#define __G_GDU_DRIVE_H__
|
|
+
|
|
+#include <glib-object.h>
|
|
+#include <gio/gio.h>
|
|
+
|
|
+#include "ggduvolumemonitor.h"
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+#define G_TYPE_GDU_DRIVE (g_gdu_drive_get_type ())
|
|
+#define G_GDU_DRIVE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_GDU_DRIVE, GGduDrive))
|
|
+#define G_GDU_DRIVE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_GDU_DRIVE, GGduDriveClass))
|
|
+#define G_IS_GDU_DRIVE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_GDU_DRIVE))
|
|
+#define G_IS_GDU_DRIVE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_GDU_DRIVE))
|
|
+
|
|
+typedef struct _GGduDriveClass GGduDriveClass;
|
|
+
|
|
+struct _GGduDriveClass {
|
|
+ GObjectClass parent_class;
|
|
+};
|
|
+
|
|
+GType g_gdu_drive_get_type (void) G_GNUC_CONST;
|
|
+
|
|
+GGduDrive *g_gdu_drive_new (GVolumeMonitor *volume_monitor,
|
|
+ GduPresentable *presentable);
|
|
+void g_gdu_drive_set_volume (GGduDrive *drive,
|
|
+ GGduVolume *volume);
|
|
+void g_gdu_drive_unset_volume (GGduDrive *drive,
|
|
+ GGduVolume *volume);
|
|
+void g_gdu_drive_disconnected (GGduDrive *drive);
|
|
+gboolean g_gdu_drive_has_device_file (GGduDrive *drive,
|
|
+ const gchar *device_file);
|
|
+time_t g_gdu_drive_get_time_of_last_media_insertion (GGduDrive *drive);
|
|
+
|
|
+gboolean g_gdu_drive_has_presentable (GGduDrive *drive,
|
|
+ GduPresentable *presentable);
|
|
+
|
|
+GduPresentable *g_gdu_drive_get_presentable (GGduDrive *drive);
|
|
+
|
|
+
|
|
+char * _drive_get_icon (GduDevice *d);
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __G_GDU_DRIVE_H__ */
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/ggdumount.c 2009-03-02 14:18:23.000000000 -0500
|
|
@@ -0,0 +1,958 @@
|
|
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
+/* gvfs - extensions for gio
|
|
+ *
|
|
+ * Copyright (C) 2006-2009 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: David Zeuthen <davidz@redhat.com>
|
|
+ */
|
|
+
|
|
+#include <config.h>
|
|
+
|
|
+#include <string.h>
|
|
+#include <sys/wait.h>
|
|
+#include <unistd.h>
|
|
+#include <stdio.h>
|
|
+
|
|
+#include <glib.h>
|
|
+#include <glib/gi18n-lib.h>
|
|
+#include <gio/gio.h>
|
|
+
|
|
+#include <gvfsmountinfo.h>
|
|
+
|
|
+#include "ggduvolumemonitor.h"
|
|
+#include "ggdumount.h"
|
|
+#include "ggduvolume.h"
|
|
+
|
|
+struct _GGduMount
|
|
+{
|
|
+ GObject parent;
|
|
+
|
|
+ GVolumeMonitor *volume_monitor; /* owned by volume monitor */
|
|
+ GGduVolume *volume; /* owned by volume monitor */
|
|
+
|
|
+ /* the following members need to be set upon construction */
|
|
+ GFile *root;
|
|
+ GIcon *icon;
|
|
+ gchar *name;
|
|
+ gchar *uuid;
|
|
+ gchar *device_file;
|
|
+ gchar *mount_path;
|
|
+ gboolean can_unmount;
|
|
+
|
|
+ gchar *mount_entry_name;
|
|
+ GIcon *mount_entry_icon;
|
|
+
|
|
+ gboolean is_burn_mount;
|
|
+
|
|
+ GIcon *autorun_icon;
|
|
+ gboolean searched_for_autorun;
|
|
+
|
|
+ gchar *xdg_volume_info_name;
|
|
+ GIcon *xdg_volume_info_icon;
|
|
+ gboolean searched_for_xdg_volume_info;
|
|
+};
|
|
+
|
|
+static gboolean update_mount (GGduMount *mount);
|
|
+
|
|
+static void g_gdu_mount_mount_iface_init (GMountIface *iface);
|
|
+
|
|
+G_DEFINE_TYPE_EXTENDED (GGduMount, g_gdu_mount, G_TYPE_OBJECT, 0,
|
|
+ G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT,
|
|
+ g_gdu_mount_mount_iface_init))
|
|
+
|
|
+static void
|
|
+volume_changed (GVolume *volume,
|
|
+ gpointer user_data);
|
|
+
|
|
+static void
|
|
+g_gdu_mount_finalize (GObject *object)
|
|
+{
|
|
+ GGduMount *mount;
|
|
+
|
|
+ mount = G_GDU_MOUNT (object);
|
|
+
|
|
+ if (mount->volume != NULL)
|
|
+ {
|
|
+ g_signal_handlers_disconnect_by_func (mount->volume, volume_changed, mount);
|
|
+ g_gdu_volume_unset_mount (mount->volume, mount);
|
|
+ }
|
|
+
|
|
+ if (mount->root != NULL)
|
|
+ g_object_unref (mount->root);
|
|
+ if (mount->icon != NULL)
|
|
+ g_object_unref (mount->icon);
|
|
+ g_free (mount->name);
|
|
+ g_free (mount->uuid);
|
|
+ g_free (mount->device_file);
|
|
+ g_free (mount->mount_path);
|
|
+
|
|
+ g_free (mount->mount_entry_name);
|
|
+ if (mount->mount_entry_icon != NULL)
|
|
+ g_object_unref (mount->mount_entry_icon);
|
|
+
|
|
+ if (mount->autorun_icon != NULL)
|
|
+ g_object_unref (mount->autorun_icon);
|
|
+
|
|
+ g_free (mount->xdg_volume_info_name);
|
|
+ if (mount->xdg_volume_info_icon != NULL)
|
|
+ g_object_unref (mount->xdg_volume_info_icon);
|
|
+
|
|
+ if (G_OBJECT_CLASS (g_gdu_mount_parent_class)->finalize)
|
|
+ (*G_OBJECT_CLASS (g_gdu_mount_parent_class)->finalize) (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_mount_class_init (GGduMountClass *klass)
|
|
+{
|
|
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
+
|
|
+ gobject_class->finalize = g_gdu_mount_finalize;
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_mount_init (GGduMount *mount)
|
|
+{
|
|
+}
|
|
+
|
|
+static void
|
|
+emit_changed (GGduMount *mount)
|
|
+{
|
|
+ g_signal_emit_by_name (mount, "changed");
|
|
+ g_signal_emit_by_name (mount->volume_monitor, "mount_changed", mount);
|
|
+}
|
|
+
|
|
+static void
|
|
+got_autorun_info_cb (GObject *source_object,
|
|
+ GAsyncResult *res,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (user_data);
|
|
+
|
|
+ mount->autorun_icon = g_vfs_mount_info_query_autorun_info_finish (G_FILE (source_object),
|
|
+ res,
|
|
+ NULL);
|
|
+
|
|
+ if (update_mount (mount))
|
|
+ emit_changed (mount);
|
|
+
|
|
+ g_object_unref (mount);
|
|
+}
|
|
+
|
|
+static void
|
|
+got_xdg_volume_info_cb (GObject *source_object,
|
|
+ GAsyncResult *res,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (user_data);
|
|
+
|
|
+ mount->xdg_volume_info_icon = g_vfs_mount_info_query_xdg_volume_info_finish (G_FILE (source_object),
|
|
+ res,
|
|
+ &(mount->xdg_volume_info_name),
|
|
+ NULL);
|
|
+ if (update_mount (mount))
|
|
+ emit_changed (mount);
|
|
+
|
|
+ g_object_unref (mount);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+update_mount (GGduMount *mount)
|
|
+{
|
|
+ gboolean changed;
|
|
+ gboolean old_can_unmount;
|
|
+ gchar *old_name;
|
|
+ GIcon *old_icon;
|
|
+
|
|
+ /* save old values */
|
|
+ old_can_unmount = mount->can_unmount;
|
|
+ old_name = g_strdup (mount->name);
|
|
+ old_icon = mount->icon != NULL ? g_object_ref (mount->icon) : NULL;
|
|
+
|
|
+ /* in with the new */
|
|
+ if (mount->volume != NULL)
|
|
+ {
|
|
+ mount->can_unmount = TRUE;
|
|
+
|
|
+ if (mount->icon != NULL)
|
|
+ g_object_unref (mount->icon);
|
|
+
|
|
+ /* order of preference: xdg, autorun, probed */
|
|
+ if (mount->xdg_volume_info_icon != NULL)
|
|
+ mount->icon = g_object_ref (mount->xdg_volume_info_icon);
|
|
+ else if (mount->autorun_icon != NULL)
|
|
+ mount->icon = g_object_ref (mount->autorun_icon);
|
|
+ else
|
|
+ mount->icon = g_volume_get_icon (G_VOLUME (mount->volume));
|
|
+
|
|
+ g_free (mount->name);
|
|
+
|
|
+ /* order of preference : xdg, probed */
|
|
+ if (mount->xdg_volume_info_name != NULL)
|
|
+ mount->name = g_strdup (mount->xdg_volume_info_name);
|
|
+ else
|
|
+ mount->name = g_volume_get_name (G_VOLUME (mount->volume));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ mount->can_unmount = TRUE;
|
|
+
|
|
+ if (mount->icon != NULL)
|
|
+ g_object_unref (mount->icon);
|
|
+
|
|
+ /* order of preference: xdg, autorun, probed */
|
|
+ if (mount->xdg_volume_info_icon != NULL)
|
|
+ mount->icon = g_object_ref (mount->xdg_volume_info_icon);
|
|
+ else if (mount->autorun_icon != NULL)
|
|
+ mount->icon = g_object_ref (mount->autorun_icon);
|
|
+ else
|
|
+ mount->icon = mount->mount_entry_icon != NULL ? g_object_ref (mount->mount_entry_icon) : NULL;
|
|
+
|
|
+ g_free (mount->name);
|
|
+
|
|
+ /* order of preference : xdg, probed */
|
|
+ if (mount->xdg_volume_info_name != NULL)
|
|
+ mount->name = g_strdup (mount->xdg_volume_info_name);
|
|
+ else
|
|
+ mount->name = g_strdup (mount->mount_entry_name);
|
|
+ }
|
|
+
|
|
+ /* compute whether something changed */
|
|
+ changed = !((old_can_unmount == mount->can_unmount) &&
|
|
+ (g_strcmp0 (old_name, mount->name) == 0) &&
|
|
+ g_icon_equal (old_icon, mount->icon)
|
|
+ );
|
|
+
|
|
+ /* free old values */
|
|
+ g_free (old_name);
|
|
+ if (old_icon != NULL)
|
|
+ g_object_unref (old_icon);
|
|
+
|
|
+ /*g_debug ("in update_mount(), changed=%d", changed);*/
|
|
+
|
|
+ /* search for .xdg-volume-info */
|
|
+ if (!mount->searched_for_xdg_volume_info)
|
|
+ {
|
|
+ mount->searched_for_xdg_volume_info = TRUE;
|
|
+ g_vfs_mount_info_query_xdg_volume_info (mount->root,
|
|
+ NULL,
|
|
+ got_xdg_volume_info_cb,
|
|
+ g_object_ref (mount));
|
|
+ }
|
|
+
|
|
+ /* search for autorun.inf */
|
|
+ if (!mount->searched_for_autorun)
|
|
+ {
|
|
+ mount->searched_for_autorun = TRUE;
|
|
+ g_vfs_mount_info_query_autorun_info (mount->root,
|
|
+ NULL,
|
|
+ got_autorun_info_cb,
|
|
+ g_object_ref (mount));
|
|
+ }
|
|
+
|
|
+ return changed;
|
|
+}
|
|
+
|
|
+static void
|
|
+volume_changed (GVolume *volume,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (user_data);
|
|
+
|
|
+ if (update_mount (mount))
|
|
+ emit_changed (mount);
|
|
+}
|
|
+
|
|
+GGduMount *
|
|
+g_gdu_mount_new (GVolumeMonitor *volume_monitor,
|
|
+ GUnixMountEntry *mount_entry,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ GGduMount *mount;
|
|
+
|
|
+ mount = NULL;
|
|
+
|
|
+ /* Ignore internal mounts unless there's a volume */
|
|
+ if (volume == NULL && (mount_entry != NULL && !g_unix_mount_guess_should_display (mount_entry)))
|
|
+ goto out;
|
|
+
|
|
+ mount = g_object_new (G_TYPE_GDU_MOUNT, NULL);
|
|
+ mount->volume_monitor = volume_monitor;
|
|
+ g_object_add_weak_pointer (G_OBJECT (volume_monitor), (gpointer) &(mount->volume_monitor));
|
|
+
|
|
+ if (mount_entry != NULL)
|
|
+ {
|
|
+ /* No ref on GUnixMountEntry so save values for later use */
|
|
+ mount->mount_entry_name = g_unix_mount_guess_name (mount_entry);
|
|
+ mount->mount_entry_icon = g_unix_mount_guess_icon (mount_entry);
|
|
+ mount->device_file = g_strdup (g_unix_mount_get_device_path (mount_entry));
|
|
+ mount->mount_path = g_strdup (g_unix_mount_get_mount_path (mount_entry));
|
|
+ mount->root = g_file_new_for_path (mount->mount_path);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* burn:/// mount (the only mounts we support with mount_entry == NULL) */
|
|
+ mount->device_file = NULL;
|
|
+ mount->mount_path = NULL;
|
|
+ mount->root = g_file_new_for_uri ("burn:///");
|
|
+ mount->is_burn_mount = TRUE;
|
|
+ }
|
|
+
|
|
+ /* need to set the volume only when the mount is fully constructed */
|
|
+ mount->volume = volume;
|
|
+ if (mount->volume != NULL)
|
|
+ {
|
|
+ g_gdu_volume_set_mount (volume, mount);
|
|
+ /* this is for piggy backing on the name and icon of the associated volume */
|
|
+ g_signal_connect (mount->volume, "changed", G_CALLBACK (volume_changed), mount);
|
|
+ }
|
|
+
|
|
+ update_mount (mount);
|
|
+
|
|
+ out:
|
|
+
|
|
+ return mount;
|
|
+}
|
|
+
|
|
+void
|
|
+g_gdu_mount_unmounted (GGduMount *mount)
|
|
+{
|
|
+ if (mount->volume != NULL)
|
|
+ {
|
|
+ g_gdu_volume_unset_mount (mount->volume, mount);
|
|
+ g_signal_handlers_disconnect_by_func (mount->volume, volume_changed, mount);
|
|
+ mount->volume = NULL;
|
|
+ emit_changed (mount);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+g_gdu_mount_unset_volume (GGduMount *mount,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ if (mount->volume == volume)
|
|
+ {
|
|
+ g_signal_handlers_disconnect_by_func (mount->volume, volume_changed, mount);
|
|
+ mount->volume = NULL;
|
|
+ emit_changed (mount);
|
|
+ }
|
|
+}
|
|
+
|
|
+static GFile *
|
|
+g_gdu_mount_get_root (GMount *_mount)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ return mount->root != NULL ? g_object_ref (mount->root) : NULL;
|
|
+}
|
|
+
|
|
+static GIcon *
|
|
+g_gdu_mount_get_icon (GMount *_mount)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ return mount->icon != NULL ? g_object_ref (mount->icon) : NULL;
|
|
+}
|
|
+
|
|
+static gchar *
|
|
+g_gdu_mount_get_uuid (GMount *_mount)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ return g_strdup (mount->uuid);
|
|
+}
|
|
+
|
|
+static gchar *
|
|
+g_gdu_mount_get_name (GMount *_mount)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ return g_strdup (mount->name);
|
|
+}
|
|
+
|
|
+gboolean
|
|
+g_gdu_mount_has_uuid (GGduMount *_mount,
|
|
+ const gchar *uuid)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ return g_strcmp0 (mount->uuid, uuid) == 0;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+g_gdu_mount_has_mount_path (GGduMount *_mount,
|
|
+ const gchar *mount_path)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ return g_strcmp0 (mount->mount_path, mount_path) == 0;
|
|
+}
|
|
+
|
|
+static GDrive *
|
|
+g_gdu_mount_get_drive (GMount *_mount)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ GDrive *drive;
|
|
+
|
|
+ drive = NULL;
|
|
+ if (mount->volume != NULL)
|
|
+ drive = g_volume_get_drive (G_VOLUME (mount->volume));
|
|
+
|
|
+ return drive;
|
|
+}
|
|
+
|
|
+static GVolume *
|
|
+g_gdu_mount_get_volume (GMount *_mount)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ GVolume *volume;
|
|
+
|
|
+ volume = NULL;
|
|
+ if (mount->volume)
|
|
+ volume = G_VOLUME (g_object_ref (mount->volume));
|
|
+
|
|
+ return volume;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_mount_can_unmount (GMount *_mount)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ return mount->can_unmount;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_mount_can_eject (GMount *_mount)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ GDrive *drive;
|
|
+ gboolean can_eject;
|
|
+
|
|
+ can_eject = FALSE;
|
|
+ if (mount->volume != NULL)
|
|
+ {
|
|
+ drive = g_volume_get_drive (G_VOLUME (mount->volume));
|
|
+ if (drive != NULL)
|
|
+ can_eject = g_drive_can_eject (drive);
|
|
+ }
|
|
+
|
|
+ return can_eject;
|
|
+}
|
|
+
|
|
+/* ---------------------------------------------------------------------------------------------------- */
|
|
+
|
|
+typedef struct {
|
|
+ GMount *mount;
|
|
+ GAsyncReadyCallback callback;
|
|
+ gpointer user_data;
|
|
+ GCancellable *cancellable;
|
|
+ int error_fd;
|
|
+ GIOChannel *error_channel;
|
|
+ guint error_channel_source_id;
|
|
+ GString *error_string;
|
|
+} UnmountEjectOp;
|
|
+
|
|
+static void
|
|
+eject_unmount_cb (GPid pid, gint status, gpointer user_data)
|
|
+{
|
|
+ UnmountEjectOp *data = user_data;
|
|
+ GSimpleAsyncResult *simple;
|
|
+
|
|
+ if (WEXITSTATUS (status) != 0)
|
|
+ {
|
|
+ GError *error;
|
|
+ error = g_error_new_literal (G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED,
|
|
+ data->error_string->str);
|
|
+ simple = g_simple_async_result_new_from_error (G_OBJECT (data->mount),
|
|
+ data->callback,
|
|
+ data->user_data,
|
|
+ error);
|
|
+ g_error_free (error);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ simple = g_simple_async_result_new (G_OBJECT (data->mount),
|
|
+ data->callback,
|
|
+ data->user_data,
|
|
+ NULL);
|
|
+ }
|
|
+
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+
|
|
+ g_source_remove (data->error_channel_source_id);
|
|
+ g_io_channel_unref (data->error_channel);
|
|
+ g_string_free (data->error_string, TRUE);
|
|
+ close (data->error_fd);
|
|
+ g_spawn_close_pid (pid);
|
|
+ g_free (data);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+eject_unmount_read_error (GIOChannel *channel,
|
|
+ GIOCondition condition,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ UnmountEjectOp *data = user_data;
|
|
+ gchar buf[BUFSIZ];
|
|
+ gsize bytes_read;
|
|
+ GError *error;
|
|
+ GIOStatus status;
|
|
+
|
|
+ error = NULL;
|
|
+read:
|
|
+ status = g_io_channel_read_chars (channel, buf, sizeof (buf), &bytes_read, &error);
|
|
+ if (status == G_IO_STATUS_NORMAL)
|
|
+ {
|
|
+ g_string_append_len (data->error_string, buf, bytes_read);
|
|
+ if (bytes_read == sizeof (buf))
|
|
+ goto read;
|
|
+ }
|
|
+ else if (status == G_IO_STATUS_EOF)
|
|
+ g_string_append_len (data->error_string, buf, bytes_read);
|
|
+ else if (status == G_IO_STATUS_ERROR)
|
|
+ {
|
|
+ if (data->error_string->len > 0)
|
|
+ g_string_append (data->error_string, "\n");
|
|
+
|
|
+ g_string_append (data->error_string, error->message);
|
|
+ g_error_free (error);
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static void
|
|
+eject_unmount_do (GMount *mount,
|
|
+ GCancellable *cancellable,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data,
|
|
+ char **argv)
|
|
+{
|
|
+ UnmountEjectOp *data;
|
|
+ GPid child_pid;
|
|
+ GError *error;
|
|
+
|
|
+ data = g_new0 (UnmountEjectOp, 1);
|
|
+ data->mount = mount;
|
|
+ data->callback = callback;
|
|
+ data->user_data = user_data;
|
|
+ data->cancellable = cancellable;
|
|
+
|
|
+ error = NULL;
|
|
+ if (!g_spawn_async_with_pipes (NULL, /* working dir */
|
|
+ argv,
|
|
+ NULL, /* envp */
|
|
+ G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH,
|
|
+ NULL, /* child_setup */
|
|
+ NULL, /* user_data for child_setup */
|
|
+ &child_pid,
|
|
+ NULL, /* standard_input */
|
|
+ NULL, /* standard_output */
|
|
+ &(data->error_fd),
|
|
+ &error)) {
|
|
+ g_assert (error != NULL);
|
|
+ goto handle_error;
|
|
+ }
|
|
+
|
|
+ data->error_string = g_string_new ("");
|
|
+
|
|
+ data->error_channel = g_io_channel_unix_new (data->error_fd);
|
|
+ g_io_channel_set_flags (data->error_channel, G_IO_FLAG_NONBLOCK, &error);
|
|
+ if (error != NULL)
|
|
+ goto handle_error;
|
|
+
|
|
+ data->error_channel_source_id = g_io_add_watch (data->error_channel, G_IO_IN, eject_unmount_read_error, data);
|
|
+ g_child_watch_add (child_pid, eject_unmount_cb, data);
|
|
+
|
|
+handle_error:
|
|
+
|
|
+ if (error != NULL)
|
|
+ {
|
|
+ GSimpleAsyncResult *simple;
|
|
+ simple = g_simple_async_result_new_from_error (G_OBJECT (data->mount),
|
|
+ data->callback,
|
|
+ data->user_data,
|
|
+ error);
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+
|
|
+ if (data->error_string != NULL)
|
|
+ g_string_free (data->error_string, TRUE);
|
|
+
|
|
+ if (data->error_channel != NULL)
|
|
+ g_io_channel_unref (data->error_channel);
|
|
+
|
|
+ g_error_free (error);
|
|
+ g_free (data);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* ---------------------------------------------------------------------------------------------------- */
|
|
+static void
|
|
+luks_lock_cb (GduDevice *device,
|
|
+ GError *error,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
|
|
+
|
|
+ if (error != NULL)
|
|
+ {
|
|
+ /* We could handle PolicyKit integration here but this action is allowed by default
|
|
+ * and this won't be needed when porting to PolicyKit 1.0 anyway
|
|
+ */
|
|
+ g_simple_async_result_set_from_error (simple, error);
|
|
+ g_error_free (error);
|
|
+ }
|
|
+
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+}
|
|
+
|
|
+static void
|
|
+unmount_cb (GduDevice *device,
|
|
+ GError *error,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
|
|
+
|
|
+ if (error != NULL)
|
|
+ {
|
|
+ /* We could handle PolicyKit integration here but this action is allowed by default
|
|
+ * and this won't be needed when porting to PolicyKit 1.0 anyway
|
|
+ */
|
|
+ g_simple_async_result_set_from_error (simple, error);
|
|
+ g_error_free (error);
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* if volume is a cleartext LUKS block device, then also lock this one */
|
|
+ if (gdu_device_is_luks_cleartext (device))
|
|
+ {
|
|
+ const gchar *luks_cleartext_slave_object_path;
|
|
+ GduDevice *luks_cleartext_slave;
|
|
+ GduPool *pool;
|
|
+
|
|
+ luks_cleartext_slave_object_path = gdu_device_luks_cleartext_get_slave (device);
|
|
+ if (luks_cleartext_slave_object_path == NULL)
|
|
+ {
|
|
+ g_simple_async_result_set_error (simple,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED,
|
|
+ "Cannot get LUKS cleartext slave");
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ pool = gdu_device_get_pool (device);
|
|
+ luks_cleartext_slave = gdu_pool_get_by_object_path (pool, luks_cleartext_slave_object_path);
|
|
+ g_object_unref (pool);
|
|
+
|
|
+ if (luks_cleartext_slave == NULL)
|
|
+ {
|
|
+ g_simple_async_result_set_error (simple,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED,
|
|
+ "Cannot get LUKS cleartext slave");
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ gdu_device_op_luks_lock (luks_cleartext_slave,
|
|
+ luks_lock_cb,
|
|
+ simple);
|
|
+
|
|
+ g_object_unref (luks_cleartext_slave);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+
|
|
+ out:
|
|
+ ;
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_mount_unmount (GMount *_mount,
|
|
+ GMountUnmountFlags flags,
|
|
+ GCancellable *cancellable,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ GSimpleAsyncResult *simple;
|
|
+
|
|
+ if (mount->volume == NULL)
|
|
+ {
|
|
+ gchar *argv[] = {"umount", NULL, NULL};
|
|
+
|
|
+ /* TODO: honor flags */
|
|
+
|
|
+ if (mount->mount_path != NULL)
|
|
+ argv[1] = mount->mount_path;
|
|
+ else
|
|
+ argv[1] = mount->device_file;
|
|
+
|
|
+ eject_unmount_do (_mount, cancellable, callback, user_data, argv);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ simple = g_simple_async_result_new (G_OBJECT (mount),
|
|
+ callback,
|
|
+ user_data,
|
|
+ NULL);
|
|
+
|
|
+ if (mount->is_burn_mount)
|
|
+ {
|
|
+ /* burn mounts are really never mounted... */
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ GduDevice *device;
|
|
+ GduPresentable *volume;
|
|
+
|
|
+ /* TODO: honor flags */
|
|
+
|
|
+ volume = g_gdu_volume_get_presentable_with_cleartext (mount->volume);
|
|
+ device = gdu_presentable_get_device (volume);
|
|
+
|
|
+ gdu_device_op_filesystem_unmount (device, unmount_cb, simple);
|
|
+
|
|
+ g_object_unref (device);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_mount_unmount_finish (GMount *mount,
|
|
+ GAsyncResult *result,
|
|
+ GError **error)
|
|
+{
|
|
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
|
|
+}
|
|
+
|
|
+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_object_unref (data->object);
|
|
+ g_free (data);
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_mount_eject (GMount *mount,
|
|
+ GMountUnmountFlags flags,
|
|
+ GCancellable *cancellable,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduMount *gdu_mount = G_GDU_MOUNT (mount);
|
|
+ GDrive *drive;
|
|
+
|
|
+ drive = NULL;
|
|
+ if (gdu_mount->volume != NULL)
|
|
+ drive = g_volume_get_drive (G_VOLUME (gdu_mount->volume));
|
|
+
|
|
+ if (drive != NULL)
|
|
+ {
|
|
+ EjectWrapperOp *data;
|
|
+ data = g_new0 (EjectWrapperOp, 1);
|
|
+ data->object = g_object_ref (mount);
|
|
+ data->callback = callback;
|
|
+ data->user_data = user_data;
|
|
+ g_drive_eject (drive, flags, cancellable, eject_wrapper_callback, data);
|
|
+ g_object_unref (drive);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ GSimpleAsyncResult *simple;
|
|
+ simple = g_simple_async_result_new_error (G_OBJECT (mount),
|
|
+ callback,
|
|
+ user_data,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED,
|
|
+ _("Operation not supported by backend"));
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_mount_eject_finish (GMount *_mount,
|
|
+ GAsyncResult *result,
|
|
+ GError **error)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ GDrive *drive;
|
|
+ gboolean res;
|
|
+
|
|
+ res = TRUE;
|
|
+
|
|
+ drive = NULL;
|
|
+ if (mount->volume != NULL)
|
|
+ drive = g_volume_get_drive (G_VOLUME (mount->volume));
|
|
+
|
|
+ if (drive != NULL)
|
|
+ {
|
|
+ res = g_drive_eject_finish (drive, result, error);
|
|
+ g_object_unref (drive);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
|
|
+ res = FALSE;
|
|
+ }
|
|
+
|
|
+ return res;
|
|
+}
|
|
+
|
|
+/* TODO: handle force_rescan */
|
|
+static gchar **
|
|
+g_gdu_mount_guess_content_type_sync (GMount *_mount,
|
|
+ gboolean force_rescan,
|
|
+ GCancellable *cancellable,
|
|
+ GError **error)
|
|
+{
|
|
+ GGduMount *mount = G_GDU_MOUNT (_mount);
|
|
+ const gchar *disc_type;
|
|
+ char **x_content_types;
|
|
+ GPtrArray *p;
|
|
+ gchar **result;
|
|
+ GduDevice *device;
|
|
+ guint n;
|
|
+
|
|
+ p = g_ptr_array_new ();
|
|
+
|
|
+ device = NULL;
|
|
+ if (mount->volume != NULL)
|
|
+ {
|
|
+ GduPresentable *presentable;
|
|
+ presentable = g_gdu_volume_get_presentable_with_cleartext (mount->volume);
|
|
+ device = gdu_presentable_get_device (presentable);
|
|
+ }
|
|
+
|
|
+ /* doesn't make sense to probe blank discs - look at the disc type instead */
|
|
+ if (device != NULL && gdu_device_optical_disc_get_is_blank (device))
|
|
+ {
|
|
+ disc_type = gdu_device_drive_get_media (device);
|
|
+ if (disc_type != NULL)
|
|
+ {
|
|
+ if (g_str_has_prefix (disc_type, "optical_dvd"))
|
|
+ g_ptr_array_add (p, g_strdup ("x-content/blank-dvd"));
|
|
+ else if (g_str_has_prefix (disc_type, "optical_hddvd"))
|
|
+ g_ptr_array_add (p, g_strdup ("x-content/blank-hddvd"));
|
|
+ else if (g_str_has_prefix (disc_type, "optical_bd"))
|
|
+ g_ptr_array_add (p, g_strdup ("x-content/blank-bd"));
|
|
+ else
|
|
+ g_ptr_array_add (p, g_strdup ("x-content/blank-cd")); /* assume CD */
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* sniff content type */
|
|
+ x_content_types = g_content_type_guess_for_tree (mount->root);
|
|
+ if (x_content_types != NULL)
|
|
+ {
|
|
+ for (n = 0; x_content_types[n] != NULL; n++)
|
|
+ g_ptr_array_add (p, g_strdup (x_content_types[n]));
|
|
+ g_strfreev (x_content_types);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (p->len == 0)
|
|
+ {
|
|
+ result = NULL;
|
|
+ g_ptr_array_free (p, TRUE);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_ptr_array_add (p, NULL);
|
|
+ result = (char **) g_ptr_array_free (p, FALSE);
|
|
+ }
|
|
+
|
|
+ if (device != NULL)
|
|
+ g_object_unref (device);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/* since we're an out-of-process volume monitor we'll just do this sync */
|
|
+static void
|
|
+g_gdu_mount_guess_content_type (GMount *mount,
|
|
+ gboolean force_rescan,
|
|
+ GCancellable *cancellable,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GSimpleAsyncResult *simple;
|
|
+
|
|
+ /* TODO: handle force_rescan */
|
|
+ simple = g_simple_async_result_new (G_OBJECT (mount),
|
|
+ callback,
|
|
+ user_data,
|
|
+ NULL);
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+}
|
|
+
|
|
+static gchar **
|
|
+g_gdu_mount_guess_content_type_finish (GMount *mount,
|
|
+ GAsyncResult *result,
|
|
+ GError **error)
|
|
+{
|
|
+ return g_gdu_mount_guess_content_type_sync (mount, FALSE, NULL, error);
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_mount_mount_iface_init (GMountIface *iface)
|
|
+{
|
|
+ iface->get_root = g_gdu_mount_get_root;
|
|
+ iface->get_name = g_gdu_mount_get_name;
|
|
+ iface->get_icon = g_gdu_mount_get_icon;
|
|
+ iface->get_uuid = g_gdu_mount_get_uuid;
|
|
+ iface->get_drive = g_gdu_mount_get_drive;
|
|
+ iface->get_volume = g_gdu_mount_get_volume;
|
|
+ iface->can_unmount = g_gdu_mount_can_unmount;
|
|
+ iface->can_eject = g_gdu_mount_can_eject;
|
|
+ iface->unmount = g_gdu_mount_unmount;
|
|
+ iface->unmount_finish = g_gdu_mount_unmount_finish;
|
|
+ iface->eject = g_gdu_mount_eject;
|
|
+ iface->eject_finish = g_gdu_mount_eject_finish;
|
|
+ iface->guess_content_type = g_gdu_mount_guess_content_type;
|
|
+ iface->guess_content_type_finish = g_gdu_mount_guess_content_type_finish;
|
|
+ iface->guess_content_type_sync = g_gdu_mount_guess_content_type_sync;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+g_gdu_mount_has_volume (GGduMount *mount,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ return mount->volume == volume;
|
|
+}
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/ggdumount.h 2009-03-01 23:48:16.000000000 -0500
|
|
@@ -0,0 +1,64 @@
|
|
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
+/* gvfs - extensions for gio
|
|
+ *
|
|
+ * Copyright (C) 2006-2009 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: David Zeuthen <davidz@redhat.com>
|
|
+ */
|
|
+
|
|
+#ifndef __G_GDU_MOUNT_H__
|
|
+#define __G_GDU_MOUNT_H__
|
|
+
|
|
+#include <glib-object.h>
|
|
+#include <gio/gio.h>
|
|
+
|
|
+#include "ggduvolumemonitor.h"
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+#define G_TYPE_GDU_MOUNT (g_gdu_mount_get_type ())
|
|
+#define G_GDU_MOUNT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_GDU_MOUNT, GGduMount))
|
|
+#define G_GDU_MOUNT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_GDU_MOUNT, GGduMountClass))
|
|
+#define G_IS_GDU_MOUNT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_GDU_MOUNT))
|
|
+#define G_IS_GDU_MOUNT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_GDU_MOUNT))
|
|
+
|
|
+typedef struct _GGduMountClass GGduMountClass;
|
|
+
|
|
+struct _GGduMountClass {
|
|
+ GObjectClass parent_class;
|
|
+};
|
|
+
|
|
+GType g_gdu_mount_get_type (void) G_GNUC_CONST;
|
|
+
|
|
+GGduMount * g_gdu_mount_new (GVolumeMonitor *volume_monitor,
|
|
+ GUnixMountEntry *mount_entry,
|
|
+ GGduVolume *volume);
|
|
+gboolean g_gdu_mount_has_mount_path (GGduMount *mount,
|
|
+ const gchar *mount_path);
|
|
+gboolean g_gdu_mount_has_uuid (GGduMount *mount,
|
|
+ const gchar *uuid);
|
|
+void g_gdu_mount_unset_volume (GGduMount *mount,
|
|
+ GGduVolume *volume);
|
|
+void g_gdu_mount_unmounted (GGduMount *mount);
|
|
+
|
|
+gboolean g_gdu_mount_has_volume (GGduMount *mount,
|
|
+ GGduVolume *volume);
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __G_GDU_MOUNT_H__ */
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/ggduvolume.c 2009-03-04 16:19:41.000000000 -0500
|
|
@@ -0,0 +1,1408 @@
|
|
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
+/* gvfs - extensions for gio
|
|
+ *
|
|
+ * Copyright (C) 2006-2009 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: David Zeuthen <davidz@redhat.com>
|
|
+ */
|
|
+
|
|
+#include <config.h>
|
|
+
|
|
+#include <string.h>
|
|
+#include <sys/wait.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include <glib.h>
|
|
+#include <glib/gi18n-lib.h>
|
|
+#include <gio/gio.h>
|
|
+
|
|
+#include "ggdudrive.h"
|
|
+#include "ggduvolume.h"
|
|
+#include "ggdumount.h"
|
|
+
|
|
+#include "polkit.h"
|
|
+
|
|
+typedef struct MountOpData MountOpData;
|
|
+
|
|
+static void cancel_pending_mount_op (MountOpData *data);
|
|
+
|
|
+struct _GGduVolume
|
|
+{
|
|
+ GObject parent;
|
|
+
|
|
+ GVolumeMonitor *volume_monitor; /* owned by volume monitor */
|
|
+ GGduMount *mount; /* owned by volume monitor */
|
|
+ GGduDrive *drive; /* owned by volume monitor */
|
|
+
|
|
+ GduVolume *gdu_volume;
|
|
+
|
|
+ /* if the volume is encrypted, this is != NULL when unlocked */
|
|
+ GduVolume *cleartext_gdu_volume;
|
|
+
|
|
+ /* If a mount operation is in progress, then pending_mount_op is != NULL. This
|
|
+ * is used to cancel the operation to make possible authentication dialogs go
|
|
+ * away.
|
|
+ */
|
|
+ MountOpData *pending_mount_op;
|
|
+
|
|
+ /* the following members need to be set upon construction */
|
|
+ GIcon *icon;
|
|
+ GFile *activation_root;
|
|
+ gchar *name;
|
|
+ gchar *device_file;
|
|
+ gchar *uuid;
|
|
+ gboolean can_mount;
|
|
+ gboolean should_automount;
|
|
+};
|
|
+
|
|
+static void g_gdu_volume_volume_iface_init (GVolumeIface *iface);
|
|
+
|
|
+G_DEFINE_TYPE_EXTENDED (GGduVolume, g_gdu_volume, G_TYPE_OBJECT, 0,
|
|
+ G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME,
|
|
+ g_gdu_volume_volume_iface_init))
|
|
+
|
|
+static void gdu_volume_changed (GduPresentable *presentable,
|
|
+ GGduVolume *volume);
|
|
+static void gdu_volume_job_changed (GduPresentable *presentable,
|
|
+ GGduVolume *volume);
|
|
+
|
|
+static void gdu_cleartext_volume_removed (GduPresentable *presentable,
|
|
+ GGduVolume *volume);
|
|
+static void gdu_cleartext_volume_changed (GduPresentable *presentable,
|
|
+ GGduVolume *volume);
|
|
+static void gdu_cleartext_volume_job_changed (GduPresentable *presentable,
|
|
+ GGduVolume *volume);
|
|
+
|
|
+static void mount_with_mount_operation (MountOpData *data);
|
|
+
|
|
+static void
|
|
+g_gdu_volume_finalize (GObject *object)
|
|
+{
|
|
+ GGduVolume *volume;
|
|
+
|
|
+ volume = G_GDU_VOLUME (object);
|
|
+
|
|
+ if (volume->mount != NULL)
|
|
+ g_gdu_mount_unset_volume (volume->mount, volume);
|
|
+
|
|
+ if (volume->drive != NULL)
|
|
+ g_gdu_drive_unset_volume (volume->drive, volume);
|
|
+
|
|
+ if (volume->gdu_volume != NULL)
|
|
+ {
|
|
+ g_signal_handlers_disconnect_by_func (volume->gdu_volume, gdu_volume_changed, volume);
|
|
+ g_signal_handlers_disconnect_by_func (volume->gdu_volume, gdu_volume_job_changed, volume);
|
|
+ g_object_unref (volume->gdu_volume);
|
|
+ }
|
|
+
|
|
+ if (volume->cleartext_gdu_volume != NULL)
|
|
+ {
|
|
+ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_removed, volume);
|
|
+ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_changed, volume);
|
|
+ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_job_changed, volume);
|
|
+ g_object_unref (volume->cleartext_gdu_volume);
|
|
+ }
|
|
+
|
|
+ if (volume->icon != NULL)
|
|
+ g_object_unref (volume->icon);
|
|
+ if (volume->activation_root != NULL)
|
|
+ g_object_unref (volume->activation_root);
|
|
+
|
|
+ g_free (volume->name);
|
|
+ g_free (volume->device_file);
|
|
+ g_free (volume->uuid);
|
|
+
|
|
+ if (G_OBJECT_CLASS (g_gdu_volume_parent_class)->finalize)
|
|
+ (*G_OBJECT_CLASS (g_gdu_volume_parent_class)->finalize) (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_volume_class_init (GGduVolumeClass *klass)
|
|
+{
|
|
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
+
|
|
+ gobject_class->finalize = g_gdu_volume_finalize;
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_volume_init (GGduVolume *gdu_volume)
|
|
+{
|
|
+}
|
|
+
|
|
+static void
|
|
+emit_changed (GGduVolume *volume)
|
|
+{
|
|
+ g_signal_emit_by_name (volume, "changed");
|
|
+ g_signal_emit_by_name (volume->volume_monitor, "volume_changed", volume);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+update_volume (GGduVolume *volume)
|
|
+{
|
|
+ GduDevice *device;
|
|
+ GduPool *pool;
|
|
+ time_t now;
|
|
+ gboolean changed;
|
|
+ gboolean old_can_mount;
|
|
+ gboolean old_should_automount;
|
|
+ gchar *old_name;
|
|
+ gchar *old_device_file;
|
|
+ GIcon *old_icon;
|
|
+ gboolean keep_cleartext_volume;
|
|
+
|
|
+ /* save old values */
|
|
+ old_can_mount = volume->can_mount;
|
|
+ old_should_automount = volume->should_automount;
|
|
+ old_name = g_strdup (volume->name);
|
|
+ old_device_file = g_strdup (volume->device_file);
|
|
+ old_icon = volume->icon != NULL ? g_object_ref (volume->icon) : NULL;
|
|
+
|
|
+ /* ---------------------------------------------------------------------------------------------------- */
|
|
+
|
|
+ /* in with the new */
|
|
+ device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->gdu_volume));
|
|
+ pool = gdu_device_get_pool (device);
|
|
+
|
|
+ keep_cleartext_volume = FALSE;
|
|
+ if (gdu_device_is_luks (device))
|
|
+ {
|
|
+ const gchar *holder_objpath;
|
|
+
|
|
+ holder_objpath = gdu_device_luks_get_holder (device);
|
|
+ if (holder_objpath != NULL && g_strcmp0 (holder_objpath, "/") != 0)
|
|
+ {
|
|
+ GduDevice *cleartext_device;
|
|
+
|
|
+ cleartext_device = gdu_pool_get_by_object_path (pool, holder_objpath);
|
|
+ if (cleartext_device != NULL)
|
|
+ {
|
|
+ GduVolume *cleartext_gdu_volume;
|
|
+
|
|
+ cleartext_gdu_volume = GDU_VOLUME (gdu_pool_get_volume_by_device (pool, cleartext_device));
|
|
+ if (cleartext_gdu_volume != volume->cleartext_gdu_volume)
|
|
+ {
|
|
+ if (volume->cleartext_gdu_volume != NULL)
|
|
+ {
|
|
+ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_removed, volume);
|
|
+ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_changed, volume);
|
|
+ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_job_changed, volume);
|
|
+ g_object_unref (volume->cleartext_gdu_volume);
|
|
+ }
|
|
+
|
|
+ volume->cleartext_gdu_volume = g_object_ref (cleartext_gdu_volume);
|
|
+ g_signal_connect (volume->cleartext_gdu_volume, "removed", G_CALLBACK (gdu_cleartext_volume_removed), volume);
|
|
+ g_signal_connect (volume->cleartext_gdu_volume, "changed", G_CALLBACK (gdu_cleartext_volume_changed), volume);
|
|
+ g_signal_connect (volume->cleartext_gdu_volume, "job-changed", G_CALLBACK (gdu_cleartext_volume_job_changed), volume);
|
|
+ }
|
|
+ g_object_unref (cleartext_gdu_volume);
|
|
+
|
|
+ g_object_unref (cleartext_device);
|
|
+
|
|
+ keep_cleartext_volume = TRUE;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!keep_cleartext_volume)
|
|
+ {
|
|
+ if (volume->cleartext_gdu_volume != NULL)
|
|
+ {
|
|
+ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_removed, volume);
|
|
+ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_changed, volume);
|
|
+ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_job_changed, volume);
|
|
+ g_object_unref (volume->cleartext_gdu_volume);
|
|
+ volume->cleartext_gdu_volume = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ /* Use data from cleartext LUKS volume if it is unlocked */
|
|
+ if (volume->cleartext_gdu_volume != NULL)
|
|
+ {
|
|
+ GduDevice *luks_cleartext_volume_device;
|
|
+
|
|
+ luks_cleartext_volume_device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->cleartext_gdu_volume));
|
|
+
|
|
+ if (volume->icon != NULL)
|
|
+ g_object_unref (volume->icon);
|
|
+ volume->icon = gdu_presentable_get_icon (GDU_PRESENTABLE (volume->cleartext_gdu_volume));
|
|
+
|
|
+ g_free (volume->name);
|
|
+ volume->name = gdu_presentable_get_name (GDU_PRESENTABLE (volume->cleartext_gdu_volume));
|
|
+
|
|
+ g_free (volume->device_file);
|
|
+ volume->device_file = g_strdup (gdu_device_get_device_file (luks_cleartext_volume_device));
|
|
+
|
|
+ volume->can_mount = TRUE;
|
|
+
|
|
+ volume->should_automount = FALSE;
|
|
+
|
|
+ g_object_unref (luks_cleartext_volume_device);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ gchar *activation_uri;
|
|
+
|
|
+ if (volume->icon != NULL)
|
|
+ g_object_unref (volume->icon);
|
|
+ volume->icon = gdu_presentable_get_icon (GDU_PRESENTABLE (volume->gdu_volume));
|
|
+
|
|
+ g_free (volume->name);
|
|
+ volume->name = gdu_presentable_get_name (GDU_PRESENTABLE (volume->gdu_volume));
|
|
+
|
|
+ /* special case the name and icon for audio discs */
|
|
+ activation_uri = volume->activation_root != NULL ? g_file_get_uri (volume->activation_root) : NULL;
|
|
+ if (activation_uri != NULL && g_str_has_prefix (activation_uri, "cdda://"))
|
|
+ {
|
|
+ if (volume->icon != NULL)
|
|
+ g_object_unref (volume->icon);
|
|
+ volume->icon = g_themed_icon_new_with_default_fallbacks ("media-optical-audio");
|
|
+ g_free (volume->name);
|
|
+ volume->name = g_strdup (_("Audio Disc"));
|
|
+ }
|
|
+
|
|
+ g_free (volume->device_file);
|
|
+ volume->device_file = g_strdup (gdu_device_get_device_file (device));
|
|
+
|
|
+ volume->can_mount = TRUE;
|
|
+
|
|
+ /* If a volume (partition) appear _much later_ than when media was insertion it
|
|
+ * can only be because the media was repartitioned. We don't want to automount
|
|
+ * such volumes.
|
|
+ */
|
|
+ volume->should_automount = TRUE;
|
|
+ if (volume->drive != NULL)
|
|
+ {
|
|
+ now = time (NULL);
|
|
+ if (now - g_gdu_drive_get_time_of_last_media_insertion (volume->drive) > 5)
|
|
+ volume->should_automount = FALSE;
|
|
+ }
|
|
+
|
|
+ g_free (activation_uri);
|
|
+ }
|
|
+
|
|
+ g_object_unref (pool);
|
|
+ g_object_unref (device);
|
|
+
|
|
+ /* ---------------------------------------------------------------------------------------------------- */
|
|
+
|
|
+ /* compute whether something changed */
|
|
+ changed = !((old_can_mount == volume->can_mount) &&
|
|
+ (old_should_automount == volume->should_automount) &&
|
|
+ (g_strcmp0 (old_name, volume->name) == 0) &&
|
|
+ (g_strcmp0 (old_device_file, volume->device_file) == 0) &&
|
|
+ g_icon_equal (old_icon, volume->icon)
|
|
+ );
|
|
+
|
|
+ /* free old values */
|
|
+ g_free (old_name);
|
|
+ g_free (old_device_file);
|
|
+ if (old_icon != NULL)
|
|
+ g_object_unref (old_icon);
|
|
+
|
|
+ /*g_debug ("in update_volume(), changed=%d", changed);*/
|
|
+
|
|
+ return changed;
|
|
+}
|
|
+
|
|
+static void
|
|
+gdu_volume_changed (GduPresentable *presentable,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ /*g_debug ("volume: presentable_changed: %p: %s", volume, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/
|
|
+ if (update_volume (volume))
|
|
+ emit_changed (volume);
|
|
+}
|
|
+
|
|
+static void
|
|
+gdu_volume_job_changed (GduPresentable *presentable,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ /*g_debug ("volume: presentable_job_changed %p: %s", volume, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/
|
|
+ if (update_volume (volume))
|
|
+ emit_changed (volume);
|
|
+}
|
|
+
|
|
+static void
|
|
+gdu_cleartext_volume_removed (GduPresentable *presentable,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ /*g_debug ("cleartext volume: presentable_removed: %p: %s", volume, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/
|
|
+ if (update_volume (volume))
|
|
+ emit_changed (volume);
|
|
+}
|
|
+
|
|
+static void
|
|
+gdu_cleartext_volume_changed (GduPresentable *presentable,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ /*g_debug ("cleartext volume: presentable_changed: %p: %s", volume, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/
|
|
+ if (update_volume (volume))
|
|
+ emit_changed (volume);
|
|
+}
|
|
+
|
|
+static void
|
|
+gdu_cleartext_volume_job_changed (GduPresentable *presentable,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ /*g_debug ("cleartext volume: presentable_job_changed %p: %s", volume, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/
|
|
+ if (update_volume (volume))
|
|
+ emit_changed (volume);
|
|
+}
|
|
+
|
|
+GGduVolume *
|
|
+g_gdu_volume_new (GVolumeMonitor *volume_monitor,
|
|
+ GduVolume *gdu_volume,
|
|
+ GGduDrive *drive,
|
|
+ GFile *activation_root)
|
|
+{
|
|
+ GGduVolume *volume;
|
|
+
|
|
+ volume = g_object_new (G_TYPE_GDU_VOLUME, NULL);
|
|
+ volume->volume_monitor = volume_monitor;
|
|
+ g_object_add_weak_pointer (G_OBJECT (volume_monitor), (gpointer) &(volume->volume_monitor));
|
|
+
|
|
+ volume->gdu_volume = g_object_ref (gdu_volume);
|
|
+ volume->activation_root = activation_root != NULL ? g_object_ref (activation_root) : NULL;
|
|
+
|
|
+ g_signal_connect (volume->gdu_volume, "changed", G_CALLBACK (gdu_volume_changed), volume);
|
|
+ g_signal_connect (volume->gdu_volume, "job-changed", G_CALLBACK (gdu_volume_job_changed), volume);
|
|
+
|
|
+ volume->drive = drive;
|
|
+ if (drive != NULL)
|
|
+ g_gdu_drive_set_volume (drive, volume);
|
|
+
|
|
+ update_volume (volume);
|
|
+
|
|
+ return volume;
|
|
+}
|
|
+
|
|
+void
|
|
+g_gdu_volume_removed (GGduVolume *volume)
|
|
+{
|
|
+ if (volume->pending_mount_op != NULL)
|
|
+ cancel_pending_mount_op (volume->pending_mount_op);
|
|
+
|
|
+ if (volume->mount != NULL)
|
|
+ {
|
|
+ g_gdu_mount_unset_volume (volume->mount, volume);
|
|
+ volume->mount = NULL;
|
|
+ }
|
|
+
|
|
+ if (volume->drive != NULL)
|
|
+ {
|
|
+ g_gdu_drive_unset_volume (volume->drive, volume);
|
|
+ volume->drive = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+g_gdu_volume_set_mount (GGduVolume *volume,
|
|
+ GGduMount *mount)
|
|
+{
|
|
+ if (volume->mount != mount)
|
|
+ {
|
|
+
|
|
+ if (volume->mount != NULL)
|
|
+ g_gdu_mount_unset_volume (volume->mount, volume);
|
|
+
|
|
+ volume->mount = mount;
|
|
+
|
|
+ emit_changed (volume);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+g_gdu_volume_unset_mount (GGduVolume *volume,
|
|
+ GGduMount *mount)
|
|
+{
|
|
+ if (volume->mount == mount)
|
|
+ {
|
|
+ volume->mount = NULL;
|
|
+ emit_changed (volume);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+g_gdu_volume_set_drive (GGduVolume *volume,
|
|
+ GGduDrive *drive)
|
|
+{
|
|
+ if (volume->drive != drive)
|
|
+ {
|
|
+ if (volume->drive != NULL)
|
|
+ g_gdu_drive_unset_volume (volume->drive, volume);
|
|
+
|
|
+ volume->drive = drive;
|
|
+
|
|
+ emit_changed (volume);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+g_gdu_volume_unset_drive (GGduVolume *volume,
|
|
+ GGduDrive *drive)
|
|
+{
|
|
+ if (volume->drive == drive)
|
|
+ {
|
|
+ volume->drive = NULL;
|
|
+ emit_changed (volume);
|
|
+ }
|
|
+}
|
|
+
|
|
+static GIcon *
|
|
+g_gdu_volume_get_icon (GVolume *_volume)
|
|
+{
|
|
+ GGduVolume *volume = G_GDU_VOLUME (_volume);
|
|
+ return volume->icon != NULL ? g_object_ref (volume->icon) : NULL;
|
|
+}
|
|
+
|
|
+static char *
|
|
+g_gdu_volume_get_name (GVolume *_volume)
|
|
+{
|
|
+ GGduVolume *volume = G_GDU_VOLUME (_volume);
|
|
+ return g_strdup (volume->name);
|
|
+}
|
|
+
|
|
+static char *
|
|
+g_gdu_volume_get_uuid (GVolume *_volume)
|
|
+{
|
|
+ GGduVolume *volume = G_GDU_VOLUME (_volume);
|
|
+ return g_strdup (volume->uuid);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_volume_can_mount (GVolume *_volume)
|
|
+{
|
|
+ GGduVolume *volume = G_GDU_VOLUME (_volume);
|
|
+ return volume->can_mount;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_volume_can_eject (GVolume *_volume)
|
|
+{
|
|
+ GGduVolume *volume = G_GDU_VOLUME (_volume);
|
|
+ gboolean can_eject;
|
|
+
|
|
+ can_eject = FALSE;
|
|
+ if (volume->drive != NULL)
|
|
+ can_eject = g_drive_can_eject (G_DRIVE (volume->drive));
|
|
+
|
|
+ return can_eject;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_volume_should_automount (GVolume *_volume)
|
|
+{
|
|
+ GGduVolume *volume = G_GDU_VOLUME (_volume);
|
|
+ return volume->should_automount;
|
|
+}
|
|
+
|
|
+static GDrive *
|
|
+g_gdu_volume_get_drive (GVolume *volume)
|
|
+{
|
|
+ GGduVolume *gdu_volume = G_GDU_VOLUME (volume);
|
|
+ GDrive *drive;
|
|
+
|
|
+ drive = NULL;
|
|
+ if (gdu_volume->drive != NULL)
|
|
+ drive = g_object_ref (gdu_volume->drive);
|
|
+
|
|
+ return drive;
|
|
+}
|
|
+
|
|
+static GMount *
|
|
+g_gdu_volume_get_mount (GVolume *volume)
|
|
+{
|
|
+ GGduVolume *gdu_volume = G_GDU_VOLUME (volume);
|
|
+ GMount *mount;
|
|
+
|
|
+ mount = NULL;
|
|
+ if (gdu_volume->mount != NULL)
|
|
+ mount = g_object_ref (gdu_volume->mount);
|
|
+
|
|
+ return mount;
|
|
+}
|
|
+
|
|
+/* ---------------------------------------------------------------------------------------------------- */
|
|
+
|
|
+struct MountOpData
|
|
+{
|
|
+ GGduVolume *volume;
|
|
+ GduDevice *device_to_mount;
|
|
+ GSimpleAsyncResult *simple;
|
|
+ GCancellable *cancellable;
|
|
+ gulong cancelled_handler_id;
|
|
+
|
|
+ GMountOperation *mount_operation;
|
|
+ gulong mount_operation_reply_handler_id;
|
|
+
|
|
+ gboolean is_cancelled;
|
|
+};
|
|
+
|
|
+static void
|
|
+mount_op_data_unref (MountOpData *data)
|
|
+{
|
|
+ g_object_unref (data->volume);
|
|
+ if (data->device_to_mount != NULL)
|
|
+ g_object_unref (data->device_to_mount);
|
|
+ g_object_unref (data->simple);
|
|
+ if (data->cancelled_handler_id != 0)
|
|
+ g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
|
|
+ if (data->cancellable != NULL)
|
|
+ g_object_unref (data->cancellable);
|
|
+ if (data->mount_operation_reply_handler_id != 0)
|
|
+ g_signal_handler_disconnect (data->mount_operation, data->mount_operation_reply_handler_id);
|
|
+ if (data->mount_operation != NULL)
|
|
+ g_object_unref (data->mount_operation);
|
|
+ g_free (data);
|
|
+}
|
|
+
|
|
+static void
|
|
+cancel_pending_mount_op (MountOpData *data)
|
|
+{
|
|
+ /* we are no longer pending */
|
|
+ data->volume->pending_mount_op = NULL;
|
|
+
|
|
+ data->is_cancelled = TRUE;
|
|
+
|
|
+ /* send an ::aborted signal to make the dialog go away */
|
|
+ if (data->mount_operation != NULL)
|
|
+ g_signal_emit_by_name (data->mount_operation, "aborted");
|
|
+
|
|
+ /* complete the operation (sends reply to caller) */
|
|
+ g_simple_async_result_set_error (data->simple,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED_HANDLED,
|
|
+ "Operation was cancelled");
|
|
+ g_simple_async_result_complete (data->simple);
|
|
+}
|
|
+
|
|
+static void
|
|
+mount_cb (GduDevice *device,
|
|
+ gchar *mount_point,
|
|
+ GError *error,
|
|
+ gpointer user_data);
|
|
+
|
|
+static void
|
|
+mount_obtain_authz_cb (GObject *source_object,
|
|
+ GAsyncResult *res,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ MountOpData *data = user_data;
|
|
+ gboolean obtained_authz;
|
|
+ GError *error;
|
|
+
|
|
+ /* if we've already aborted due to device removal / cancellation, just bail out */
|
|
+ if (data->is_cancelled)
|
|
+ goto bailout;
|
|
+
|
|
+ error = NULL;
|
|
+ obtained_authz = _obtain_authz_finish (res, &error);
|
|
+
|
|
+ if (!obtained_authz)
|
|
+ {
|
|
+ /* be quiet if the daemon is inhibited */
|
|
+ if (error->code == GDU_ERROR_INHIBITED)
|
|
+ {
|
|
+ error->domain = G_IO_ERROR;
|
|
+ error->code = G_IO_ERROR_FAILED_HANDLED;
|
|
+ }
|
|
+ g_simple_async_result_set_from_error (data->simple, error);
|
|
+ g_simple_async_result_complete (data->simple);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* got the authz, now try again */
|
|
+ gdu_device_op_filesystem_mount (data->device_to_mount, mount_cb, data);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ bailout:
|
|
+ data->volume->pending_mount_op = NULL;
|
|
+ mount_op_data_unref (data);
|
|
+
|
|
+ out:
|
|
+ ;
|
|
+}
|
|
+
|
|
+static void
|
|
+mount_cb (GduDevice *device,
|
|
+ gchar *mount_point,
|
|
+ GError *error,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ MountOpData *data = user_data;
|
|
+
|
|
+ /* if we've already aborted due to device removal / cancellation, just bail out */
|
|
+ if (data->is_cancelled)
|
|
+ goto bailout;
|
|
+
|
|
+ if (error != NULL)
|
|
+ {
|
|
+ PolKitAction *pk_action;
|
|
+ PolKitResult pk_result;
|
|
+
|
|
+ /* only attempt to show authentication dialog if we have a mount operation */
|
|
+ if (data->mount_operation != NULL && gdu_error_check_polkit_not_authorized (error,
|
|
+ &pk_action,
|
|
+ &pk_result))
|
|
+ {
|
|
+ if (pk_result != POLKIT_RESULT_NO && pk_result != POLKIT_RESULT_UNKNOWN)
|
|
+ {
|
|
+ const gchar *action_id;
|
|
+ /* try to obtain the authorization */
|
|
+ polkit_action_get_action_id (pk_action, (char **) &action_id);
|
|
+ _obtain_authz (action_id,
|
|
+ data->cancellable,
|
|
+ mount_obtain_authz_cb,
|
|
+ data);
|
|
+ goto out;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_simple_async_result_set_from_error (data->simple, error);
|
|
+ }
|
|
+ polkit_action_unref (pk_action);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* be quiet if the daemon is inhibited */
|
|
+ if (error->code == GDU_ERROR_INHIBITED)
|
|
+ {
|
|
+ error->domain = G_IO_ERROR;
|
|
+ error->code = G_IO_ERROR_FAILED_HANDLED;
|
|
+ }
|
|
+ g_simple_async_result_set_from_error (data->simple, error);
|
|
+ }
|
|
+ g_error_free (error);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_free (mount_point);
|
|
+ }
|
|
+
|
|
+ g_simple_async_result_complete (data->simple);
|
|
+
|
|
+ bailout:
|
|
+ data->volume->pending_mount_op = NULL;
|
|
+ mount_op_data_unref (data);
|
|
+
|
|
+ out:
|
|
+ ;
|
|
+}
|
|
+
|
|
+static void
|
|
+mount_cleartext_device (MountOpData *data,
|
|
+ const gchar *object_path_of_cleartext_device)
|
|
+{
|
|
+ GduPool *pool;
|
|
+
|
|
+ /* if we've already aborted due to device removal / cancellation, just bail out */
|
|
+ if (data->is_cancelled)
|
|
+ {
|
|
+ mount_op_data_unref (data);
|
|
+ goto bailout;
|
|
+ }
|
|
+
|
|
+ pool = gdu_presentable_get_pool (GDU_PRESENTABLE (data->volume->gdu_volume));
|
|
+
|
|
+ data->device_to_mount = gdu_pool_get_by_object_path (pool, object_path_of_cleartext_device);
|
|
+ if (data->device_to_mount == NULL)
|
|
+ {
|
|
+ g_simple_async_result_set_error (data->simple,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED,
|
|
+ "Successfully unlocked encrypted volume but cleartext device does not exist");
|
|
+ g_simple_async_result_complete (data->simple);
|
|
+ data->volume->pending_mount_op = NULL;
|
|
+ mount_op_data_unref (data);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ gdu_device_op_filesystem_mount (data->device_to_mount, mount_cb, data);
|
|
+ }
|
|
+
|
|
+ g_object_unref (pool);
|
|
+
|
|
+ bailout:
|
|
+ ;
|
|
+}
|
|
+
|
|
+static void
|
|
+unlock_from_keyring_cb (GduDevice *device,
|
|
+ char *object_path_of_cleartext_device,
|
|
+ GError *error,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ MountOpData *data = user_data;
|
|
+
|
|
+ /* if we've already aborted due to device removal / cancellation, just bail out */
|
|
+ if (data->is_cancelled)
|
|
+ {
|
|
+ mount_op_data_unref (data);
|
|
+ goto bailout;
|
|
+ }
|
|
+
|
|
+ if (error != NULL)
|
|
+ {
|
|
+ /*g_debug ("keyring password didn't work: %s", error->message);*/
|
|
+
|
|
+ /* The password we retrieved from the keyring didn't work. So go ahead and prompt
|
|
+ * the user.
|
|
+ */
|
|
+ mount_with_mount_operation (data);
|
|
+
|
|
+ g_error_free (error);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ mount_cleartext_device (data, object_path_of_cleartext_device);
|
|
+ g_free (object_path_of_cleartext_device);
|
|
+ }
|
|
+
|
|
+ bailout:
|
|
+ ;
|
|
+}
|
|
+
|
|
+static void
|
|
+unlock_cb (GduDevice *device,
|
|
+ gchar *object_path_of_cleartext_device,
|
|
+ GError *error,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ MountOpData *data = user_data;
|
|
+
|
|
+ /* if we've already aborted due to device removal / cancellation, just bail out */
|
|
+ if (data->is_cancelled)
|
|
+ {
|
|
+ mount_op_data_unref (data);
|
|
+ goto bailout;
|
|
+ }
|
|
+
|
|
+ if (error != NULL)
|
|
+ {
|
|
+ /* be quiet if the daemon is inhibited */
|
|
+ if (error->code == GDU_ERROR_INHIBITED)
|
|
+ {
|
|
+ error->domain = G_IO_ERROR;
|
|
+ error->code = G_IO_ERROR_FAILED_HANDLED;
|
|
+ }
|
|
+ g_simple_async_result_set_from_error (data->simple, error);
|
|
+ g_error_free (error);
|
|
+ g_simple_async_result_complete (data->simple);
|
|
+ data->volume->pending_mount_op = NULL;
|
|
+ mount_op_data_unref (data);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ GPasswordSave password_save;
|
|
+ const gchar *password;
|
|
+
|
|
+ password_save = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (device), "password-save"));
|
|
+ password = g_object_get_data (G_OBJECT (device), "password");
|
|
+
|
|
+ if (password != NULL)
|
|
+ {
|
|
+ switch (password_save)
|
|
+ {
|
|
+ case G_PASSWORD_SAVE_FOR_SESSION:
|
|
+ gdu_util_save_secret (device, password, TRUE);
|
|
+ break;
|
|
+
|
|
+ case G_PASSWORD_SAVE_PERMANENTLY:
|
|
+ gdu_util_save_secret (device, password, FALSE);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ /* do nothing */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* now we have a cleartext device; update the GVolume details to show that */
|
|
+ if (update_volume (data->volume))
|
|
+ emit_changed (data->volume);
|
|
+
|
|
+ mount_cleartext_device (data, object_path_of_cleartext_device);
|
|
+ g_free (object_path_of_cleartext_device);
|
|
+ }
|
|
+
|
|
+ bailout:
|
|
+
|
|
+ /* scrub the password */
|
|
+ g_object_set_data (G_OBJECT (device), "password-save", NULL);
|
|
+ g_object_set_data (G_OBJECT (device), "password", NULL);
|
|
+}
|
|
+
|
|
+static void
|
|
+scrub_n_free_string (char *password)
|
|
+{
|
|
+ memset (password, '\0', strlen (password));
|
|
+ g_free (password);
|
|
+}
|
|
+
|
|
+static void
|
|
+mount_operation_reply (GMountOperation *mount_operation,
|
|
+ GMountOperationResult result,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ MountOpData *data = user_data;
|
|
+ GduDevice *device;
|
|
+ const gchar *password;
|
|
+
|
|
+ /* if we've already aborted due to device removal, just bail out */
|
|
+ if (data->is_cancelled)
|
|
+ {
|
|
+ mount_op_data_unref (data);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* we got what we wanted; don't listen to any other signals from the mount operation */
|
|
+ if (data->mount_operation_reply_handler_id != 0)
|
|
+ {
|
|
+ g_signal_handler_disconnect (data->mount_operation, data->mount_operation_reply_handler_id);
|
|
+ data->mount_operation_reply_handler_id = 0;
|
|
+ }
|
|
+
|
|
+ if (result != G_MOUNT_OPERATION_HANDLED)
|
|
+ {
|
|
+ if (result == G_MOUNT_OPERATION_ABORTED)
|
|
+ {
|
|
+ /* The user aborted the operation so consider it "handled" */
|
|
+ g_simple_async_result_set_error (data->simple,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED_HANDLED,
|
|
+ "Password dialog aborted (user should never see this error since it is G_IO_ERROR_FAILED_HANDLED)");
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_simple_async_result_set_error (data->simple,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_PERMISSION_DENIED,
|
|
+ "Expected G_MOUNT_OPERATION_HANDLED but got %d", result);
|
|
+ }
|
|
+ g_simple_async_result_complete (data->simple);
|
|
+ data->volume->pending_mount_op = NULL;
|
|
+ mount_op_data_unref (data);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ password = g_mount_operation_get_password (mount_operation);
|
|
+
|
|
+ device = gdu_presentable_get_device (GDU_PRESENTABLE (data->volume->gdu_volume));
|
|
+
|
|
+ g_object_set_data (G_OBJECT (device),
|
|
+ "password-save",
|
|
+ GINT_TO_POINTER (g_mount_operation_get_password_save (mount_operation)));
|
|
+ g_object_set_data_full (G_OBJECT (device),
|
|
+ "password",
|
|
+ g_strdup (password),
|
|
+ (GDestroyNotify) scrub_n_free_string);
|
|
+
|
|
+ gdu_device_op_luks_unlock (device, password, unlock_cb, data);
|
|
+
|
|
+ g_object_unref (device);
|
|
+
|
|
+ out:
|
|
+ ;
|
|
+}
|
|
+
|
|
+static void
|
|
+mount_with_mount_operation (MountOpData *data)
|
|
+{
|
|
+ gchar *message;
|
|
+ gchar *drive_name;
|
|
+ GduPresentable *toplevel;
|
|
+ GduDevice *device;
|
|
+
|
|
+ device = NULL;
|
|
+ drive_name = NULL;
|
|
+ message = NULL;
|
|
+ toplevel = NULL;
|
|
+
|
|
+ /* if we've already aborted due to device removal, just bail out */
|
|
+ if (data->is_cancelled)
|
|
+ {
|
|
+ mount_op_data_unref (data);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (data->mount_operation == NULL)
|
|
+ {
|
|
+ g_simple_async_result_set_error (data->simple,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED,
|
|
+ "Password required to access the encrypted data");
|
|
+ g_simple_async_result_complete (data->simple);
|
|
+ data->volume->pending_mount_op = NULL;
|
|
+ mount_op_data_unref (data);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ device = gdu_presentable_get_device (GDU_PRESENTABLE (data->volume->gdu_volume));
|
|
+
|
|
+ toplevel = gdu_presentable_get_toplevel (GDU_PRESENTABLE (data->volume->gdu_volume));
|
|
+ if (toplevel != NULL)
|
|
+ drive_name = gdu_presentable_get_name (toplevel);
|
|
+
|
|
+ /* This is going to look ass until bug 573416 is fixed. Unfortunately
|
|
+ * the gtk+ maintain has stated "oh, I stopped using luks" but that's
|
|
+ * more of a gtk+ problem ;-)
|
|
+ */
|
|
+ if (drive_name != NULL)
|
|
+ {
|
|
+ if (gdu_device_is_partition (device))
|
|
+ {
|
|
+ message = g_strdup_printf (_("Enter a password to unlock the volume\n"
|
|
+ "The device \"%s\" contains encrypted data on partition %d."),
|
|
+ drive_name,
|
|
+ gdu_device_partition_get_number (device));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ message = g_strdup_printf (_("Enter a password to unlock the volume\n"
|
|
+ "The device \"%s\" contains encrypted data."),
|
|
+ drive_name);
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ message = g_strdup_printf (_("Enter a password to unlock the volume\n"
|
|
+ "The device %s contains encrypted data."),
|
|
+ gdu_device_get_device_file (device));
|
|
+ }
|
|
+
|
|
+ data->mount_operation_reply_handler_id = g_signal_connect (data->mount_operation,
|
|
+ "reply",
|
|
+ G_CALLBACK (mount_operation_reply),
|
|
+ data);
|
|
+
|
|
+ g_signal_emit_by_name (data->mount_operation,
|
|
+ "ask-password",
|
|
+ message,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ G_ASK_PASSWORD_NEED_PASSWORD |
|
|
+ G_ASK_PASSWORD_SAVING_SUPPORTED);
|
|
+
|
|
+ out:
|
|
+ g_free (drive_name);
|
|
+ g_free (message);
|
|
+ if (device != NULL)
|
|
+ g_object_unref (device);
|
|
+ if (toplevel != NULL)
|
|
+ g_object_unref (toplevel);
|
|
+}
|
|
+
|
|
+static void
|
|
+cancelled_cb (GCancellable *cancellable,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ if (volume->pending_mount_op != NULL)
|
|
+ {
|
|
+ cancel_pending_mount_op (volume->pending_mount_op);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_volume_mount (GVolume *_volume,
|
|
+ GMountMountFlags flags,
|
|
+ GMountOperation *mount_operation,
|
|
+ GCancellable *cancellable,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduVolume *volume = G_GDU_VOLUME (_volume);
|
|
+ GSimpleAsyncResult *simple;
|
|
+ GduDevice *device;
|
|
+ GduPool *pool;
|
|
+ const gchar *usage;
|
|
+ const gchar *type;
|
|
+ MountOpData *data;
|
|
+
|
|
+ pool = NULL;
|
|
+ device = NULL;
|
|
+
|
|
+ if (volume->pending_mount_op != NULL)
|
|
+ {
|
|
+ simple = g_simple_async_result_new_error (G_OBJECT (volume),
|
|
+ callback,
|
|
+ user_data,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED,
|
|
+ "A mount operation is already pending");
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->gdu_volume));
|
|
+ pool = gdu_device_get_pool (device);
|
|
+
|
|
+ /* Makes no sense to mount
|
|
+ *
|
|
+ * - blank discs since these already have a burn:/// mount
|
|
+ * - other things that are already mounted
|
|
+ *
|
|
+ * Unfortunately Nautilus will try to do this anyway. For now, just return success for
|
|
+ * such requests.
|
|
+ */
|
|
+ if (gdu_device_optical_disc_get_is_blank (device) || gdu_device_is_mounted (device))
|
|
+ {
|
|
+ simple = g_simple_async_result_new (G_OBJECT (volume),
|
|
+ callback,
|
|
+ user_data,
|
|
+ g_gdu_volume_mount);
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ data = g_new0 (MountOpData, 1);
|
|
+
|
|
+ data->volume = g_object_ref (volume);
|
|
+
|
|
+ data->simple = g_simple_async_result_new (G_OBJECT (volume),
|
|
+ callback,
|
|
+ user_data,
|
|
+ g_gdu_volume_mount);
|
|
+
|
|
+ data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
|
|
+
|
|
+ data->mount_operation = mount_operation != NULL ? g_object_ref (mount_operation) : NULL;
|
|
+
|
|
+ if (data->cancellable != NULL)
|
|
+ data->cancelled_handler_id = g_signal_connect (data->cancellable, "cancelled", G_CALLBACK (cancelled_cb), volume);
|
|
+
|
|
+ volume->pending_mount_op = data;
|
|
+
|
|
+ /* if the device is already unlocked, just attempt to mount it */
|
|
+ if (volume->cleartext_gdu_volume != NULL)
|
|
+ {
|
|
+ GduDevice *luks_cleartext_volume_device;
|
|
+ const gchar *object_path_of_cleartext_device;
|
|
+
|
|
+ luks_cleartext_volume_device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->cleartext_gdu_volume));
|
|
+
|
|
+ object_path_of_cleartext_device = gdu_device_get_object_path (luks_cleartext_volume_device);
|
|
+
|
|
+ mount_cleartext_device (data, object_path_of_cleartext_device);
|
|
+
|
|
+ g_object_unref (luks_cleartext_volume_device);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ usage = gdu_device_id_get_usage (device);
|
|
+ type = gdu_device_id_get_type (device);
|
|
+ if (g_strcmp0 (usage, "crypto") == 0 && g_strcmp0 (type, "crypto_LUKS") == 0)
|
|
+ {
|
|
+ gchar *password;
|
|
+
|
|
+ /* if we have the secret in the keyring, try with that first */
|
|
+ password = gdu_util_get_secret (device);
|
|
+ if (password != NULL)
|
|
+ {
|
|
+ gdu_device_op_luks_unlock (device, password, unlock_from_keyring_cb, data);
|
|
+
|
|
+ scrub_n_free_string (password);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* don't put up a password dialog if the daemon is inhibited */
|
|
+ if (gdu_pool_is_daemon_inhibited (pool))
|
|
+ {
|
|
+ g_simple_async_result_set_error (data->simple,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED_HANDLED,
|
|
+ "Daemon is currently inhibited");
|
|
+ g_simple_async_result_complete (data->simple);
|
|
+ volume->pending_mount_op = NULL;
|
|
+ mount_op_data_unref (data);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ mount_with_mount_operation (data);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ data->device_to_mount = g_object_ref (device);
|
|
+ gdu_device_op_filesystem_mount (data->device_to_mount, mount_cb, data);
|
|
+ }
|
|
+
|
|
+ out:
|
|
+ if (pool != NULL)
|
|
+ g_object_unref (pool);
|
|
+ if (device != NULL)
|
|
+ g_object_unref (device);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_volume_mount_finish (GVolume *volume,
|
|
+ GAsyncResult *result,
|
|
+ GError **error)
|
|
+{
|
|
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
|
|
+
|
|
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_gdu_volume_mount);
|
|
+
|
|
+ return !g_simple_async_result_propagate_error (simple, error);
|
|
+}
|
|
+
|
|
+/* ---------------------------------------------------------------------------------------------------- */
|
|
+
|
|
+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_object_unref (data->object);
|
|
+ g_free (data);
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_volume_eject (GVolume *volume,
|
|
+ GMountUnmountFlags flags,
|
|
+ GCancellable *cancellable,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduVolume *gdu_volume = G_GDU_VOLUME (volume);
|
|
+ GGduDrive *drive;
|
|
+
|
|
+ drive = NULL;
|
|
+ if (gdu_volume->drive != NULL)
|
|
+ drive = g_object_ref (gdu_volume->drive);
|
|
+
|
|
+ if (drive != NULL)
|
|
+ {
|
|
+ EjectWrapperOp *data;
|
|
+ data = g_new0 (EjectWrapperOp, 1);
|
|
+ data->object = g_object_ref (volume);
|
|
+ data->callback = callback;
|
|
+ data->user_data = user_data;
|
|
+ g_drive_eject (G_DRIVE (drive), flags, cancellable, eject_wrapper_callback, data);
|
|
+ g_object_unref (drive);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ GSimpleAsyncResult *simple;
|
|
+ simple = g_simple_async_result_new_error (G_OBJECT (volume),
|
|
+ callback,
|
|
+ user_data,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED,
|
|
+ _("Operation not supported by backend"));
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+g_gdu_volume_eject_finish (GVolume *volume,
|
|
+ GAsyncResult *result,
|
|
+ GError **error)
|
|
+{
|
|
+ GGduVolume *gdu_volume = G_GDU_VOLUME (volume);
|
|
+ gboolean res;
|
|
+
|
|
+ res = TRUE;
|
|
+ if (gdu_volume->drive != NULL)
|
|
+ {
|
|
+ res = g_drive_eject_finish (G_DRIVE (gdu_volume->drive), result, error);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
|
|
+ res = FALSE;
|
|
+ }
|
|
+
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static char *
|
|
+g_gdu_volume_get_identifier (GVolume *_volume,
|
|
+ const char *kind)
|
|
+{
|
|
+ GGduVolume *volume = G_GDU_VOLUME (_volume);
|
|
+ GduDevice *device;
|
|
+ const gchar *label;
|
|
+ const gchar *uuid;
|
|
+ gchar *id;
|
|
+
|
|
+ id = NULL;
|
|
+
|
|
+ device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->gdu_volume));
|
|
+
|
|
+ label = gdu_device_id_get_label (device);
|
|
+ uuid = gdu_device_id_get_uuid (device);
|
|
+
|
|
+ g_object_unref (device);
|
|
+
|
|
+ if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == 0)
|
|
+ id = g_strdup (volume->device_file);
|
|
+ else if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_LABEL) == 0)
|
|
+ id = strlen (label) > 0 ? g_strdup (label) : NULL;
|
|
+ else if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UUID) == 0)
|
|
+ id = strlen (uuid) > 0 ? g_strdup (uuid) : NULL;
|
|
+
|
|
+ return id;
|
|
+}
|
|
+
|
|
+static char **
|
|
+g_gdu_volume_enumerate_identifiers (GVolume *_volume)
|
|
+{
|
|
+ GGduVolume *volume = G_GDU_VOLUME (_volume);
|
|
+ GduDevice *device;
|
|
+ GPtrArray *p;
|
|
+ const gchar *label;
|
|
+ const gchar *uuid;
|
|
+
|
|
+ device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->gdu_volume));
|
|
+
|
|
+ label = gdu_device_id_get_label (device);
|
|
+ uuid = gdu_device_id_get_uuid (device);
|
|
+
|
|
+ g_object_unref (device);
|
|
+
|
|
+ p = g_ptr_array_new ();
|
|
+ g_ptr_array_add (p, g_strdup (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE));
|
|
+ if (strlen (label) > 0)
|
|
+ g_ptr_array_add (p, g_strdup (G_VOLUME_IDENTIFIER_KIND_LABEL));
|
|
+ if (strlen (uuid) > 0)
|
|
+ g_ptr_array_add (p, g_strdup (G_VOLUME_IDENTIFIER_KIND_UUID));
|
|
+
|
|
+ g_ptr_array_add (p, NULL);
|
|
+
|
|
+ return (gchar **) g_ptr_array_free (p, FALSE);
|
|
+}
|
|
+
|
|
+static GFile *
|
|
+g_gdu_volume_get_activation_root (GVolume *_volume)
|
|
+{
|
|
+ GGduVolume *volume = G_GDU_VOLUME (_volume);
|
|
+ return volume->activation_root != NULL ? g_object_ref (volume->activation_root) : NULL;
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_volume_volume_iface_init (GVolumeIface *iface)
|
|
+{
|
|
+ iface->get_name = g_gdu_volume_get_name;
|
|
+ iface->get_icon = g_gdu_volume_get_icon;
|
|
+ iface->get_uuid = g_gdu_volume_get_uuid;
|
|
+ iface->get_drive = g_gdu_volume_get_drive;
|
|
+ iface->get_mount = g_gdu_volume_get_mount;
|
|
+ iface->can_mount = g_gdu_volume_can_mount;
|
|
+ iface->can_eject = g_gdu_volume_can_eject;
|
|
+ iface->should_automount = g_gdu_volume_should_automount;
|
|
+ iface->mount_fn = g_gdu_volume_mount;
|
|
+ iface->mount_finish = g_gdu_volume_mount_finish;
|
|
+ iface->eject = g_gdu_volume_eject;
|
|
+ iface->eject_finish = g_gdu_volume_eject_finish;
|
|
+ iface->get_identifier = g_gdu_volume_get_identifier;
|
|
+ iface->enumerate_identifiers = g_gdu_volume_enumerate_identifiers;
|
|
+ iface->get_activation_root = g_gdu_volume_get_activation_root;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+g_gdu_volume_has_device_file (GGduVolume *volume,
|
|
+ const gchar *device_file)
|
|
+{
|
|
+ const gchar *_device_file;
|
|
+
|
|
+ _device_file = volume->device_file;
|
|
+
|
|
+ if (volume->cleartext_gdu_volume != NULL)
|
|
+ {
|
|
+ GduDevice *luks_cleartext_volume_device;
|
|
+ luks_cleartext_volume_device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->cleartext_gdu_volume));
|
|
+ _device_file = gdu_device_get_device_file (luks_cleartext_volume_device);
|
|
+ g_object_unref (luks_cleartext_volume_device);
|
|
+ }
|
|
+
|
|
+ return g_strcmp0 (_device_file, device_file) == 0;
|
|
+}
|
|
+
|
|
+
|
|
+gboolean
|
|
+g_gdu_volume_has_mount_path (GGduVolume *volume,
|
|
+ const char *mount_path)
|
|
+{
|
|
+ GduDevice *device;
|
|
+ GduPresentable *presentable;
|
|
+ gboolean ret;
|
|
+
|
|
+ ret = FALSE;
|
|
+
|
|
+ presentable = g_gdu_volume_get_presentable_with_cleartext (volume);
|
|
+ if (presentable != NULL)
|
|
+ {
|
|
+ device = gdu_presentable_get_device (presentable);
|
|
+ if (device != NULL)
|
|
+ {
|
|
+ ret = g_strcmp0 (gdu_device_get_mount_path (device), mount_path) == 0;
|
|
+ g_object_unref (device);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+g_gdu_volume_has_uuid (GGduVolume *volume,
|
|
+ const char *uuid)
|
|
+{
|
|
+ const gchar *_uuid;
|
|
+
|
|
+ _uuid = volume->uuid;
|
|
+
|
|
+ if (volume->cleartext_gdu_volume != NULL)
|
|
+ {
|
|
+ GduDevice *luks_cleartext_volume_device;
|
|
+ luks_cleartext_volume_device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->cleartext_gdu_volume));
|
|
+ _uuid = gdu_device_id_get_uuid (luks_cleartext_volume_device);
|
|
+ g_object_unref (luks_cleartext_volume_device);
|
|
+ }
|
|
+
|
|
+ return g_strcmp0 (_uuid, uuid) == 0;
|
|
+}
|
|
+
|
|
+GduPresentable *
|
|
+g_gdu_volume_get_presentable (GGduVolume *volume)
|
|
+{
|
|
+ return GDU_PRESENTABLE (volume->gdu_volume);
|
|
+}
|
|
+
|
|
+GduPresentable *
|
|
+g_gdu_volume_get_presentable_with_cleartext (GGduVolume *volume)
|
|
+{
|
|
+ GduVolume *ret;
|
|
+
|
|
+ ret = volume->cleartext_gdu_volume;
|
|
+ if (ret == NULL)
|
|
+ ret = volume->gdu_volume;
|
|
+
|
|
+ return GDU_PRESENTABLE (ret);
|
|
+}
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/ggduvolume.h 2009-03-01 23:48:05.000000000 -0500
|
|
@@ -0,0 +1,78 @@
|
|
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
+/* gvfs - extensions for gio
|
|
+ *
|
|
+ * Copyright (C) 2006-2009 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: David Zeuthen <davidz@redhat.com>
|
|
+ */
|
|
+
|
|
+#ifndef __G_GDU_VOLUME_H__
|
|
+#define __G_GDU_VOLUME_H__
|
|
+
|
|
+#include <glib-object.h>
|
|
+#include <gio/gio.h>
|
|
+
|
|
+#include "ggduvolumemonitor.h"
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+#define G_TYPE_GDU_VOLUME (g_gdu_volume_get_type ())
|
|
+#define G_GDU_VOLUME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_GDU_VOLUME, GGduVolume))
|
|
+#define G_GDU_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_GDU_VOLUME, GGduVolumeClass))
|
|
+#define G_IS_GDU_VOLUME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_GDU_VOLUME))
|
|
+#define G_IS_GDU_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_GDU_VOLUME))
|
|
+
|
|
+typedef struct _GGduVolumeClass GGduVolumeClass;
|
|
+
|
|
+struct _GGduVolumeClass {
|
|
+ GObjectClass parent_class;
|
|
+};
|
|
+
|
|
+GType g_gdu_volume_get_type (void) G_GNUC_CONST;
|
|
+
|
|
+GGduVolume *g_gdu_volume_new (GVolumeMonitor *volume_monitor,
|
|
+ GduVolume *gdu_volume,
|
|
+ GGduDrive *drive,
|
|
+ GFile *activation_root);
|
|
+
|
|
+void g_gdu_volume_set_mount (GGduVolume *volume,
|
|
+ GGduMount *mount);
|
|
+void g_gdu_volume_unset_mount (GGduVolume *volume,
|
|
+ GGduMount *mount);
|
|
+
|
|
+void g_gdu_volume_set_drive (GGduVolume *volume,
|
|
+ GGduDrive *drive);
|
|
+void g_gdu_volume_unset_drive (GGduVolume *volume,
|
|
+ GGduDrive *drive);
|
|
+
|
|
+void g_gdu_volume_removed (GGduVolume *volume);
|
|
+
|
|
+gboolean g_gdu_volume_has_mount_path (GGduVolume *volume,
|
|
+ const char *mount_path);
|
|
+gboolean g_gdu_volume_has_uuid (GGduVolume *volume,
|
|
+ const char *uuid);
|
|
+gboolean g_gdu_volume_has_device_file (GGduVolume *volume,
|
|
+ const gchar *device_file);
|
|
+
|
|
+GduPresentable *g_gdu_volume_get_presentable (GGduVolume *volume);
|
|
+
|
|
+GduPresentable *g_gdu_volume_get_presentable_with_cleartext (GGduVolume *volume);
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __G_GDU_VOLUME_H__ */
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/ggduvolumemonitor.c 2009-03-04 16:29:02.000000000 -0500
|
|
@@ -0,0 +1,1432 @@
|
|
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
+/* gvfs - extensions for gio
|
|
+ *
|
|
+ * Copyright (C) 2006-2009 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: David Zeuthen <davidz@redhat.com>
|
|
+ */
|
|
+
|
|
+#include <config.h>
|
|
+
|
|
+#include <limits.h>
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+
|
|
+#include <glib.h>
|
|
+#include <glib/gi18n-lib.h>
|
|
+#include <gio/gio.h>
|
|
+
|
|
+#include "ggduvolumemonitor.h"
|
|
+#include "ggdumount.h"
|
|
+#include "ggduvolume.h"
|
|
+#include "ggdudrive.h"
|
|
+
|
|
+static GGduVolumeMonitor *the_volume_monitor = NULL;
|
|
+
|
|
+struct _GGduVolumeMonitor {
|
|
+ GNativeVolumeMonitor parent;
|
|
+
|
|
+ GUnixMountMonitor *mount_monitor;
|
|
+
|
|
+ GduPool *pool;
|
|
+
|
|
+ GList *last_optical_disc_devices;
|
|
+ GList *last_mountpoints;
|
|
+ GList *last_mounts;
|
|
+
|
|
+ GList *drives;
|
|
+ GList *volumes;
|
|
+ GList *mounts;
|
|
+
|
|
+ /* we keep volumes/mounts for blank and audio discs separate to handle e.g. mixed discs properly */
|
|
+ GList *disc_volumes;
|
|
+ GList *disc_mounts;
|
|
+
|
|
+};
|
|
+
|
|
+static void mountpoints_changed (GUnixMountMonitor *mount_monitor,
|
|
+ gpointer user_data);
|
|
+static void mounts_changed (GUnixMountMonitor *mount_monitor,
|
|
+ gpointer user_data);
|
|
+
|
|
+static void presentable_added (GduPool *pool,
|
|
+ GduPresentable *presentable,
|
|
+ gpointer user_data);
|
|
+static void presentable_removed (GduPool *pool,
|
|
+ GduPresentable *presentable,
|
|
+ gpointer user_data);
|
|
+
|
|
+static void update_all (GGduVolumeMonitor *monitor,
|
|
+ gboolean emit_changes);
|
|
+
|
|
+static void update_drives (GGduVolumeMonitor *monitor,
|
|
+ GList **added_drives,
|
|
+ GList **removed_drives);
|
|
+static void update_volumes (GGduVolumeMonitor *monitor,
|
|
+ GList **added_volumes,
|
|
+ GList **removed_volumes);
|
|
+static void update_mounts (GGduVolumeMonitor *monitor,
|
|
+ GList **added_mounts,
|
|
+ GList **removed_mounts);
|
|
+static void update_discs (GGduVolumeMonitor *monitor,
|
|
+ GList **added_volumes,
|
|
+ GList **removed_volumes,
|
|
+ GList **added_mounts,
|
|
+ GList **removed_mounts);
|
|
+
|
|
+
|
|
+G_DEFINE_TYPE (GGduVolumeMonitor, g_gdu_volume_monitor, G_TYPE_NATIVE_VOLUME_MONITOR)
|
|
+
|
|
+static void
|
|
+list_free (GList *objects)
|
|
+{
|
|
+ g_list_foreach (objects, (GFunc)g_object_unref, NULL);
|
|
+ g_list_free (objects);
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_volume_monitor_dispose (GObject *object)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor;
|
|
+
|
|
+ monitor = G_GDU_VOLUME_MONITOR (object);
|
|
+
|
|
+ the_volume_monitor = NULL;
|
|
+
|
|
+ if (G_OBJECT_CLASS (g_gdu_volume_monitor_parent_class)->dispose)
|
|
+ (*G_OBJECT_CLASS (g_gdu_volume_monitor_parent_class)->dispose) (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_volume_monitor_finalize (GObject *object)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor;
|
|
+
|
|
+ monitor = G_GDU_VOLUME_MONITOR (object);
|
|
+
|
|
+ g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mountpoints_changed, monitor);
|
|
+ g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mounts_changed, monitor);
|
|
+ g_signal_handlers_disconnect_by_func (monitor->mount_monitor, presentable_added, monitor);
|
|
+ g_signal_handlers_disconnect_by_func (monitor->mount_monitor, presentable_removed, monitor);
|
|
+
|
|
+ g_object_unref (monitor->mount_monitor);
|
|
+
|
|
+ g_object_unref (monitor->pool);
|
|
+
|
|
+ list_free (monitor->last_optical_disc_devices);
|
|
+ list_free (monitor->last_mountpoints);
|
|
+ g_list_foreach (monitor->last_mounts,
|
|
+ (GFunc)g_unix_mount_free, NULL);
|
|
+ g_list_free (monitor->last_mounts);
|
|
+
|
|
+ list_free (monitor->drives);
|
|
+ list_free (monitor->volumes);
|
|
+ list_free (monitor->mounts);
|
|
+
|
|
+ list_free (monitor->disc_volumes);
|
|
+ list_free (monitor->disc_mounts);
|
|
+
|
|
+ if (G_OBJECT_CLASS (g_gdu_volume_monitor_parent_class)->finalize)
|
|
+ (*G_OBJECT_CLASS (g_gdu_volume_monitor_parent_class)->finalize) (object);
|
|
+}
|
|
+
|
|
+static GList *
|
|
+get_mounts (GVolumeMonitor *volume_monitor)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor;
|
|
+ GList *l, *ll;
|
|
+
|
|
+ monitor = G_GDU_VOLUME_MONITOR (volume_monitor);
|
|
+
|
|
+ l = g_list_copy (monitor->mounts);
|
|
+ ll = g_list_copy (monitor->disc_mounts);
|
|
+ l = g_list_concat (l, ll);
|
|
+
|
|
+ g_list_foreach (l, (GFunc)g_object_ref, NULL);
|
|
+
|
|
+ return l;
|
|
+}
|
|
+
|
|
+static GList *
|
|
+get_volumes (GVolumeMonitor *volume_monitor)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor;
|
|
+ GList *l, *ll;
|
|
+
|
|
+ monitor = G_GDU_VOLUME_MONITOR (volume_monitor);
|
|
+
|
|
+ l = g_list_copy (monitor->volumes);
|
|
+ ll = g_list_copy (monitor->disc_volumes);
|
|
+ l = g_list_concat (l, ll);
|
|
+
|
|
+ g_list_foreach (l, (GFunc)g_object_ref, NULL);
|
|
+
|
|
+ return l;
|
|
+}
|
|
+
|
|
+static GList *
|
|
+get_connected_drives (GVolumeMonitor *volume_monitor)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor;
|
|
+ GList *l;
|
|
+
|
|
+ monitor = G_GDU_VOLUME_MONITOR (volume_monitor);
|
|
+
|
|
+ l = g_list_copy (monitor->drives);
|
|
+ g_list_foreach (l, (GFunc)g_object_ref, NULL);
|
|
+
|
|
+ return l;
|
|
+}
|
|
+
|
|
+static GVolume *
|
|
+get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor;
|
|
+ GGduVolume *volume;
|
|
+ GList *l;
|
|
+
|
|
+ monitor = G_GDU_VOLUME_MONITOR (volume_monitor);
|
|
+
|
|
+ volume = NULL;
|
|
+
|
|
+ for (l = monitor->volumes; l != NULL; l = l->next)
|
|
+ {
|
|
+ volume = l->data;
|
|
+ if (g_gdu_volume_has_uuid (volume, uuid))
|
|
+ goto found;
|
|
+ }
|
|
+
|
|
+ for (l = monitor->disc_volumes; l != NULL; l = l->next)
|
|
+ {
|
|
+ volume = l->data;
|
|
+ if (g_gdu_volume_has_uuid (volume, uuid))
|
|
+ goto found;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+
|
|
+ found:
|
|
+
|
|
+ g_object_ref (volume);
|
|
+
|
|
+ return (GVolume *)volume;
|
|
+}
|
|
+
|
|
+static GMount *
|
|
+get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor;
|
|
+ GGduMount *mount;
|
|
+ GList *l;
|
|
+
|
|
+ monitor = G_GDU_VOLUME_MONITOR (volume_monitor);
|
|
+
|
|
+ mount = NULL;
|
|
+
|
|
+ for (l = monitor->mounts; l != NULL; l = l->next)
|
|
+ {
|
|
+ mount = l->data;
|
|
+ if (g_gdu_mount_has_uuid (mount, uuid))
|
|
+ goto found;
|
|
+ }
|
|
+
|
|
+ for (l = monitor->disc_mounts; l != NULL; l = l->next)
|
|
+ {
|
|
+ mount = l->data;
|
|
+ if (g_gdu_mount_has_uuid (mount, uuid))
|
|
+ goto found;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+
|
|
+ found:
|
|
+
|
|
+ g_object_ref (mount);
|
|
+
|
|
+ return (GMount *)mount;
|
|
+}
|
|
+
|
|
+static GMount *
|
|
+get_mount_for_mount_path (const char *mount_path,
|
|
+ GCancellable *cancellable)
|
|
+{
|
|
+ GMount *mount;
|
|
+ GGduMount *gdu_mount;
|
|
+ GGduVolumeMonitor *volume_monitor;
|
|
+
|
|
+ if (the_volume_monitor == NULL)
|
|
+ {
|
|
+ /* Dammit, no monitor is set up.. so we have to create one, find
|
|
+ * what the user asks for and throw it away again.
|
|
+ *
|
|
+ * What a waste - especially considering that there's IO
|
|
+ * involved in doing this: connect to the system message bus;
|
|
+ * IPC to DeviceKit-disks etc etc
|
|
+ */
|
|
+ volume_monitor = G_GDU_VOLUME_MONITOR (g_gdu_volume_monitor_new ());
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ volume_monitor = g_object_ref (the_volume_monitor);
|
|
+ }
|
|
+
|
|
+ mount = NULL;
|
|
+
|
|
+ /* creation of the volume monitor might actually fail */
|
|
+ if (volume_monitor != NULL)
|
|
+ {
|
|
+ GList *l;
|
|
+
|
|
+ for (l = volume_monitor->mounts; l != NULL; l = l->next)
|
|
+ {
|
|
+ gdu_mount = l->data;
|
|
+
|
|
+ if (g_gdu_mount_has_mount_path (gdu_mount, mount_path))
|
|
+ {
|
|
+ mount = g_object_ref (gdu_mount);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ g_object_unref (volume_monitor);
|
|
+
|
|
+ return (GMount *) mount;
|
|
+}
|
|
+
|
|
+static void
|
|
+mountpoints_changed (GUnixMountMonitor *mount_monitor,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data);
|
|
+
|
|
+ update_all (monitor, TRUE);
|
|
+}
|
|
+
|
|
+static void
|
|
+mounts_changed (GUnixMountMonitor *mount_monitor,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data);
|
|
+
|
|
+ update_all (monitor, TRUE);
|
|
+}
|
|
+
|
|
+static void
|
|
+presentable_added (GduPool *pool,
|
|
+ GduPresentable *presentable,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data);
|
|
+
|
|
+ /*g_debug ("presentable_added %p: %s", presentable, gdu_presentable_get_id (presentable));*/
|
|
+
|
|
+ update_all (monitor, TRUE);
|
|
+}
|
|
+
|
|
+static void
|
|
+presentable_removed (GduPool *pool,
|
|
+ GduPresentable *presentable,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data);
|
|
+
|
|
+ /*g_debug ("presentable_removed %p: %s", presentable, gdu_presentable_get_id (presentable));*/
|
|
+
|
|
+ update_all (monitor, TRUE);
|
|
+}
|
|
+
|
|
+static void
|
|
+presentable_changed (GduPool *pool,
|
|
+ GduPresentable *presentable,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data);
|
|
+
|
|
+ /*g_debug ("presentable_changed %p: %s", presentable, gdu_presentable_get_id (presentable));*/
|
|
+
|
|
+ update_all (monitor, TRUE);
|
|
+}
|
|
+
|
|
+static void
|
|
+presentable_job_changed (GduPool *pool,
|
|
+ GduPresentable *presentable,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data);
|
|
+
|
|
+ /*g_debug ("presentable_job_changed %p: %s", presentable, gdu_presentable_get_id (presentable));*/
|
|
+
|
|
+ update_all (monitor, TRUE);
|
|
+}
|
|
+
|
|
+static GObject *
|
|
+g_gdu_volume_monitor_constructor (GType type,
|
|
+ guint n_construct_properties,
|
|
+ GObjectConstructParam *construct_properties)
|
|
+{
|
|
+ GObject *object;
|
|
+ GGduVolumeMonitor *monitor;
|
|
+ GGduVolumeMonitorClass *klass;
|
|
+ GObjectClass *parent_class;
|
|
+
|
|
+ if (the_volume_monitor != NULL)
|
|
+ {
|
|
+ object = g_object_ref (the_volume_monitor);
|
|
+ return object;
|
|
+ }
|
|
+
|
|
+ /*g_warning ("creating gdu vm");*/
|
|
+
|
|
+ object = NULL;
|
|
+
|
|
+ /* Invoke parent constructor. */
|
|
+ klass = G_GDU_VOLUME_MONITOR_CLASS (g_type_class_peek (G_TYPE_GDU_VOLUME_MONITOR));
|
|
+ parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
|
|
+ object = parent_class->constructor (type,
|
|
+ n_construct_properties,
|
|
+ construct_properties);
|
|
+
|
|
+ monitor = G_GDU_VOLUME_MONITOR (object);
|
|
+
|
|
+ monitor->mount_monitor = g_unix_mount_monitor_new ();
|
|
+
|
|
+ g_signal_connect (monitor->mount_monitor,
|
|
+ "mounts_changed",
|
|
+ G_CALLBACK (mounts_changed),
|
|
+ monitor);
|
|
+
|
|
+ g_signal_connect (monitor->mount_monitor,
|
|
+ "mountpoints_changed",
|
|
+ G_CALLBACK (mountpoints_changed),
|
|
+ monitor);
|
|
+
|
|
+ monitor->pool = gdu_pool_new ();
|
|
+
|
|
+ g_signal_connect (monitor->pool,
|
|
+ "presentable_added",
|
|
+ G_CALLBACK (presentable_added),
|
|
+ monitor);
|
|
+
|
|
+ g_signal_connect (monitor->pool,
|
|
+ "presentable_removed",
|
|
+ G_CALLBACK (presentable_removed),
|
|
+ monitor);
|
|
+
|
|
+ g_signal_connect (monitor->pool,
|
|
+ "presentable_changed",
|
|
+ G_CALLBACK (presentable_changed),
|
|
+ monitor);
|
|
+
|
|
+ g_signal_connect (monitor->pool,
|
|
+ "presentable_job_changed",
|
|
+ G_CALLBACK (presentable_job_changed),
|
|
+ monitor);
|
|
+
|
|
+ update_all (monitor, FALSE);
|
|
+
|
|
+ the_volume_monitor = monitor;
|
|
+
|
|
+ return object;
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_volume_monitor_init (GGduVolumeMonitor *monitor)
|
|
+{
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+is_supported (void)
|
|
+{
|
|
+ /* TODO: return FALSE if DeviceKit-disks is not available */
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static void
|
|
+g_gdu_volume_monitor_class_init (GGduVolumeMonitorClass *klass)
|
|
+{
|
|
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
+ GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
|
|
+ GNativeVolumeMonitorClass *native_class = G_NATIVE_VOLUME_MONITOR_CLASS (klass);
|
|
+
|
|
+ gobject_class->constructor = g_gdu_volume_monitor_constructor;
|
|
+ gobject_class->finalize = g_gdu_volume_monitor_finalize;
|
|
+ gobject_class->dispose = g_gdu_volume_monitor_dispose;
|
|
+
|
|
+ monitor_class->get_mounts = get_mounts;
|
|
+ monitor_class->get_volumes = get_volumes;
|
|
+ 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->is_supported = is_supported;
|
|
+
|
|
+ native_class->get_mount_for_mount_path = get_mount_for_mount_path;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * g_gdu_volume_monitor_new:
|
|
+ *
|
|
+ * Returns: a new #GVolumeMonitor.
|
|
+ **/
|
|
+GVolumeMonitor *
|
|
+g_gdu_volume_monitor_new (void)
|
|
+{
|
|
+ GGduVolumeMonitor *monitor;
|
|
+
|
|
+ monitor = g_object_new (G_TYPE_GDU_VOLUME_MONITOR, NULL);
|
|
+
|
|
+ return G_VOLUME_MONITOR (monitor);
|
|
+}
|
|
+
|
|
+static void
|
|
+diff_sorted_lists (GList *list1,
|
|
+ GList *list2,
|
|
+ GCompareFunc compare,
|
|
+ GList **added,
|
|
+ GList **removed)
|
|
+{
|
|
+ int order;
|
|
+
|
|
+ *added = *removed = NULL;
|
|
+
|
|
+ while (list1 != NULL &&
|
|
+ list2 != NULL)
|
|
+ {
|
|
+ order = (*compare) (list1->data, list2->data);
|
|
+ if (order < 0)
|
|
+ {
|
|
+ *removed = g_list_prepend (*removed, list1->data);
|
|
+ list1 = list1->next;
|
|
+ }
|
|
+ else if (order > 0)
|
|
+ {
|
|
+ *added = g_list_prepend (*added, list2->data);
|
|
+ list2 = list2->next;
|
|
+ }
|
|
+ else
|
|
+ { /* same item */
|
|
+ list1 = list1->next;
|
|
+ list2 = list2->next;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ while (list1 != NULL)
|
|
+ {
|
|
+ *removed = g_list_prepend (*removed, list1->data);
|
|
+ list1 = list1->next;
|
|
+ }
|
|
+ while (list2 != NULL)
|
|
+ {
|
|
+ *added = g_list_prepend (*added, list2->data);
|
|
+ list2 = list2->next;
|
|
+ }
|
|
+}
|
|
+
|
|
+static GGduVolume *
|
|
+find_volume_for_mount_path (GGduVolumeMonitor *monitor,
|
|
+ const char *mount_path)
|
|
+{
|
|
+ GList *l;
|
|
+ GGduVolume *found;
|
|
+
|
|
+ found = NULL;
|
|
+
|
|
+ for (l = monitor->volumes; l != NULL; l = l->next)
|
|
+ {
|
|
+ GGduVolume *volume = l->data;
|
|
+
|
|
+ if (g_gdu_volume_has_mount_path (volume, mount_path))
|
|
+ {
|
|
+ found = volume;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return found;
|
|
+}
|
|
+
|
|
+static GGduMount *
|
|
+find_mount_by_mount_path (GGduVolumeMonitor *monitor,
|
|
+ const char *mount_path)
|
|
+{
|
|
+ GList *l;
|
|
+
|
|
+ for (l = monitor->mounts; l != NULL; l = l->next)
|
|
+ {
|
|
+ GGduMount *mount = l->data;
|
|
+
|
|
+ if (g_gdu_mount_has_mount_path (mount, mount_path))
|
|
+ return mount;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/* TODO: move to gio */
|
|
+static gboolean
|
|
+_g_unix_mount_point_guess_should_display (GUnixMountPoint *mount_point)
|
|
+{
|
|
+ const char *mount_path;
|
|
+
|
|
+ mount_path = g_unix_mount_point_get_mount_path (mount_point);
|
|
+
|
|
+ /* Never display internal mountpoints */
|
|
+ if (g_unix_is_mount_path_system_internal (mount_path))
|
|
+ return FALSE;
|
|
+
|
|
+ /* Only display things in /media (which are generally user mountable)
|
|
+ and home dir (fuse stuff) */
|
|
+ if (g_str_has_prefix (mount_path, "/media/"))
|
|
+ return TRUE;
|
|
+
|
|
+ if (g_str_has_prefix (mount_path, g_get_home_dir ()))
|
|
+ return TRUE;
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static GUnixMountPoint *
|
|
+get_mount_point_for_device (GduDevice *d, GList *fstab_mount_points)
|
|
+{
|
|
+ GList *l;
|
|
+ const gchar *device_file;
|
|
+ const gchar *mount_path;
|
|
+ GUnixMountPoint *ret;
|
|
+
|
|
+ ret = NULL;
|
|
+
|
|
+ mount_path = gdu_device_get_mount_path (d);
|
|
+
|
|
+ device_file = gdu_device_get_device_file (d);
|
|
+
|
|
+ for (l = fstab_mount_points; l != NULL; l = l->next)
|
|
+ {
|
|
+ GUnixMountPoint *mount_point = l->data;
|
|
+ const gchar *device_file;
|
|
+ const gchar *fstab_mount_path;
|
|
+
|
|
+ fstab_mount_path = g_unix_mount_point_get_mount_path (mount_point);
|
|
+ if (g_strcmp0 (mount_path, fstab_mount_path) == 0)
|
|
+ {
|
|
+ ret = mount_point;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ device_file = g_unix_mount_point_get_device_path (mount_point);
|
|
+ if (g_str_has_prefix (device_file, "LABEL="))
|
|
+ {
|
|
+ if (g_strcmp0 (device_file + 6, gdu_device_id_get_label (d)) == 0)
|
|
+ {
|
|
+ ret = mount_point;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+ else if (g_str_has_prefix (device_file, "UUID="))
|
|
+ {
|
|
+ if (g_ascii_strcasecmp (device_file + 5, gdu_device_id_get_uuid (d)) == 0)
|
|
+ {
|
|
+ ret = mount_point;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ char resolved_device_file[PATH_MAX];
|
|
+
|
|
+ /* handle symlinks such as /dev/disk/by-uuid/47C2-1994 */
|
|
+ if (realpath (device_file, resolved_device_file) != NULL &&
|
|
+ g_strcmp0 (resolved_device_file, device_file) == 0)
|
|
+ {
|
|
+ ret = mount_point;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ out:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+should_mount_be_ignored (GduPool *pool, GduDevice *d)
|
|
+{
|
|
+ gboolean ret;
|
|
+ const gchar *mount_path;
|
|
+ GUnixMountEntry *mount_entry;
|
|
+
|
|
+ ret = FALSE;
|
|
+
|
|
+ if (gdu_device_is_mounted (d))
|
|
+ goto out;
|
|
+
|
|
+ mount_path = gdu_device_get_mount_path (d);
|
|
+ if (mount_path == NULL || strlen (mount_path) == 0)
|
|
+ goto out;
|
|
+
|
|
+ mount_entry = g_unix_mount_at (mount_path, NULL);
|
|
+ if (mount_entry != NULL)
|
|
+ {
|
|
+ if (!g_unix_mount_guess_should_display (mount_entry))
|
|
+ {
|
|
+ ret = TRUE;
|
|
+ }
|
|
+ g_unix_mount_free (mount_entry);
|
|
+ }
|
|
+
|
|
+ out:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+should_volume_be_ignored (GduPool *pool, GduVolume *volume, GList *fstab_mount_points)
|
|
+{
|
|
+ GduDevice *device;
|
|
+ gboolean ret;
|
|
+ const gchar *usage;
|
|
+ const gchar *type;
|
|
+
|
|
+ ret = TRUE;
|
|
+ device = NULL;
|
|
+
|
|
+ device = gdu_presentable_get_device (GDU_PRESENTABLE (volume));
|
|
+
|
|
+ usage = gdu_device_id_get_usage (device);
|
|
+ type = gdu_device_id_get_type (device);
|
|
+
|
|
+ if (g_strcmp0 (usage, "filesystem") == 0)
|
|
+ {
|
|
+ GUnixMountPoint *mount_point;
|
|
+
|
|
+ /* don't ignore volumes with a mountable filesystem unless
|
|
+ *
|
|
+ * - volume is referenced in /etc/fstab and deemed to be ignored
|
|
+ *
|
|
+ * - volume is mounted and should_mount_be_ignored() deems it should be ignored
|
|
+ *
|
|
+ * - volume is a cleartext LUKS device as the cryptotext LUKS volume will morph
|
|
+ * into the cleartext volume when unlocked (see ggduvolume.c)
|
|
+ */
|
|
+
|
|
+ if (gdu_device_is_luks_cleartext (device))
|
|
+ goto out;
|
|
+
|
|
+ mount_point = get_mount_point_for_device (device, fstab_mount_points);
|
|
+ if (mount_point != NULL && !_g_unix_mount_point_guess_should_display (mount_point))
|
|
+ goto out;
|
|
+
|
|
+ if (gdu_device_is_mounted (device))
|
|
+ {
|
|
+ ret = should_mount_be_ignored (pool, device);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = FALSE;
|
|
+
|
|
+ }
|
|
+ else if (g_strcmp0 (usage, "crypto") == 0 && g_strcmp0 (type, "crypto_LUKS") == 0)
|
|
+ {
|
|
+ /* don't ignore LUKS volumes */
|
|
+ ret = FALSE;
|
|
+ }
|
|
+
|
|
+ out:
|
|
+
|
|
+ g_object_unref (device);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+should_drive_be_ignored (GduPool *pool, GduDrive *d, GList *fstab_mount_points)
|
|
+{
|
|
+ GduDevice *device;
|
|
+ gboolean ret;
|
|
+ gboolean has_volumes;
|
|
+ gboolean all_volumes_are_ignored;
|
|
+ GList *enclosed;
|
|
+ GList *l;
|
|
+
|
|
+ ret = FALSE;
|
|
+ device = NULL;
|
|
+ enclosed = NULL;
|
|
+
|
|
+ device = gdu_presentable_get_device (GDU_PRESENTABLE (d));
|
|
+
|
|
+ /* the GduDevice for an activatable drive (such as RAID) is NULL if the drive is not
|
|
+ * activated; never ignore these
|
|
+ */
|
|
+ if (device == NULL)
|
|
+ goto out;
|
|
+
|
|
+ /* never ignore drives with removable media */
|
|
+ if (gdu_device_is_removable (device))
|
|
+ goto out;
|
|
+
|
|
+ has_volumes = FALSE;
|
|
+ all_volumes_are_ignored = TRUE;
|
|
+
|
|
+ /* never ignore a drive if it has volumes that we don't want to ignore */
|
|
+ enclosed = gdu_pool_get_enclosed_presentables (pool, GDU_PRESENTABLE (d));
|
|
+ for (l = enclosed; l != NULL; l = l->next)
|
|
+ {
|
|
+ GduPresentable *enclosed_presentable = GDU_PRESENTABLE (l->data);
|
|
+
|
|
+ /* There might be other presentables that GduVolume objects; for example GduVolumeHole */
|
|
+ if (GDU_IS_VOLUME (enclosed_presentable))
|
|
+ {
|
|
+ GduVolume *volume = GDU_VOLUME (enclosed_presentable);
|
|
+
|
|
+ has_volumes = TRUE;
|
|
+
|
|
+ if (!should_volume_be_ignored (pool, volume, fstab_mount_points))
|
|
+ {
|
|
+ all_volumes_are_ignored = FALSE;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = has_volumes && all_volumes_are_ignored;
|
|
+
|
|
+ out:
|
|
+ g_list_foreach (enclosed, (GFunc) g_object_unref, NULL);
|
|
+ g_list_free (enclosed);
|
|
+
|
|
+ if (device != NULL)
|
|
+ g_object_unref (device);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void
|
|
+list_emit (GGduVolumeMonitor *monitor,
|
|
+ const char *monitor_signal,
|
|
+ const char *object_signal,
|
|
+ GList *objects)
|
|
+{
|
|
+ GList *l;
|
|
+
|
|
+ for (l = objects; l != NULL; l = l->next)
|
|
+ {
|
|
+ g_signal_emit_by_name (monitor, monitor_signal, l->data);
|
|
+ if (object_signal)
|
|
+ g_signal_emit_by_name (l->data, object_signal);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+update_all (GGduVolumeMonitor *monitor,
|
|
+ gboolean emit_changes)
|
|
+{
|
|
+ GList *added_drives, *removed_drives;
|
|
+ GList *added_volumes, *removed_volumes;
|
|
+ GList *added_mounts, *removed_mounts;
|
|
+
|
|
+ added_drives = NULL;
|
|
+ removed_drives = NULL;
|
|
+ added_volumes = NULL;
|
|
+ removed_volumes = NULL;
|
|
+ added_mounts = NULL;
|
|
+ removed_mounts = NULL;
|
|
+
|
|
+ update_drives (monitor, &added_drives, &removed_drives);
|
|
+ update_volumes (monitor, &added_volumes, &removed_volumes);
|
|
+ update_mounts (monitor, &added_mounts, &removed_mounts);
|
|
+ update_discs (monitor,
|
|
+ &added_volumes, &removed_volumes,
|
|
+ &added_mounts, &removed_mounts);
|
|
+
|
|
+ if (emit_changes)
|
|
+ {
|
|
+ list_emit (monitor,
|
|
+ "drive_disconnected", NULL,
|
|
+ removed_drives);
|
|
+ list_emit (monitor,
|
|
+ "drive_connected", NULL,
|
|
+ added_drives);
|
|
+
|
|
+ list_emit (monitor,
|
|
+ "volume_removed", "removed",
|
|
+ removed_volumes);
|
|
+ list_emit (monitor,
|
|
+ "volume_added", NULL,
|
|
+ added_volumes);
|
|
+
|
|
+ list_emit (monitor,
|
|
+ "mount_removed", "unmounted",
|
|
+ removed_mounts);
|
|
+ list_emit (monitor,
|
|
+ "mount_added", NULL,
|
|
+ added_mounts);
|
|
+ }
|
|
+
|
|
+ list_free (removed_drives);
|
|
+ list_free (added_drives);
|
|
+ list_free (removed_volumes);
|
|
+ list_free (added_volumes);
|
|
+ list_free (removed_mounts);
|
|
+ list_free (added_mounts);
|
|
+}
|
|
+
|
|
+static GGduMount *
|
|
+find_disc_mount_for_volume (GGduVolumeMonitor *monitor,
|
|
+ GGduVolume *volume)
|
|
+{
|
|
+ GList *l;
|
|
+
|
|
+ for (l = monitor->disc_mounts; l != NULL; l = l->next)
|
|
+ {
|
|
+ GGduMount *mount = G_GDU_MOUNT (l->data);
|
|
+
|
|
+ if (g_gdu_mount_has_volume (mount, volume))
|
|
+ return mount;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static GGduVolume *
|
|
+find_disc_volume_for_device_file (GGduVolumeMonitor *monitor,
|
|
+ const gchar *device_file)
|
|
+{
|
|
+ GList *l;
|
|
+
|
|
+ for (l = monitor->disc_volumes; l != NULL; l = l->next)
|
|
+ {
|
|
+ GGduVolume *volume = G_GDU_VOLUME (l->data);
|
|
+
|
|
+ if (g_gdu_volume_has_device_file (volume, device_file))
|
|
+ return volume;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static GGduVolume *
|
|
+find_volume_for_device_file (GGduVolumeMonitor *monitor,
|
|
+ const gchar *device_file)
|
|
+{
|
|
+ GList *l;
|
|
+
|
|
+ for (l = monitor->volumes; l != NULL; l = l->next)
|
|
+ {
|
|
+ GGduVolume *volume = G_GDU_VOLUME (l->data);
|
|
+
|
|
+ if (g_gdu_volume_has_device_file (volume, device_file))
|
|
+ return volume;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static GGduDrive *
|
|
+find_drive_by_device_file (GGduVolumeMonitor *monitor,
|
|
+ const gchar *device_file)
|
|
+{
|
|
+ GList *l;
|
|
+
|
|
+ for (l = monitor->drives; l != NULL; l = l->next)
|
|
+ {
|
|
+ GGduDrive *drive = G_GDU_DRIVE (l->data);
|
|
+
|
|
+ if (g_gdu_drive_has_device_file (drive, device_file))
|
|
+ return drive;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static GGduDrive *
|
|
+find_drive_by_presentable (GGduVolumeMonitor *monitor,
|
|
+ GduPresentable *presentable)
|
|
+{
|
|
+ GList *l;
|
|
+
|
|
+ for (l = monitor->drives; l != NULL; l = l->next)
|
|
+ {
|
|
+ GGduDrive *drive = G_GDU_DRIVE (l->data);
|
|
+
|
|
+ if (g_gdu_drive_has_presentable (drive, presentable))
|
|
+ return drive;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void
|
|
+update_drives (GGduVolumeMonitor *monitor,
|
|
+ GList **added_drives,
|
|
+ GList **removed_drives)
|
|
+{
|
|
+ GList *cur_drives;
|
|
+ GList *new_drives;
|
|
+ GList *removed, *added;
|
|
+ GList *l, *ll;
|
|
+ GGduDrive *drive;
|
|
+ GList *fstab_mount_points;
|
|
+
|
|
+ fstab_mount_points = g_unix_mount_points_get (NULL);
|
|
+
|
|
+ cur_drives = NULL;
|
|
+ for (l = monitor->drives; l != NULL; l = l->next)
|
|
+ cur_drives = g_list_prepend (cur_drives, g_gdu_drive_get_presentable (G_GDU_DRIVE (l->data)));
|
|
+
|
|
+ /* remove devices we want to ignore - we do it here so we get to reevaluate
|
|
+ * on the next update whether they should still be ignored
|
|
+ */
|
|
+ new_drives = gdu_pool_get_presentables (monitor->pool);
|
|
+ for (l = new_drives; l != NULL; l = ll)
|
|
+ {
|
|
+ GduPresentable *p = GDU_PRESENTABLE (l->data);
|
|
+ ll = l->next;
|
|
+ if (!GDU_IS_DRIVE (p) || should_drive_be_ignored (monitor->pool, GDU_DRIVE (p), fstab_mount_points))
|
|
+ {
|
|
+ g_object_unref (p);
|
|
+ new_drives = g_list_delete_link (new_drives, l);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cur_drives = g_list_sort (cur_drives, (GCompareFunc) gdu_presentable_compare);
|
|
+ new_drives = g_list_sort (new_drives, (GCompareFunc) gdu_presentable_compare);
|
|
+ diff_sorted_lists (cur_drives,
|
|
+ new_drives, (GCompareFunc) gdu_presentable_compare,
|
|
+ &added, &removed);
|
|
+
|
|
+ for (l = removed; l != NULL; l = l->next)
|
|
+ {
|
|
+ GduPresentable *p = GDU_PRESENTABLE (l->data);
|
|
+ GduDevice *d;
|
|
+
|
|
+ d = gdu_presentable_get_device (p);
|
|
+
|
|
+ drive = find_drive_by_presentable (monitor, p);
|
|
+ if (drive != NULL)
|
|
+ {
|
|
+ /*g_debug ("removing drive %s", gdu_presentable_get_id (p));*/
|
|
+ g_gdu_drive_disconnected (drive);
|
|
+ monitor->drives = g_list_remove (monitor->drives, drive);
|
|
+ *removed_drives = g_list_prepend (*removed_drives, drive);
|
|
+ }
|
|
+ if (d != NULL)
|
|
+ g_object_unref (d);
|
|
+ }
|
|
+
|
|
+ for (l = added; l != NULL; l = l->next)
|
|
+ {
|
|
+ GduPresentable *p = GDU_PRESENTABLE (l->data);
|
|
+ GduDevice *d;
|
|
+
|
|
+ d = gdu_presentable_get_device (p);
|
|
+
|
|
+ drive = find_drive_by_presentable (monitor, p);
|
|
+ if (drive == NULL)
|
|
+ {
|
|
+ /*g_debug ("adding drive %s", gdu_presentable_get_id (p));*/
|
|
+ drive = g_gdu_drive_new (G_VOLUME_MONITOR (monitor), p);
|
|
+ if (drive != NULL)
|
|
+ {
|
|
+ monitor->drives = g_list_prepend (monitor->drives, drive);
|
|
+ *added_drives = g_list_prepend (*added_drives, g_object_ref (drive));
|
|
+ }
|
|
+ }
|
|
+ if (d != NULL)
|
|
+ g_object_unref (d);
|
|
+ }
|
|
+
|
|
+ g_list_free (added);
|
|
+ g_list_free (removed);
|
|
+
|
|
+ g_list_free (cur_drives);
|
|
+
|
|
+ g_list_foreach (new_drives, (GFunc) g_object_unref, NULL);
|
|
+ g_list_free (new_drives);
|
|
+
|
|
+ g_list_foreach (fstab_mount_points, (GFunc) g_unix_mount_point_free, NULL);
|
|
+ g_list_free (fstab_mount_points);
|
|
+}
|
|
+
|
|
+static void
|
|
+update_volumes (GGduVolumeMonitor *monitor,
|
|
+ GList **added_volumes,
|
|
+ GList **removed_volumes)
|
|
+{
|
|
+ GList *cur_volumes;
|
|
+ GList *new_volumes;
|
|
+ GList *removed, *added;
|
|
+ GList *l, *ll;
|
|
+ GGduVolume *volume;
|
|
+ GGduDrive *drive;
|
|
+ GList *fstab_mount_points;
|
|
+
|
|
+ fstab_mount_points = g_unix_mount_points_get (NULL);
|
|
+
|
|
+ cur_volumes = NULL;
|
|
+ for (l = monitor->volumes; l != NULL; l = l->next)
|
|
+ cur_volumes = g_list_prepend (cur_volumes, g_gdu_volume_get_presentable (G_GDU_VOLUME (l->data)));
|
|
+
|
|
+ /* remove devices we want to ignore - we do it here so we get to reevaluate
|
|
+ * on the next update whether they should still be ignored
|
|
+ */
|
|
+ new_volumes = gdu_pool_get_presentables (monitor->pool);
|
|
+ for (l = new_volumes; l != NULL; l = ll)
|
|
+ {
|
|
+ GduPresentable *p = GDU_PRESENTABLE (l->data);
|
|
+ ll = l->next;
|
|
+ if (!GDU_IS_VOLUME (p) || should_volume_be_ignored (monitor->pool, GDU_VOLUME (p), fstab_mount_points))
|
|
+ {
|
|
+ g_object_unref (p);
|
|
+ new_volumes = g_list_delete_link (new_volumes, l);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cur_volumes = g_list_sort (cur_volumes, (GCompareFunc) gdu_presentable_compare);
|
|
+ new_volumes = g_list_sort (new_volumes, (GCompareFunc) gdu_presentable_compare);
|
|
+ diff_sorted_lists (cur_volumes,
|
|
+ new_volumes, (GCompareFunc) gdu_presentable_compare,
|
|
+ &added, &removed);
|
|
+
|
|
+ for (l = removed; l != NULL; l = l->next)
|
|
+ {
|
|
+ GduPresentable *p = GDU_PRESENTABLE (l->data);
|
|
+ GduDevice *d;
|
|
+
|
|
+ d = gdu_presentable_get_device (p);
|
|
+
|
|
+ volume = find_volume_for_device_file (monitor, gdu_device_get_device_file (d));
|
|
+ if (volume != NULL)
|
|
+ {
|
|
+ /*g_debug ("removing volume %s", gdu_device_get_device_file (d));*/
|
|
+ g_gdu_volume_removed (volume);
|
|
+ monitor->volumes = g_list_remove (monitor->volumes, volume);
|
|
+ *removed_volumes = g_list_prepend (*removed_volumes, volume);
|
|
+ }
|
|
+ g_object_unref (d);
|
|
+ }
|
|
+
|
|
+ for (l = added; l != NULL; l = l->next)
|
|
+ {
|
|
+ GduPresentable *p = GDU_PRESENTABLE (l->data);
|
|
+ GduDevice *d;
|
|
+
|
|
+ d = gdu_presentable_get_device (p);
|
|
+
|
|
+ volume = find_volume_for_device_file (monitor, gdu_device_get_device_file (d));
|
|
+ if (volume == NULL)
|
|
+ {
|
|
+ GduPresentable *toplevel_presentable;
|
|
+
|
|
+ toplevel_presentable = gdu_presentable_get_toplevel (p);
|
|
+ if (toplevel_presentable != NULL)
|
|
+ {
|
|
+ GduDevice *toplevel_device;
|
|
+
|
|
+ toplevel_device = gdu_presentable_get_device (toplevel_presentable);
|
|
+ drive = find_drive_by_device_file (monitor, gdu_device_get_device_file (toplevel_device));
|
|
+ /*g_debug ("adding volume %s (drive %s)",
|
|
+ gdu_device_get_device_file (d),
|
|
+ gdu_device_get_device_file (toplevel_device));*/
|
|
+ g_object_unref (toplevel_device);
|
|
+ g_object_unref (toplevel_presentable);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ drive = NULL;
|
|
+ /*g_debug ("adding volume %s (no drive)", gdu_device_get_device_file (d));*/
|
|
+ }
|
|
+
|
|
+ volume = g_gdu_volume_new (G_VOLUME_MONITOR (monitor),
|
|
+ GDU_VOLUME (p),
|
|
+ drive,
|
|
+ NULL);
|
|
+ if (volume != NULL)
|
|
+ {
|
|
+ monitor->volumes = g_list_prepend (monitor->volumes, volume);
|
|
+ *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ g_object_unref (d);
|
|
+ }
|
|
+
|
|
+ g_list_free (added);
|
|
+ g_list_free (removed);
|
|
+
|
|
+ g_list_foreach (new_volumes, (GFunc) g_object_unref, NULL);
|
|
+ g_list_free (new_volumes);
|
|
+
|
|
+ g_list_free (cur_volumes);
|
|
+
|
|
+ g_list_foreach (fstab_mount_points, (GFunc) g_unix_mount_point_free, NULL);
|
|
+ g_list_free (fstab_mount_points);
|
|
+}
|
|
+
|
|
+static void
|
|
+update_mounts (GGduVolumeMonitor *monitor,
|
|
+ GList **added_mounts,
|
|
+ GList **removed_mounts)
|
|
+{
|
|
+ GList *new_mounts;
|
|
+ GList *removed, *added;
|
|
+ GList *l, *ll;
|
|
+ GGduMount *mount;
|
|
+ GGduVolume *volume;
|
|
+ const char *device_file;
|
|
+ const char *mount_path;
|
|
+
|
|
+ new_mounts = g_unix_mounts_get (NULL);
|
|
+
|
|
+ /* remove mounts we want to ignore - we do it here so we get to reevaluate
|
|
+ * on the next update whether they should still be ignored
|
|
+ */
|
|
+ for (l = new_mounts; l != NULL; l = ll)
|
|
+ {
|
|
+ GUnixMountEntry *mount_entry = l->data;
|
|
+ ll = l->next;
|
|
+
|
|
+ /* keep in sync with should_mount_be_ignored() */
|
|
+ if (!g_unix_mount_guess_should_display (mount_entry))
|
|
+ {
|
|
+ g_unix_mount_free (mount_entry);
|
|
+ new_mounts = g_list_delete_link (new_mounts, l);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ new_mounts = g_list_sort (new_mounts, (GCompareFunc) g_unix_mount_compare);
|
|
+
|
|
+ diff_sorted_lists (monitor->last_mounts,
|
|
+ new_mounts, (GCompareFunc) g_unix_mount_compare,
|
|
+ &added, &removed);
|
|
+
|
|
+ for (l = removed; l != NULL; l = l->next)
|
|
+ {
|
|
+ GUnixMountEntry *mount_entry = l->data;
|
|
+
|
|
+ mount = find_mount_by_mount_path (monitor, g_unix_mount_get_mount_path (mount_entry));
|
|
+ if (mount)
|
|
+ {
|
|
+ /*g_debug ("removing mount %s", g_unix_mount_get_device_path (mount_entry));*/
|
|
+ g_gdu_mount_unmounted (mount);
|
|
+ monitor->mounts = g_list_remove (monitor->mounts, mount);
|
|
+
|
|
+ *removed_mounts = g_list_prepend (*removed_mounts, mount);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (l = added; l != NULL; l = l->next)
|
|
+ {
|
|
+ GUnixMountEntry *mount_entry = l->data;
|
|
+
|
|
+ device_file = g_unix_mount_get_device_path (mount_entry);
|
|
+ mount_path = g_unix_mount_get_mount_path (mount_entry);
|
|
+ volume = find_volume_for_device_file (monitor, device_file);
|
|
+ if (volume == NULL)
|
|
+ volume = find_volume_for_mount_path (monitor, mount_path);
|
|
+
|
|
+ /*g_debug ("adding mount %s (vol %p)", g_unix_mount_get_device_path (mount_entry), volume);*/
|
|
+ mount = g_gdu_mount_new (G_VOLUME_MONITOR (monitor), mount_entry, volume);
|
|
+ if (mount)
|
|
+ {
|
|
+ monitor->mounts = g_list_prepend (monitor->mounts, mount);
|
|
+ *added_mounts = g_list_prepend (*added_mounts, g_object_ref (mount));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ g_list_free (added);
|
|
+ g_list_free (removed);
|
|
+ g_list_foreach (monitor->last_mounts,
|
|
+ (GFunc)g_unix_mount_free, NULL);
|
|
+ g_list_free (monitor->last_mounts);
|
|
+ monitor->last_mounts = new_mounts;
|
|
+}
|
|
+
|
|
+static void
|
|
+update_discs (GGduVolumeMonitor *monitor,
|
|
+ GList **added_volumes,
|
|
+ GList **removed_volumes,
|
|
+ GList **added_mounts,
|
|
+ GList **removed_mounts)
|
|
+{
|
|
+ GList *cur_discs;
|
|
+ GList *new_discs;
|
|
+ GList *removed, *added;
|
|
+ GList *l, *ll;
|
|
+ GGduDrive *drive;
|
|
+ GGduVolume *volume;
|
|
+ GGduMount *mount;
|
|
+
|
|
+ /* we also need to generate GVolume + GMount objects for
|
|
+ *
|
|
+ * - optical discs that have audio
|
|
+ * - optical discs that are blank
|
|
+ *
|
|
+ */
|
|
+
|
|
+ cur_discs = NULL;
|
|
+ for (l = monitor->disc_volumes; l != NULL; l = l->next)
|
|
+ cur_discs = g_list_prepend (cur_discs, g_gdu_volume_get_presentable (G_GDU_VOLUME (l->data)));
|
|
+
|
|
+ new_discs = gdu_pool_get_presentables (monitor->pool);
|
|
+ for (l = new_discs; l != NULL; l = ll)
|
|
+ {
|
|
+ GduPresentable *p = GDU_PRESENTABLE (l->data);
|
|
+ GduDevice *d;
|
|
+ gboolean ignore;
|
|
+
|
|
+ ll = l->next;
|
|
+ ignore = TRUE;
|
|
+
|
|
+ /* filter out everything but discs that are blank or has audio */
|
|
+ d = gdu_presentable_get_device (p);
|
|
+ if (GDU_IS_VOLUME (p) && d != NULL && gdu_device_is_optical_disc (d))
|
|
+ {
|
|
+ if (gdu_device_optical_disc_get_num_audio_tracks (d) > 0 || gdu_device_optical_disc_get_is_blank (d))
|
|
+ ignore = FALSE;
|
|
+ }
|
|
+
|
|
+ if (ignore)
|
|
+ {
|
|
+ g_object_unref (p);
|
|
+ new_discs = g_list_delete_link (new_discs, l);
|
|
+ }
|
|
+
|
|
+ if (d != NULL)
|
|
+ g_object_unref (d);
|
|
+ }
|
|
+
|
|
+ cur_discs = g_list_sort (cur_discs, (GCompareFunc) gdu_presentable_compare);
|
|
+ new_discs = g_list_sort (new_discs, (GCompareFunc) gdu_presentable_compare);
|
|
+ diff_sorted_lists (cur_discs, new_discs, (GCompareFunc) gdu_presentable_compare, &added, &removed);
|
|
+
|
|
+ for (l = removed; l != NULL; l = l->next)
|
|
+ {
|
|
+ GduPresentable *p = GDU_PRESENTABLE (l->data);
|
|
+ GduDevice *d;
|
|
+
|
|
+ d = gdu_presentable_get_device (p);
|
|
+
|
|
+ volume = find_disc_volume_for_device_file (monitor, gdu_device_get_device_file (d));
|
|
+ mount = find_disc_mount_for_volume (monitor, volume);
|
|
+
|
|
+ if (mount != NULL)
|
|
+ {
|
|
+ /*g_debug ("removing disc mount %s", gdu_device_get_device_file (d));*/
|
|
+ g_gdu_mount_unmounted (mount);
|
|
+ monitor->disc_mounts = g_list_remove (monitor->disc_mounts, mount);
|
|
+ *removed_mounts = g_list_prepend (*removed_mounts, mount);
|
|
+ }
|
|
+
|
|
+ if (volume != NULL)
|
|
+ {
|
|
+ /*g_debug ("removing disc volume %s", gdu_device_get_device_file (d));*/
|
|
+ g_gdu_volume_removed (volume);
|
|
+ monitor->disc_volumes = g_list_remove (monitor->disc_volumes, volume);
|
|
+ *removed_volumes = g_list_prepend (*removed_volumes, volume);
|
|
+ }
|
|
+
|
|
+ g_object_unref (d);
|
|
+ }
|
|
+
|
|
+ for (l = added; l != NULL; l = l->next)
|
|
+ {
|
|
+ GduPresentable *p = GDU_PRESENTABLE (l->data);
|
|
+ GduDevice *d;
|
|
+ gboolean is_blank;
|
|
+
|
|
+ d = gdu_presentable_get_device (p);
|
|
+
|
|
+ is_blank = gdu_device_optical_disc_get_is_blank (d);
|
|
+
|
|
+ volume = find_disc_volume_for_device_file (monitor, gdu_device_get_device_file (d));
|
|
+ if (volume == NULL)
|
|
+ {
|
|
+ GduPresentable *toplevel_presentable;
|
|
+
|
|
+ toplevel_presentable = gdu_presentable_get_toplevel (p);
|
|
+ if (toplevel_presentable != NULL)
|
|
+ {
|
|
+ GduDevice *toplevel_device;
|
|
+
|
|
+ toplevel_device = gdu_presentable_get_device (toplevel_presentable);
|
|
+ drive = find_drive_by_device_file (monitor, gdu_device_get_device_file (toplevel_device));
|
|
+ /*g_debug ("adding volume %s (drive %s)",
|
|
+ gdu_device_get_device_file (d),
|
|
+ gdu_device_get_device_file (toplevel_device));*/
|
|
+ g_object_unref (toplevel_device);
|
|
+ g_object_unref (toplevel_presentable);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ drive = NULL;
|
|
+ /*g_debug ("adding volume %s (no drive)", gdu_device_get_device_file (d));*/
|
|
+ }
|
|
+
|
|
+ mount = NULL;
|
|
+ if (is_blank)
|
|
+ {
|
|
+ volume = g_gdu_volume_new (G_VOLUME_MONITOR (monitor),
|
|
+ GDU_VOLUME (p),
|
|
+ drive,
|
|
+ NULL);
|
|
+ mount = g_gdu_mount_new (G_VOLUME_MONITOR (monitor),
|
|
+ NULL,
|
|
+ volume);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ gchar *uri;
|
|
+ gchar *device_basename;
|
|
+ GFile *activation_root;
|
|
+
|
|
+ /* the gvfsd-cdda backend uses URI's like these */
|
|
+ device_basename = g_path_get_basename (gdu_device_get_device_file (d));
|
|
+ uri = g_strdup_printf ("cdda://%s", device_basename);
|
|
+ activation_root = g_file_new_for_uri (uri);
|
|
+ g_free (device_basename);
|
|
+ g_free (uri);
|
|
+
|
|
+ volume = g_gdu_volume_new (G_VOLUME_MONITOR (monitor),
|
|
+ GDU_VOLUME (p),
|
|
+ drive,
|
|
+ activation_root);
|
|
+
|
|
+ g_object_unref (activation_root);
|
|
+ }
|
|
+
|
|
+ if (volume != NULL)
|
|
+ {
|
|
+ monitor->disc_volumes = g_list_prepend (monitor->disc_volumes, volume);
|
|
+ *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume));
|
|
+
|
|
+ if (mount != NULL)
|
|
+ {
|
|
+ monitor->disc_mounts = g_list_prepend (monitor->disc_mounts, mount);
|
|
+ *added_mounts = g_list_prepend (*added_mounts, g_object_ref (mount));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ g_object_unref (d);
|
|
+ }
|
|
+
|
|
+ g_list_free (added);
|
|
+ g_list_free (removed);
|
|
+
|
|
+ g_list_foreach (new_discs, (GFunc) g_object_unref, NULL);
|
|
+ g_list_free (new_discs);
|
|
+
|
|
+ g_list_free (cur_discs);
|
|
+}
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/ggduvolumemonitor.h 2009-03-01 23:49:48.000000000 -0500
|
|
@@ -0,0 +1,60 @@
|
|
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
+/* gvfs - extensions for gio
|
|
+ *
|
|
+ * Copyright (C) 2006-2009 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: David Zeuthen <davidz@redhat.com>
|
|
+ */
|
|
+
|
|
+#ifndef __G_GDU_VOLUME_MONITOR_H__
|
|
+#define __G_GDU_VOLUME_MONITOR_H__
|
|
+
|
|
+#include <glib-object.h>
|
|
+#include <gio/gio.h>
|
|
+#include <gio/gunixmounts.h>
|
|
+
|
|
+#include <gdu/gdu.h>
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+#define G_TYPE_GDU_VOLUME_MONITOR (g_gdu_volume_monitor_get_type ())
|
|
+#define G_GDU_VOLUME_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_GDU_VOLUME_MONITOR, GGduVolumeMonitor))
|
|
+#define G_GDU_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_GDU_VOLUME_MONITOR, GGduVolumeMonitorClass))
|
|
+#define G_IS_GDU_VOLUME_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_GDU_VOLUME_MONITOR))
|
|
+#define G_IS_GDU_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_GDU_VOLUME_MONITOR))
|
|
+
|
|
+typedef struct _GGduVolumeMonitor GGduVolumeMonitor;
|
|
+typedef struct _GGduVolumeMonitorClass GGduVolumeMonitorClass;
|
|
+
|
|
+/* Forward definitions */
|
|
+typedef struct _GGduDrive GGduDrive;
|
|
+typedef struct _GGduVolume GGduVolume;
|
|
+typedef struct _GGduMount GGduMount;
|
|
+
|
|
+struct _GGduVolumeMonitorClass {
|
|
+ GNativeVolumeMonitorClass parent_class;
|
|
+
|
|
+};
|
|
+
|
|
+GType g_gdu_volume_monitor_get_type (void) G_GNUC_CONST;
|
|
+
|
|
+GVolumeMonitor *g_gdu_volume_monitor_new (void);
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __G_GDU_VOLUME_MONITOR_H__ */
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/polkit.c 2009-03-01 23:47:50.000000000 -0500
|
|
@@ -0,0 +1,137 @@
|
|
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
+/* gvfs - extensions for gio
|
|
+ *
|
|
+ * Copyright (C) 2006-2009 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: David Zeuthen <davidz@redhat.com>
|
|
+ */
|
|
+
|
|
+#include <sys/types.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include <gdbusutils.h>
|
|
+
|
|
+#include "polkit.h"
|
|
+
|
|
+static void
|
|
+_obtain_authz_cb (DBusMessage *reply,
|
|
+ GError *error,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
|
|
+ gboolean gained_authz;
|
|
+ DBusError derror;
|
|
+
|
|
+ if (error != NULL) {
|
|
+ g_simple_async_result_set_from_error (simple, error);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ dbus_error_init (&derror);
|
|
+ if (!dbus_message_get_args (reply,
|
|
+ &derror,
|
|
+ DBUS_TYPE_BOOLEAN, &gained_authz,
|
|
+ DBUS_TYPE_INVALID))
|
|
+ {
|
|
+ /* no need to translate; this only happens if the auth agent is buggy */
|
|
+ g_simple_async_result_set_error (simple,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED,
|
|
+ "Error parsing reply for ObtainAuthorization(): %s: %s",
|
|
+ derror.name, derror.message);
|
|
+ dbus_error_free (&derror);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (!gained_authz && error == NULL)
|
|
+ {
|
|
+ /* no need to translate, is never shown */
|
|
+ g_simple_async_result_set_error (simple,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_FAILED_HANDLED,
|
|
+ "Didn't obtain authorization (bug in libgio user, it shouldn't display this error)");
|
|
+ }
|
|
+
|
|
+ out:
|
|
+ g_simple_async_result_complete (simple);
|
|
+ g_object_unref (simple);
|
|
+}
|
|
+
|
|
+
|
|
+gboolean
|
|
+_obtain_authz_finish (GAsyncResult *res,
|
|
+ GError **error)
|
|
+{
|
|
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
|
|
+
|
|
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == _obtain_authz);
|
|
+
|
|
+ if (g_simple_async_result_propagate_error (simple, error))
|
|
+ return FALSE;
|
|
+ else
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+void
|
|
+_obtain_authz (const gchar *action_id,
|
|
+ GCancellable *cancellable,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ DBusConnection *connection;
|
|
+ DBusMessage *message;
|
|
+ GSimpleAsyncResult *simple;
|
|
+ guint xid;
|
|
+ guint pid;
|
|
+ DBusError derror;
|
|
+
|
|
+ dbus_error_init (&derror);
|
|
+
|
|
+ /* this connection is already integrated and guaranteed to exist, see gvfsproxyvolumemonitordaemon.c */
|
|
+ connection = dbus_bus_get (DBUS_BUS_SESSION, &derror);
|
|
+
|
|
+ simple = g_simple_async_result_new (NULL,
|
|
+ callback,
|
|
+ user_data,
|
|
+ _obtain_authz);
|
|
+
|
|
+ message = dbus_message_new_method_call ("org.freedesktop.PolicyKit.AuthenticationAgent", /* bus name */
|
|
+ "/", /* object */
|
|
+ "org.freedesktop.PolicyKit.AuthenticationAgent", /* interface */
|
|
+ "ObtainAuthorization");
|
|
+
|
|
+ xid = 0;
|
|
+ pid = getpid ();
|
|
+
|
|
+ dbus_message_append_args (message,
|
|
+ DBUS_TYPE_STRING,
|
|
+ &(action_id),
|
|
+ DBUS_TYPE_UINT32,
|
|
+ &(xid),
|
|
+ DBUS_TYPE_UINT32,
|
|
+ &(pid),
|
|
+ DBUS_TYPE_INVALID);
|
|
+
|
|
+ _g_dbus_connection_call_async (connection,
|
|
+ message,
|
|
+ -1,
|
|
+ (GAsyncDBusCallback) _obtain_authz_cb,
|
|
+ simple);
|
|
+ dbus_message_unref (message);
|
|
+ dbus_connection_unref (connection);
|
|
+}
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/polkit.h 2009-03-01 23:47:41.000000000 -0500
|
|
@@ -0,0 +1,44 @@
|
|
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
+/* gvfs - extensions for gio
|
|
+ *
|
|
+ * Copyright (C) 2006-2009 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: David Zeuthen <davidz@redhat.com>
|
|
+ */
|
|
+
|
|
+
|
|
+#ifndef __POLKIT_H__
|
|
+#define __POLKIT_H__
|
|
+
|
|
+#include <glib-object.h>
|
|
+#include <gio/gio.h>
|
|
+#include <polkit/polkit.h>
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+void _obtain_authz (const gchar *action_id,
|
|
+ GCancellable *cancellable,
|
|
+ GAsyncReadyCallback callback,
|
|
+ gpointer user_data);
|
|
+
|
|
+gboolean _obtain_authz_finish (GAsyncResult *res,
|
|
+ GError **error);
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __POLKIT_H__ */
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/Makefile.am 2009-03-01 21:13:46.000000000 -0500
|
|
@@ -0,0 +1,53 @@
|
|
+
|
|
+NULL =
|
|
+
|
|
+libexec_PROGRAMS = gvfs-gdu-volume-monitor
|
|
+
|
|
+
|
|
+gvfs_gdu_volume_monitor_SOURCES = \
|
|
+ gdu-volume-monitor-daemon.c \
|
|
+ ggdudrive.c ggdudrive.h \
|
|
+ ggduvolume.c ggduvolume.h \
|
|
+ ggdumount.c ggdumount.h \
|
|
+ ggduvolumemonitor.c ggduvolumemonitor.h \
|
|
+ polkit.c polkit.h \
|
|
+ $(NULL)
|
|
+
|
|
+gvfs_gdu_volume_monitor_CFLAGS = \
|
|
+ -DG_LOG_DOMAIN=\"GVFS-Gdu\" \
|
|
+ -I$(top_srcdir)/common \
|
|
+ -I$(top_srcdir)/monitor/proxy \
|
|
+ $(GLIB_CFLAGS) \
|
|
+ $(GDU_CFLAGS) \
|
|
+ $(DBUS_CFLAGS) \
|
|
+ -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \
|
|
+ -DGVFS_LOCALEDIR=\""$(localedir)"\" \
|
|
+ -DG_DISABLE_DEPRECATED \
|
|
+ -DGDU_API_IS_SUBJECT_TO_CHANGE \
|
|
+ $(NULL)
|
|
+
|
|
+gvfs_gdu_volume_monitor_LDFLAGS = \
|
|
+ $(NULL)
|
|
+
|
|
+gvfs_gdu_volume_monitor_LDADD = \
|
|
+ $(GLIB_LIBS) \
|
|
+ $(GDU_LIBS) \
|
|
+ $(DBUS_LIBS) \
|
|
+ $(top_builddir)/common/libgvfscommon.la \
|
|
+ $(top_builddir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la \
|
|
+ $(NULL)
|
|
+
|
|
+remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors
|
|
+remote_volume_monitors_DATA = gdu.monitor
|
|
+
|
|
+servicedir = $(datadir)/dbus-1/services
|
|
+service_in_files = org.gtk.Private.GduVolumeMonitor.service.in
|
|
+service_DATA = $(service_in_files:.service.in=.service)
|
|
+
|
|
+$(service_DATA): $(service_in_files) Makefile
|
|
+ @sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
|
|
+
|
|
+clean-local:
|
|
+ rm -f *~ *.loT $(BUILT_SOURCES) $(service_DATA)
|
|
+
|
|
+EXTRA_DIST = $(service_in_files) gdu.monitor
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/gdu.monitor 2009-02-11 06:42:26.000000000 -0500
|
|
@@ -0,0 +1,5 @@
|
|
+[RemoteVolumeMonitor]
|
|
+Name=GProxyVolumeMonitorGdu
|
|
+DBusName=org.gtk.Private.GduVolumeMonitor
|
|
+IsNative=true
|
|
+NativePriority=3
|
|
--- /dev/null 2009-03-04 16:07:30.099029290 -0500
|
|
+++ monitor/gdu/org.gtk.Private.GduVolumeMonitor.service.in 2009-02-19 22:34:26.000000000 -0500
|
|
@@ -0,0 +1,3 @@
|
|
+[D-BUS Service]
|
|
+Name=org.gtk.Private.GduVolumeMonitor
|
|
+Exec=@libexecdir@/gvfs-gdu-volume-monitor
|