gvfs/daemon/gvfsbackend.c

1107 lines
35 KiB
C

/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include <config.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <glib.h>
#include <glib/gi18n.h>
#include "gvfsbackend.h"
#include "gvfsjobsource.h"
#include <gvfsjobopenforread.h>
#include <gvfsjobopeniconforread.h>
#include <gvfsjobopenforwrite.h>
#include <gvfsjobqueryinfo.h>
#include <gvfsjobqueryfsinfo.h>
#include <gvfsjobsetdisplayname.h>
#include <gvfsjobenumerate.h>
#include <gvfsjobdelete.h>
#include <gvfsjobtrash.h>
#include <gvfsjobunmount.h>
#include <gvfsjobmountmountable.h>
#include <gvfsjobunmountmountable.h>
#include <gvfsjobstartmountable.h>
#include <gvfsjobstopmountable.h>
#include <gvfsjobpollmountable.h>
#include <gvfsjobmakedirectory.h>
#include <gvfsjobmakesymlink.h>
#include <gvfsjobcreatemonitor.h>
#include <gvfsjobcopy.h>
#include <gvfsjobmove.h>
#include <gvfsjobpush.h>
#include <gvfsjobpull.h>
#include <gvfsjobsetattribute.h>
#include <gvfsjobqueryattributes.h>
#include <gvfsdbus.h>
enum {
PROP_0,
PROP_OBJECT_PATH,
PROP_DAEMON
};
struct _GVfsBackendPrivate
{
GVfsDaemon *daemon;
char *object_path;
gboolean is_mounted;
char *display_name;
char *stable_name;
char **x_content_types;
GIcon *icon;
GIcon *symbolic_icon;
char *prefered_filename_encoding;
gboolean user_visible;
char *default_location;
GMountSpec *mount_spec;
gboolean block_requests;
GSettings *lockdown_settings;
gboolean readonly_lockdown;
};
/* TODO: Real P_() */
#define P_(_x) (_x)
static void g_vfs_backend_job_source_iface_init (GVfsJobSourceIface *iface);
static void g_vfs_backend_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void g_vfs_backend_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static GObject* g_vfs_backend_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params);
G_DEFINE_TYPE_WITH_CODE (GVfsBackend, g_vfs_backend, G_TYPE_OBJECT,
G_ADD_PRIVATE (GVfsBackend)
G_IMPLEMENT_INTERFACE (G_VFS_TYPE_JOB_SOURCE,
g_vfs_backend_job_source_iface_init))
static GHashTable *registered_backends = NULL;
void
g_vfs_register_backend (GType backend_type,
const char *type)
{
if (registered_backends == NULL)
registered_backends = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
g_hash_table_insert (registered_backends,
g_strdup (type), (void *)backend_type);
}
GType
g_vfs_lookup_backend (const char *type)
{
gpointer res;
if (registered_backends != NULL)
{
res = g_hash_table_lookup (registered_backends, type);
if (res != NULL)
return (GType)res;
}
return G_TYPE_INVALID;
}
static void
g_vfs_backend_finalize (GObject *object)
{
GVfsBackend *backend;
backend = G_VFS_BACKEND (object);
g_vfs_daemon_unregister_path (backend->priv->daemon, backend->priv->object_path);
g_object_unref (backend->priv->daemon);
g_free (backend->priv->object_path);
g_free (backend->priv->display_name);
g_free (backend->priv->stable_name);
g_strfreev (backend->priv->x_content_types);
g_clear_object (&backend->priv->icon);
g_clear_object (&backend->priv->symbolic_icon);
g_free (backend->priv->prefered_filename_encoding);
g_free (backend->priv->default_location);
if (backend->priv->mount_spec)
g_mount_spec_unref (backend->priv->mount_spec);
g_clear_object (&backend->priv->lockdown_settings);
if (G_OBJECT_CLASS (g_vfs_backend_parent_class)->finalize)
(*G_OBJECT_CLASS (g_vfs_backend_parent_class)->finalize) (object);
}
static void
g_vfs_backend_job_source_iface_init (GVfsJobSourceIface *iface)
{
}
static void
g_vfs_backend_class_init (GVfsBackendClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructor = g_vfs_backend_constructor;
gobject_class->finalize = g_vfs_backend_finalize;
gobject_class->set_property = g_vfs_backend_set_property;
gobject_class->get_property = g_vfs_backend_get_property;
g_object_class_install_property (gobject_class,
PROP_OBJECT_PATH,
g_param_spec_string ("object-path",
P_("Backend object path"),
P_("The dbus object path for the backend object."),
"",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
g_object_class_install_property (gobject_class,
PROP_DAEMON,
g_param_spec_object ("daemon",
P_("Daemon"),
P_("The daemon this backend is handled by."),
G_VFS_TYPE_DAEMON,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
}
static void
g_vfs_backend_init (GVfsBackend *backend)
{
backend->priv = g_vfs_backend_get_instance_private (backend);
backend->priv->icon = NULL;
backend->priv->symbolic_icon = NULL;
backend->priv->prefered_filename_encoding = g_strdup ("");
backend->priv->display_name = g_strdup ("");
backend->priv->stable_name = g_strdup ("");
backend->priv->user_visible = TRUE;
backend->priv->default_location = g_strdup ("");
}
static void
g_vfs_backend_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GVfsBackend *backend = G_VFS_BACKEND (object);
switch (prop_id)
{
case PROP_OBJECT_PATH:
backend->priv->object_path = g_value_dup_string (value);
break;
case PROP_DAEMON:
backend->priv->daemon = G_VFS_DAEMON (g_value_dup_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_vfs_backend_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GVfsBackend *backend = G_VFS_BACKEND (object);
switch (prop_id)
{
case PROP_OBJECT_PATH:
g_value_set_string (value, backend->priv->object_path);
break;
case PROP_DAEMON:
g_value_set_object (value, backend->priv->daemon);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GDBusInterfaceSkeleton *
register_path_cb (GDBusConnection *conn,
const char *obj_path,
gpointer data)
{
GError *error;
GVfsDBusMount *skeleton;
skeleton = gvfs_dbus_mount_skeleton_new ();
g_signal_connect (skeleton, "handle-enumerate", G_CALLBACK (g_vfs_job_enumerate_new_handle), data);
g_signal_connect (skeleton, "handle-query-info", G_CALLBACK (g_vfs_job_query_info_new_handle), data);
g_signal_connect (skeleton, "handle-query-filesystem-info", G_CALLBACK (g_vfs_job_query_fs_info_new_handle), data);
g_signal_connect (skeleton, "handle-set-display-name", G_CALLBACK (g_vfs_job_set_display_name_new_handle), data);
g_signal_connect (skeleton, "handle-delete", G_CALLBACK (g_vfs_job_delete_new_handle), data);
g_signal_connect (skeleton, "handle-trash", G_CALLBACK (g_vfs_job_trash_new_handle), data);
g_signal_connect (skeleton, "handle-make-directory", G_CALLBACK (g_vfs_job_make_directory_new_handle), data);
g_signal_connect (skeleton, "handle-make-symbolic-link", G_CALLBACK (g_vfs_job_make_symlink_new_handle), data);
g_signal_connect (skeleton, "handle-query-settable-attributes", G_CALLBACK (g_vfs_job_query_settable_attributes_new_handle), data);
g_signal_connect (skeleton, "handle-query-writable-namespaces", G_CALLBACK (g_vfs_job_query_writable_namespaces_new_handle), data);
g_signal_connect (skeleton, "handle-set-attribute", G_CALLBACK (g_vfs_job_set_attribute_new_handle), data);
g_signal_connect (skeleton, "handle-poll-mountable", G_CALLBACK (g_vfs_job_poll_mountable_new_handle), data);
g_signal_connect (skeleton, "handle-start-mountable", G_CALLBACK (g_vfs_job_start_mountable_new_handle), data);
g_signal_connect (skeleton, "handle-stop-mountable", G_CALLBACK (g_vfs_job_stop_mountable_new_handle), data);
g_signal_connect (skeleton, "handle-unmount-mountable", G_CALLBACK (g_vfs_job_unmount_mountable_new_handle), data);
g_signal_connect (skeleton, "handle-eject-mountable", G_CALLBACK (g_vfs_job_eject_mountable_new_handle), data);
g_signal_connect (skeleton, "handle-mount-mountable", G_CALLBACK (g_vfs_job_mount_mountable_new_handle), data);
g_signal_connect (skeleton, "handle-unmount", G_CALLBACK (g_vfs_job_unmount_new_handle), data);
g_signal_connect (skeleton, "handle-open-for-read", G_CALLBACK (g_vfs_job_open_for_read_new_handle), data);
g_signal_connect (skeleton, "handle-open-for-write", G_CALLBACK (g_vfs_job_open_for_write_new_handle), data);
g_signal_connect (skeleton, "handle-open-for-write-flags", G_CALLBACK (g_vfs_job_open_for_write_new_handle_with_flags), data);
g_signal_connect (skeleton, "handle-copy", G_CALLBACK (g_vfs_job_copy_new_handle), data);
g_signal_connect (skeleton, "handle-move", G_CALLBACK (g_vfs_job_move_new_handle), data);
g_signal_connect (skeleton, "handle-push", G_CALLBACK (g_vfs_job_push_new_handle), data);
g_signal_connect (skeleton, "handle-pull", G_CALLBACK (g_vfs_job_pull_new_handle), data);
g_signal_connect (skeleton, "handle-create-directory-monitor", G_CALLBACK (g_vfs_job_create_directory_monitor_new_handle), data);
g_signal_connect (skeleton, "handle-create-file-monitor", G_CALLBACK (g_vfs_job_create_file_monitor_new_handle), data);
g_signal_connect (skeleton, "handle-open-icon-for-read", G_CALLBACK (g_vfs_job_open_icon_for_read_new_handle), data);
error = NULL;
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
conn,
obj_path,
&error))
{
g_warning ("Error registering path: %s (%s, %d)\n",
error->message, g_quark_to_string (error->domain), error->code);
g_error_free (error);
}
return G_DBUS_INTERFACE_SKELETON (skeleton);
}
static GObject*
g_vfs_backend_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params)
{
GObject *object;
GVfsBackend *backend;
object = (* G_OBJECT_CLASS (g_vfs_backend_parent_class)->constructor) (type,
n_construct_properties,
construct_params);
backend = G_VFS_BACKEND (object);
g_vfs_daemon_register_path (backend->priv->daemon,
backend->priv->object_path,
register_path_cb,
backend);
return object;
}
GVfsDaemon *
g_vfs_backend_get_daemon (GVfsBackend *backend)
{
return backend->priv->daemon;
}
gboolean
g_vfs_backend_is_mounted (GVfsBackend *backend)
{
return backend->priv->is_mounted;
}
void
g_vfs_backend_set_display_name (GVfsBackend *backend,
const char *display_name)
{
g_free (backend->priv->display_name);
backend->priv->display_name = g_strdup (display_name);
}
/**
* g_vfs_backend_set_stable_name:
* @backend: backend
* @stable_name: the stable name
*
* For filesystems that can change the name during the lifetime
* of the filesystem this can be uses to set a separate stable
* name. This is used for instance as the directory representing
* the mounted file system in the standard UNIX file system
* namespace.
*
* If this function isn't called, the value passed to
* g_vfs_backend_set_display_name() will be used instead.
**/
void
g_vfs_backend_set_stable_name (GVfsBackend *backend,
const char *stable_name)
{
g_free (backend->priv->stable_name);
backend->priv->stable_name = g_strdup (stable_name);
}
/**
* g_vfs_backend_set_x_content_types:
* @backend: backend
* @x_content_types: the x-content types
*
* For backends where the x-content type is known ahead of time and
* won't change (such as a CDDA audio disc backend), this function
* should be called when the backend is constructed with the given
* types.
*
* See the <ulink url="http://www.freedesktop.org/wiki/Specifications/shared-mime-info-spec">shared-mime-info</ulink>
* specification for more on x-content types.
**/
void
g_vfs_backend_set_x_content_types (GVfsBackend *backend,
char **x_content_types)
{
g_strfreev (backend->priv->x_content_types);
backend->priv->x_content_types = g_strdupv (x_content_types);
}
void
g_vfs_backend_set_icon_name (GVfsBackend *backend,
const char *icon_name)
{
g_clear_object (&backend->priv->icon);
backend->priv->icon = g_themed_icon_new_with_default_fallbacks (icon_name);
}
void
g_vfs_backend_set_icon (GVfsBackend *backend,
GIcon *icon)
{
g_clear_object (&backend->priv->icon);
backend->priv->icon = g_object_ref (icon);
}
void
g_vfs_backend_set_symbolic_icon_name (GVfsBackend *backend,
const char *icon_name)
{
g_clear_object (&backend->priv->symbolic_icon);
backend->priv->symbolic_icon = g_themed_icon_new_with_default_fallbacks (icon_name);
}
void
g_vfs_backend_set_symbolic_icon (GVfsBackend *backend,
GIcon *icon)
{
g_clear_object (&backend->priv->symbolic_icon);
backend->priv->symbolic_icon = g_object_ref (icon);
}
void
g_vfs_backend_set_prefered_filename_encoding (GVfsBackend *backend,
const char *prefered_filename_encoding)
{
g_free (backend->priv->prefered_filename_encoding);
backend->priv->prefered_filename_encoding = g_strdup (prefered_filename_encoding);
}
void
g_vfs_backend_set_user_visible (GVfsBackend *backend,
gboolean user_visible)
{
backend->priv->user_visible = user_visible;
}
/**
* g_vfs_backend_set_default_location:
* @backend: backend
* @location: the default location
*
* With this function the backend can set a "default location", which is a path
* that reflects the main entry point for the user (e.g. * the home directory,
* or the root of the volume).
*
* NB: Does not include the mount prefix, you need to prepend that if there is
* one.
**/
void
g_vfs_backend_set_default_location (GVfsBackend *backend,
const char *location)
{
g_free (backend->priv->default_location);
backend->priv->default_location = g_strdup (location);
}
void
g_vfs_backend_set_mount_spec (GVfsBackend *backend,
GMountSpec *mount_spec)
{
if (backend->priv->mount_spec)
g_mount_spec_unref (backend->priv->mount_spec);
backend->priv->mount_spec = g_mount_spec_ref (mount_spec);
}
const char *
g_vfs_backend_get_backend_type (GVfsBackend *backend)
{
if (backend->priv->mount_spec)
return g_mount_spec_get_type (backend->priv->mount_spec);
return NULL;
}
const char *
g_vfs_backend_get_display_name (GVfsBackend *backend)
{
return backend->priv->display_name;
}
const char *
g_vfs_backend_get_stable_name (GVfsBackend *backend)
{
return backend->priv->stable_name;
}
char **
g_vfs_backend_get_x_content_types (GVfsBackend *backend)
{
return backend->priv->x_content_types;
}
GIcon *
g_vfs_backend_get_icon (GVfsBackend *backend)
{
return backend->priv->icon;
}
GIcon *
g_vfs_backend_get_symbolic_icon (GVfsBackend *backend)
{
return backend->priv->symbolic_icon;
}
const char *
g_vfs_backend_get_default_location (GVfsBackend *backend)
{
return backend->priv->default_location;
}
GMountSpec *
g_vfs_backend_get_mount_spec (GVfsBackend *backend)
{
return backend->priv->mount_spec;
}
static void
get_thumbnail_attributes (const char *uri,
GFileInfo *info)
{
GChecksum *checksum;
char *filename;
char *basename;
const char *size_dirs[4] = { "xx-large", "x-large", "large", "normal" };
gsize i;
checksum = g_checksum_new (G_CHECKSUM_MD5);
g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
g_checksum_free (checksum);
for (i = 0; i < G_N_ELEMENTS (size_dirs); i++)
{
filename = g_build_filename (g_get_user_cache_dir (),
"thumbnails", size_dirs[i], basename,
NULL);
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
break;
g_clear_pointer (&filename, g_free);
}
if (filename)
g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, filename);
else
{
filename = g_build_filename (g_get_user_cache_dir (),
"thumbnails", "fail",
"gnome-thumbnail-factory",
basename,
NULL);
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, TRUE);
}
g_free (basename);
g_free (filename);
}
void
g_vfs_backend_add_auto_info (GVfsBackend *backend,
GFileAttributeMatcher *matcher,
GFileInfo *info,
const char *uri)
{
GMountSpec *spec;
char *id;
if (g_file_attribute_matcher_matches (matcher,
G_FILE_ATTRIBUTE_ID_FILESYSTEM))
{
spec = g_vfs_backend_get_mount_spec (backend);
if (spec)
{
id = g_mount_spec_to_string (spec);
g_file_info_set_attribute_string (info,
G_FILE_ATTRIBUTE_ID_FILESYSTEM,
id);
g_free (id);
}
}
if (uri != NULL &&
(g_file_attribute_matcher_matches (matcher,
G_FILE_ATTRIBUTE_THUMBNAIL_PATH) ||
g_file_attribute_matcher_matches (matcher,
G_FILE_ATTRIBUTE_THUMBNAILING_FAILED)))
get_thumbnail_attributes (uri, info);
if (backend->priv->readonly_lockdown)
{
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
}
}
void
g_vfs_backend_add_auto_fs_info (GVfsBackend *backend,
GFileAttributeMatcher *matcher,
GFileInfo *info)
{
const char *type;
type = g_vfs_backend_get_backend_type (backend);
if (type)
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_GVFS_BACKEND, type);
if (backend->priv->readonly_lockdown)
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
}
void
g_vfs_backend_set_block_requests (GVfsBackend *backend, gboolean value)
{
backend->priv->block_requests = value;
}
gboolean
g_vfs_backend_get_block_requests (GVfsBackend *backend)
{
return backend->priv->block_requests;
}
gboolean
g_vfs_backend_invocation_first_handler (GVfsDBusMount *object,
GDBusMethodInvocation *invocation,
GVfsBackend *backend)
{
GDBusConnection *connection;
GCredentials *credentials;
pid_t pid = -1;
connection = g_dbus_method_invocation_get_connection (invocation);
credentials = g_dbus_connection_get_peer_credentials (connection);
if (credentials)
pid = g_credentials_get_unix_pid (credentials, NULL);
g_debug ("backend_dbus_handler %s:%s (pid=%ld)\n",
g_dbus_method_invocation_get_interface_name (invocation),
g_dbus_method_invocation_get_method_name (invocation),
(long)pid);
if (backend->priv->block_requests)
{
g_dbus_method_invocation_return_error (invocation,
G_IO_ERROR,
G_IO_ERROR_NOT_MOUNTED,
"%s", "Backend currently unmounting");
return TRUE;
}
return FALSE;
}
static void
create_mount_tracker_proxy (GTask *task,
GAsyncReadyCallback callback)
{
gvfs_dbus_mount_tracker_proxy_new_for_bus (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
G_VFS_DBUS_DAEMON_NAME,
G_VFS_DBUS_MOUNTTRACKER_PATH,
NULL,
callback,
task);
}
static void
register_mount_cb (GVfsDBusMountTracker *proxy,
GAsyncResult *res,
gpointer user_data)
{
GTask *task = G_TASK (user_data);
GError *error = NULL;
if (!gvfs_dbus_mount_tracker_call_register_mount_finish (proxy, res, &error))
{
g_dbus_error_strip_remote_error (error);
g_task_return_error (task, error);
}
else
{
g_task_return_boolean (task, TRUE);
}
g_object_unref (task);
}
static void
register_mount_got_proxy_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GTask *task = G_TASK (user_data);
GVfsDBusMountTracker *proxy;
GError *error = NULL;
GVfsBackend *backend = G_VFS_BACKEND (g_task_get_source_object (task));
char *stable_name;
char *x_content_types_string;
char *icon_str;
char *symbolic_icon_str;
proxy = gvfs_dbus_mount_tracker_proxy_new_for_bus_finish (res, &error);
if (proxy == NULL)
{
g_dbus_error_strip_remote_error (error);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
backend->priv->is_mounted = TRUE;
if (backend->priv->x_content_types != NULL && g_strv_length (backend->priv->x_content_types) > 0)
x_content_types_string = g_strjoinv (" ", backend->priv->x_content_types);
else
x_content_types_string = g_strdup ("");
if (backend->priv->icon != NULL)
icon_str = g_icon_to_string (backend->priv->icon);
else
icon_str = g_strdup ("");
if (backend->priv->symbolic_icon != NULL)
symbolic_icon_str = g_icon_to_string (backend->priv->symbolic_icon);
else
symbolic_icon_str = g_strdup ("");
stable_name = g_mount_spec_to_string (backend->priv->mount_spec);
gvfs_dbus_mount_tracker_call_register_mount (proxy,
backend->priv->object_path,
backend->priv->display_name,
stable_name,
x_content_types_string,
icon_str,
symbolic_icon_str,
backend->priv->prefered_filename_encoding,
backend->priv->user_visible,
g_mount_spec_to_dbus (backend->priv->mount_spec),
backend->priv->default_location ? backend->priv->default_location : "",
NULL,
(GAsyncReadyCallback) register_mount_cb,
task);
g_free (stable_name);
g_free (x_content_types_string);
g_free (icon_str);
g_free (symbolic_icon_str);
g_object_unref (proxy);
}
void
g_vfs_backend_register_mount (GVfsBackend *backend,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (backend, NULL, callback, user_data);
g_task_set_source_tag (task, g_vfs_backend_register_mount);
create_mount_tracker_proxy (task, register_mount_got_proxy_cb);
}
gboolean
g_vfs_backend_register_mount_finish (GVfsBackend *backend,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (res, backend), FALSE);
g_return_val_if_fail (g_async_result_is_tagged (res, g_vfs_backend_register_mount), FALSE);
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
unregister_mount_cb (GVfsDBusMountTracker *proxy,
GAsyncResult *res,
gpointer user_data)
{
GTask *task = G_TASK (user_data);
GError *error = NULL;
if (!gvfs_dbus_mount_tracker_call_unregister_mount_finish (proxy, res, &error))
{
g_dbus_error_strip_remote_error (error);
g_task_return_error (task, error);
}
else
{
g_task_return_boolean (task, TRUE);
}
g_object_unref (task);
}
static void
unregister_mount_got_proxy_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GTask *task = G_TASK (user_data);
GVfsDBusMountTracker *proxy;
GError *error = NULL;
GVfsBackend *backend = G_VFS_BACKEND (g_task_get_source_object (task));
proxy = gvfs_dbus_mount_tracker_proxy_new_for_bus_finish (res, &error);
if (proxy == NULL)
{
g_dbus_error_strip_remote_error (error);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
gvfs_dbus_mount_tracker_call_unregister_mount (proxy,
backend->priv->object_path,
NULL,
(GAsyncReadyCallback) unregister_mount_cb,
task);
g_object_unref (proxy);
}
void
g_vfs_backend_unregister_mount (GVfsBackend *backend,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (backend, NULL, callback, user_data);
g_task_set_source_tag (task, g_vfs_backend_unregister_mount);
create_mount_tracker_proxy (task, unregister_mount_got_proxy_cb);
}
gboolean
g_vfs_backend_unregister_mount_finish (GVfsBackend *backend,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (res, backend), FALSE);
g_return_val_if_fail (g_async_result_is_tagged (res, g_vfs_backend_unregister_mount), FALSE);
return g_task_propagate_boolean (G_TASK (res), error);
}
/* ------------------------------------------------------------------------------------------------- */
typedef struct
{
GMountSource *mount_source;
const gchar *message;
const gchar *choices[3];
gboolean no_more_processes;
guint timeout_id;
} UnmountWithOpData;
static void
on_show_processes_reply (GMountSource *mount_source,
GAsyncResult *res,
gpointer user_data)
{
GTask *task = G_TASK (user_data);
UnmountWithOpData *data = g_task_get_task_data (task);
gboolean ret, aborted;
gint choice;
if (data->timeout_id != 0)
g_source_remove (data->timeout_id);
ret = g_mount_source_show_processes_finish (mount_source, res, &aborted, &choice);
if (!data->no_more_processes && !ret)
{
/* If the "show-processes" signal wasn't handled */
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_BUSY,
_("File system is busy"));
}
else if (!data->no_more_processes && (aborted || choice == 1))
{
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED,
"GMountOperation aborted");
}
else
{
g_task_return_boolean (task, TRUE);
}
g_object_unref (task);
}
static gboolean
on_update_processes_timeout (gpointer user_data)
{
GTask *task = G_TASK (user_data);
UnmountWithOpData *data = g_task_get_task_data (task);
GArray *processes;
GVfsBackend *backend = G_VFS_BACKEND (g_task_get_source_object (task));
GVfsDaemon *daemon = g_vfs_backend_get_daemon (backend);
if (!g_vfs_daemon_has_blocking_processes (daemon))
{
g_mount_source_abort (data->mount_source);
data->timeout_id = 0;
data->no_more_processes = TRUE;
return G_SOURCE_REMOVE;
}
else
{
processes = g_vfs_daemon_get_blocking_processes (daemon);
g_mount_source_show_processes_async (data->mount_source,
data->message,
processes,
data->choices,
(GAsyncReadyCallback) on_show_processes_reply,
task);
g_array_unref (processes);
return G_SOURCE_CONTINUE;
}
}
static void
unmount_with_op_data_free (UnmountWithOpData *data)
{
g_free (data);
}
/**
* g_vfs_backend_unmount_with_operation_finish:
* @backend: A #GVfsBackend.
* @res: A #GAsyncResult obtained from the @callback function passed
* to g_vfs_backend_unmount_with_operation().
* @error: A #GError, or NULL.
*
* Gets the result of the operation started by
* gvfs_backend_unmount_with_operation_sync().
*
* If the operation was cancelled, G_IO_ERROR_FAILED_HANDLED will be returned.
* If the operation wasn't interacted and there were outstanding jobs,
* G_IO_ERROR_BUSY will be returned.
*
* Returns: %TRUE if the backend should be unmounted (either no blocking
* processes or the user decided to unmount anyway), %FALSE if
* no action should be taken (error is set).
*/
gboolean
g_vfs_backend_unmount_with_operation_finish (GVfsBackend *backend,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (res, backend), FALSE);
g_return_val_if_fail (g_async_result_is_tagged (res, g_vfs_backend_unmount_with_operation), FALSE);
return g_task_propagate_boolean (G_TASK (res), error);
}
/**
* gvfs_backend_unmount_with_operation:
* @backend: A #GVfsBackend.
* @callback: A #GAsyncReadyCallback.
* @user_data: User data to pass to @callback.
*
* Utility function to checks if there are pending operations on
* @backend preventing unmount. If not, then @callback is invoked
* immediately.
*
* Otherwise, a dialog will be shown (using @mount_source) to interact
* with the user about blocking processes (e.g. using the
* #GMountOperation::show-processes signal). The list of blocking
* processes is continuously updated.
*
* Once the user has decided (or if it's not possible to interact with
* the user), @callback will be invoked. You can then call
* g_vfs_backend_unmount_with_operation_finish() to get the result
* of the operation.
*/
void
g_vfs_backend_unmount_with_operation (GVfsBackend *backend,
GMountSource *mount_source,
GAsyncReadyCallback callback,
gpointer user_data)
{
GArray *processes;
UnmountWithOpData *data;
GVfsDaemon *daemon;
GTask *task;
g_return_if_fail (G_VFS_IS_BACKEND (backend));
g_return_if_fail (G_IS_MOUNT_SOURCE (mount_source));
g_return_if_fail (callback != NULL);
task = g_task_new (backend, NULL, callback, user_data);
g_task_set_source_tag (task, g_vfs_backend_unmount_with_operation);
daemon = g_vfs_backend_get_daemon (backend);
/* if no processes are blocking, complete immediately */
if (!g_vfs_daemon_has_blocking_processes (daemon))
{
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
data = g_new0 (UnmountWithOpData, 1);
data->mount_source = mount_source;
data->choices[0] = _("Unmount Anyway");
data->choices[1] = _("Cancel");
data->choices[2] = NULL;
data->message = _("Volume is busy\n"
"One or more applications are keeping the volume busy.");
g_task_set_task_data (task, data, (GDestroyNotify) unmount_with_op_data_free);
/* show processes */
processes = g_vfs_daemon_get_blocking_processes (daemon);
g_mount_source_show_processes_async (mount_source,
data->message,
processes,
data->choices,
(GAsyncReadyCallback) on_show_processes_reply,
task);
g_array_unref (processes);
/* update these processes every two secs */
data->timeout_id = g_timeout_add_seconds (2, on_update_processes_timeout, task);
}
static void
forced_unregister_mount_callback (GVfsBackend *backend,
GAsyncResult *res,
gpointer user_data)
{
GVfsDaemon *daemon;
GError *error = NULL;
g_debug ("forced_unregister_mount_callback\n");
if (!g_vfs_backend_unregister_mount_finish (backend, res, &error))
{
g_dbus_error_strip_remote_error (error);
g_warning ("Error unregistering mount: %s (%s, %d)\n",
error->message, g_quark_to_string (error->domain), error->code);
g_error_free (error);
}
/* Unlink job source from daemon */
daemon = g_vfs_backend_get_daemon (backend);
g_vfs_daemon_close_active_channels (daemon, backend);
g_vfs_job_source_closed (G_VFS_JOB_SOURCE (backend));
}
void
g_vfs_backend_force_unmount (GVfsBackend *backend)
{
g_vfs_backend_set_block_requests (backend, TRUE);
g_vfs_backend_unregister_mount (backend,
(GAsyncReadyCallback) forced_unregister_mount_callback,
NULL);
}
static void
lockdown_settings_changed (GSettings *settings,
gchar *key,
gpointer user_data)
{
GVfsBackend *backend = G_VFS_BACKEND (user_data);
backend->priv->readonly_lockdown = g_settings_get_boolean (settings,
"mount-removable-storage-devices-as-read-only");
}
void
g_vfs_backend_handle_readonly_lockdown (GVfsBackend *backend)
{
backend->priv->lockdown_settings = g_settings_new ("org.gnome.desktop.lockdown");
backend->priv->readonly_lockdown = g_settings_get_boolean (backend->priv->lockdown_settings,
"mount-removable-storage-devices-as-read-only");
g_signal_connect_object (backend->priv->lockdown_settings,
"changed",
G_CALLBACK (lockdown_settings_changed),
backend,
0);
}
gboolean
g_vfs_backend_get_readonly_lockdown (GVfsBackend *backend)
{
return backend->priv->readonly_lockdown;
}