From: David Zeuthen Date: Wed, 16 Feb 2011 20:26:12 +0000 (-0500) Subject: Move contents of BlockDevice.Probed and Filesystem into the BlockDevice iface X-Git-Tag: upstream/2.1.2~480^2~147 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ef58bf8d7e321bffb77cea5c1a0d1c9c5bfaa82c;p=platform%2Fupstream%2Fudisks2.git Move contents of BlockDevice.Probed and Filesystem into the BlockDevice iface Signed-off-by: David Zeuthen --- diff --git a/data/org.freedesktop.UDisks2.xml b/data/org.freedesktop.UDisks2.xml index e485fc3..545ff43 100644 --- a/data/org.freedesktop.UDisks2.xml +++ b/data/org.freedesktop.UDisks2.xml @@ -37,30 +37,22 @@ to, or '/' if no such object exists. --> - - - - - - - - - - + + + + + + - - - - - - + + + - - + diff --git a/src/Makefile.am b/src/Makefile.am index adc70ab..7a8c9b9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -50,7 +50,6 @@ libudisks_daemon_la_SOURCES = \ udisksfstabprovider.h udisksfstabprovider.c \ udiskslinuxblock.h udiskslinuxblock.c \ udiskslinuxdrive.h udiskslinuxdrive.c \ - udisksfilesystemimpl.h udisksfilesystemimpl.c \ udisksbasejob.h udisksbasejob.c \ udisksspawnedjob.h udisksspawnedjob.c \ udisksthreadedjob.h udisksthreadedjob.c \ diff --git a/src/udisksfilesystemimpl.c b/src/udisksfilesystemimpl.c deleted file mode 100644 index 445dde5..0000000 --- a/src/udisksfilesystemimpl.c +++ /dev/null @@ -1,1244 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2007-2010 David Zeuthen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "config.h" - -#include - -#include -#include -#include - -#include - -#include - -#include "udisksdaemon.h" -#include "udiskslinuxblock.h" -#include "udisksbasejob.h" -#include "udiskssimplejob.h" -#include "udisksfilesystemimpl.h" -#include "udiskspersistentstore.h" - -/** - * SECTION:udisksfilesystemimpl - * @title: UDisksFilesystemImpl - * @short_description: Filesystem Implementation - * - * This type provides an implementation of the #UDisksFilesystem - * interface that uses the mount and - * umount commands. - * - * TODO: mention other impl details like - * /var/lib/udisks/mtab, how mount options work, - * what role /etc/fstab plays and so on. - */ - - -typedef struct _UDisksFilesystemImplClass UDisksFilesystemImplClass; - -/** - * UDisksFilesystemImpl: - * - * The #UDisksFilesystemImpl structure contains only private data and - * should only be accessed using the provided API. - */ -struct _UDisksFilesystemImpl -{ - UDisksFilesystemStub parent_instance; -}; - -struct _UDisksFilesystemImplClass -{ - UDisksFilesystemStubClass parent_class; -}; - -static void filesystem_iface_init (UDisksFilesystemIface *iface); - -G_DEFINE_TYPE_WITH_CODE (UDisksFilesystemImpl, udisks_filesystem_impl, UDISKS_TYPE_FILESYSTEM_STUB, - G_IMPLEMENT_INTERFACE (UDISKS_TYPE_FILESYSTEM, filesystem_iface_init)); - -static void -udisks_filesystem_impl_init (UDisksFilesystemImpl *filesystem) -{ - g_dbus_interface_set_flags (G_DBUS_INTERFACE (filesystem), - G_DBUS_INTERFACE_FLAGS_RUN_IN_THREAD); -} - -static void -udisks_filesystem_impl_class_init (UDisksFilesystemImplClass *klass) -{ -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -get_uid_sync (GDBusMethodInvocation *invocation, - GCancellable *cancellable, - uid_t *out_uid, - GError **error) -{ - gboolean ret; - const gchar *caller; - GVariant *value; - GError *local_error; - - ret = FALSE; - - caller = g_dbus_method_invocation_get_sender (invocation); - - local_error = NULL; - value = g_dbus_connection_call_sync (g_dbus_method_invocation_get_connection (invocation), - "org.freedesktop.DBus", /* bus name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface */ - "GetConnectionUnixUser", /* method */ - g_variant_new ("(s)", caller), - G_VARIANT_TYPE ("(u)"), - G_DBUS_CALL_FLAGS_NONE, - -1, /* timeout_msec */ - cancellable, - &local_error); - if (value == NULL) - { - g_set_error (error, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Error determining uid of caller %s: %s (%s, %d)", - caller, - local_error->message, - g_quark_to_string (local_error->domain), - local_error->code); - g_error_free (local_error); - goto out; - } - - G_STATIC_ASSERT (sizeof (uid_t) == sizeof (guint32)); - g_variant_get (value, "(u)", out_uid); - - ret = TRUE; - - out: - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct -{ - const gchar *fstype; - const gchar * const *defaults; - const gchar * const *allow; - const gchar * const *allow_uid_self; - const gchar * const *allow_gid_self; -} FSMountOptions; - -/* ---------------------- vfat -------------------- */ - -static const gchar *vfat_defaults[] = { "uid=", "gid=", "shortname=mixed", "dmask=0077", "utf8=1", "showexec", NULL }; -static const gchar *vfat_allow[] = { "flush", "utf8=", "shortname=", "umask=", "dmask=", "fmask=", "codepage=", "iocharset=", "usefree", "showexec", NULL }; -static const gchar *vfat_allow_uid_self[] = { "uid=", NULL }; -static const gchar *vfat_allow_gid_self[] = { "gid=", NULL }; - -/* ---------------------- ntfs -------------------- */ -/* this is assuming that ntfs-3g is used */ - -static const gchar *ntfs_defaults[] = { "uid=", "gid=", "dmask=0077", "fmask=0177", NULL }; -static const gchar *ntfs_allow[] = { "umask=", "dmask=", "fmask=", NULL }; -static const gchar *ntfs_allow_uid_self[] = { "uid=", NULL }; -static const gchar *ntfs_allow_gid_self[] = { "gid=", NULL }; - -/* ---------------------- iso9660 -------------------- */ - -static const gchar *iso9660_defaults[] = { "uid=", "gid=", "iocharset=utf8", "mode=0400", "dmode=0500", NULL }; -static const gchar *iso9660_allow[] = { "norock", "nojoliet", "iocharset=", "mode=", "dmode=", NULL }; -static const gchar *iso9660_allow_uid_self[] = { "uid=", NULL }; -static const gchar *iso9660_allow_gid_self[] = { "gid=", NULL }; - -/* ---------------------- udf -------------------- */ - -static const gchar *udf_defaults[] = { "uid=", "gid=", "iocharset=utf8", "umask=0077", NULL }; -static const gchar *udf_allow[] = { "iocharset=", "umask=", NULL }; -static const gchar *udf_allow_uid_self[] = { "uid=", NULL }; -static const gchar *udf_allow_gid_self[] = { "gid=", NULL }; - -/* ------------------------------------------------ */ -/* TODO: support context= */ - -static const gchar *any_allow[] = { "exec", "noexec", "nodev", "nosuid", "atime", "noatime", "nodiratime", "ro", "rw", "sync", "dirsync", NULL }; - -static const FSMountOptions fs_mount_options[] = - { - { "vfat", vfat_defaults, vfat_allow, vfat_allow_uid_self, vfat_allow_gid_self }, - { "ntfs", ntfs_defaults, ntfs_allow, ntfs_allow_uid_self, ntfs_allow_gid_self }, - { "iso9660", iso9660_defaults, iso9660_allow, iso9660_allow_uid_self, iso9660_allow_gid_self }, - { "udf", udf_defaults, udf_allow, udf_allow_uid_self, udf_allow_gid_self }, - }; - -/* ------------------------------------------------ */ - -static int num_fs_mount_options = sizeof(fs_mount_options) / sizeof(FSMountOptions); - -static const FSMountOptions * -find_mount_options_for_fs (const gchar *fstype) -{ - int n; - const FSMountOptions *fsmo; - - for (n = 0; n < num_fs_mount_options; n++) - { - fsmo = fs_mount_options + n; - if (strcmp (fsmo->fstype, fstype) == 0) - goto out; - } - - fsmo = NULL; - out: - return fsmo; -} - -static gid_t -find_primary_gid (uid_t uid) -{ - struct passwd *pw; - gid_t gid; - - gid = (gid_t) - 1; - - pw = getpwuid (uid); - if (pw == NULL) - { - g_warning ("Couldn't look up uid %d: %m", uid); - goto out; - } - gid = pw->pw_gid; - - out: - return gid; -} - -static gboolean -is_uid_in_gid (uid_t uid, - gid_t gid) -{ - gboolean ret; - struct passwd *pw; - static gid_t supplementary_groups[128]; - int num_supplementary_groups = 128; - int n; - - /* TODO: use some #define instead of harcoding some random number like 128 */ - - ret = FALSE; - - pw = getpwuid (uid); - if (pw == NULL) - { - g_warning ("Couldn't look up uid %d: %m", uid); - goto out; - } - if (pw->pw_gid == gid) - { - ret = TRUE; - goto out; - } - - if (getgrouplist (pw->pw_name, pw->pw_gid, supplementary_groups, &num_supplementary_groups) < 0) - { - g_warning ("Couldn't find supplementary groups for uid %d: %m", uid); - goto out; - } - - for (n = 0; n < num_supplementary_groups; n++) - { - if (supplementary_groups[n] == gid) - { - ret = TRUE; - goto out; - } - } - - out: - return ret; -} - -static gboolean -is_mount_option_allowed (const FSMountOptions *fsmo, - const gchar *option, - uid_t caller_uid) -{ - int n; - gchar *endp; - uid_t uid; - gid_t gid; - gboolean allowed; - const gchar *ep; - gsize ep_len; - - allowed = FALSE; - - /* first run through the allowed mount options */ - if (fsmo != NULL) - { - for (n = 0; fsmo->allow != NULL && fsmo->allow[n] != NULL; n++) - { - ep = strstr (fsmo->allow[n], "="); - if (ep != NULL && ep[1] == '\0') - { - ep_len = ep - fsmo->allow[n] + 1; - if (strncmp (fsmo->allow[n], option, ep_len) == 0) - { - allowed = TRUE; - goto out; - } - } - else - { - if (strcmp (fsmo->allow[n], option) == 0) - { - allowed = TRUE; - goto out; - } - } - } - } - for (n = 0; any_allow[n] != NULL; n++) - { - ep = strstr (any_allow[n], "="); - if (ep != NULL && ep[1] == '\0') - { - ep_len = ep - any_allow[n] + 1; - if (strncmp (any_allow[n], option, ep_len) == 0) - { - allowed = TRUE; - goto out; - } - } - else - { - if (strcmp (any_allow[n], option) == 0) - { - allowed = TRUE; - goto out; - } - } - } - - /* .. then check for mount options where the caller is allowed to pass - * in his own uid - */ - if (fsmo != NULL) - { - for (n = 0; fsmo->allow_uid_self != NULL && fsmo->allow_uid_self[n] != NULL; n++) - { - const gchar *r_mount_option = fsmo->allow_uid_self[n]; - if (g_str_has_prefix (option, r_mount_option)) - { - uid = strtol (option + strlen (r_mount_option), &endp, 10); - if (*endp != '\0') - continue; - if (uid == caller_uid) - { - allowed = TRUE; - goto out; - } - } - } - } - - /* .. ditto for gid - */ - if (fsmo != NULL) - { - for (n = 0; fsmo->allow_gid_self != NULL && fsmo->allow_gid_self[n] != NULL; n++) - { - const gchar *r_mount_option = fsmo->allow_gid_self[n]; - if (g_str_has_prefix (option, r_mount_option)) - { - gid = strtol (option + strlen (r_mount_option), &endp, 10); - if (*endp != '\0') - continue; - if (is_uid_in_gid (caller_uid, gid)) - { - allowed = TRUE; - goto out; - } - } - } - } - - out: - return allowed; -} - -static gchar ** -prepend_default_mount_options (const FSMountOptions *fsmo, - uid_t caller_uid, - const gchar * const *given_options) -{ - GPtrArray *options; - int n; - gchar *s; - gid_t gid; - - options = g_ptr_array_new (); - if (fsmo != NULL) - { - for (n = 0; fsmo->defaults != NULL && fsmo->defaults[n] != NULL; n++) - { - const gchar *option = fsmo->defaults[n]; - - if (strcmp (option, "uid=") == 0) - { - s = g_strdup_printf ("uid=%d", caller_uid); - g_ptr_array_add (options, s); - } - else if (strcmp (option, "gid=") == 0) - { - gid = find_primary_gid (caller_uid); - if (gid != (gid_t) - 1) - { - s = g_strdup_printf ("gid=%d", gid); - g_ptr_array_add (options, s); - } - } - else - { - g_ptr_array_add (options, g_strdup (option)); - } - } - } - for (n = 0; given_options[n] != NULL; n++) - { - g_ptr_array_add (options, g_strdup (given_options[n])); - } - - g_ptr_array_add (options, NULL); - - return (char **) g_ptr_array_free (options, FALSE); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/* - * calculate_fs_type: - * @block: A #UDisksBlockDevice. - * @block_probed: A #UDisksBlockDeviceProbed or %NULL. - * @requested_fs_type: The requested file system type or %NULL. - * @error: Return location for error or %NULL. - * - * Calculates the file system type to use. - * - * Returns: A valid UTF-8 string with the filesystem type (may be "auto") or %NULL if @error is set. Free with g_free(). - */ -static gchar * -calculate_fs_type (UDisksBlockDevice *block, - UDisksBlockDeviceProbed *block_probed, - const gchar *requested_fs_type, - GError **error) -{ - gchar *fs_type_to_use; - const gchar *probed_fs_usage; - const gchar *probed_fs_type; - - probed_fs_usage = NULL; - probed_fs_type = NULL; - if (block_probed != NULL) - { - probed_fs_usage = udisks_block_device_probed_get_usage (block_probed); - probed_fs_type = udisks_block_device_probed_get_type (block_probed); - } - - fs_type_to_use = NULL; - if (requested_fs_type != NULL && strlen (requested_fs_type) > 0) - { - /* TODO: maybe check that it's compatible with probed_fs_type */ - fs_type_to_use = g_strdup (requested_fs_type); - } - else - { - if (probed_fs_type != NULL && strlen (probed_fs_type) > 0) - fs_type_to_use = g_strdup (probed_fs_type); - else - fs_type_to_use = g_strdup ("auto"); - } - - g_assert (fs_type_to_use == NULL || g_utf8_validate (fs_type_to_use, -1, NULL)); - - return fs_type_to_use; -} - -/* - * calculate_mount_options: - * @block: A #UDisksBlockDevice. - * @block_probed: A #UDisksBlockDeviceProbed or %NULL. - * @caller_uid: The uid of the caller making the request. - * @fs_type: The filesystem type to use or %NULL. - * @requested_options: Options requested by the caller. - * @out_auth_no_user_interaction: Return location for whether the 'auth_no_user_interaction' option was passed or %NULL. - * @error: Return location for error or %NULL. - * - * Calculates the mount option string to use. Ensures (by returning an - * error) that only safe options are used. - * - * Returns: A string with mount options or %NULL if @error is set. Free with g_free(). - */ -static gchar * -calculate_mount_options (UDisksBlockDevice *block, - UDisksBlockDeviceProbed *block_probed, - uid_t caller_uid, - const gchar *fs_type, - const gchar *const *requested_options, - gboolean *out_auth_no_user_interaction, - GError **error) -{ - const FSMountOptions *fsmo; - gchar **options_to_use; - gchar *options_to_use_str; - GString *str; - guint n; - gboolean auth_no_user_interaction; - - options_to_use = NULL; - options_to_use_str = NULL; - auth_no_user_interaction = FALSE; - - fsmo = find_mount_options_for_fs (fs_type); - - /* always prepend some reasonable default mount options; these are - * chosen here; the user can override them if he wants to - */ - options_to_use = prepend_default_mount_options (fsmo, caller_uid, requested_options); - - /* validate mount options */ - str = g_string_new ("uhelper=udisks2,nodev,nosuid"); - for (n = 0; options_to_use[n] != NULL; n++) - { - const gchar *option = options_to_use[n]; - - if (g_strcmp0 (option, "auth_no_user_interaction") == 0) - { - auth_no_user_interaction = TRUE; - continue; - } - - /* avoid attacks like passing "shortname=lower,uid=0" as a single mount option */ - if (strstr (option, ",") != NULL) - { - g_set_error (error, - UDISKS_ERROR, - UDISKS_ERROR_OPTION_NOT_PERMITTED, - "Malformed mount option `%s'", - option); - g_string_free (str, TRUE); - goto out; - } - - /* first check if the mount option is allowed */ - if (!is_mount_option_allowed (fsmo, option, caller_uid)) - { - g_set_error (error, - UDISKS_ERROR, - UDISKS_ERROR_OPTION_NOT_PERMITTED, - "Mount option `%s' is not allowed", - option); - g_string_free (str, TRUE); - goto out; - } - - g_string_append_c (str, ','); - g_string_append (str, option); - } - options_to_use_str = g_string_free (str, FALSE); - - out: - g_strfreev (options_to_use); - - g_assert (options_to_use_str == NULL || g_utf8_validate (options_to_use_str, -1, NULL)); - - if (out_auth_no_user_interaction != NULL) - *out_auth_no_user_interaction = auth_no_user_interaction; - - return options_to_use_str; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gchar * -ensure_utf8 (const gchar *s) -{ - const gchar *end; - gchar *ret; - - if (!g_utf8_validate (s, -1, &end)) - { - gchar *tmp; - gint pos; - /* TODO: could possibly return a nicer UTF-8 string */ - pos = (gint) (end - s); - tmp = g_strndup (s, end - s); - ret = g_strdup_printf ("%s (Invalid UTF-8 at byte %d)", tmp, pos); - g_free (tmp); - } - else - { - ret = g_strdup (s); - } - - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/* - * calculate_mount_point: - * @block: A #UDisksBlockDevice. - * @block_probed: A #UDisksBlockDeviceProbed or %NULL. - * @fs_type: The file system type to mount with - * @error: Return location for error or %NULL. - * - * Calculates the mount point to use. - * - * Returns: A UTF-8 string with the mount point to use or %NULL if @error is set. Free with g_free(). - */ -static gchar * -calculate_mount_point (UDisksBlockDevice *block, - UDisksBlockDeviceProbed *block_probed, - const gchar *fs_type, - GError **error) -{ - const gchar *label; - const gchar *uuid; - gchar *mount_point; - gchar *orig_mount_point; - GString *str; - gchar *s; - guint n; - - label = NULL; - uuid = NULL; - if (block_probed != NULL) - { - label = udisks_block_device_probed_get_label (block_probed); - uuid = udisks_block_device_probed_get_uuid (block_probed); - } - - /* NOTE: UTF-8 has the nice property that valid UTF-8 strings only contains - * the byte 0x2F if it's for the '/' character (U+002F SOLIDUS). - * - * See http://en.wikipedia.org/wiki/UTF-8 for details. - */ - - if (label != NULL && strlen (label) > 0) - { - str = g_string_new ("/media/"); - s = ensure_utf8 (label); - for (n = 0; s[n] != '\0'; n++) - { - gint c = s[n]; - if (c == '/') - g_string_append_c (str, '_'); - else - g_string_append_c (str, c); - } - mount_point = g_string_free (str, FALSE); - g_free (s); - } - else if (uuid != NULL && strlen (uuid) > 0) - { - str = g_string_new ("/media/"); - s = ensure_utf8 (uuid); - for (n = 0; s[n] != '\0'; n++) - { - gint c = s[n]; - if (c == '/') - g_string_append_c (str, '_'); - else - g_string_append_c (str, c); - } - mount_point = g_string_free (str, FALSE); - g_free (s); - } - else - { - mount_point = g_strdup ("/media/disk"); - } - - /* ... then uniqify the mount point */ - orig_mount_point = g_strdup (mount_point); - n = 1; - while (TRUE) - { - if (!g_file_test (mount_point, G_FILE_TEST_EXISTS)) - { - break; - } - else - { - g_free (mount_point); - mount_point = g_strdup_printf ("%s%d", orig_mount_point, n++); - } - } - g_free (orig_mount_point); - - return mount_point; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/* runs in thread dedicated to handling method call */ -static gboolean -handle_mount (UDisksFilesystem *filesystem, - GDBusMethodInvocation *invocation, - const gchar *requested_fs_type, - const gchar* const *requested_options) -{ - GDBusObject *object; - UDisksDaemon *daemon; - UDisksPersistentStore *store; - gboolean ret; - uid_t caller_uid; - UDisksBlockDevice *block; - UDisksBlockDeviceProbed *block_probed; - GDBusInterface *iface; - const gchar * const *existing_mount_points; - const gchar *probed_fs_usage; - const gchar *probed_fs_type; - gchar *fs_type_to_use; - gchar *mount_options_to_use; - gchar *mount_point_to_use; - gchar *escaped_fs_type_to_use; - gchar *escaped_mount_options_to_use; - gchar *escaped_mount_point_to_use; - gchar *error_message; - GError *error; - PolkitSubject *auth_subject; - const gchar *auth_action_id; - PolkitDetails *auth_details; - gboolean auth_no_user_interaction; - PolkitCheckAuthorizationFlags auth_flags; - PolkitAuthorizationResult *auth_result; - - ret = FALSE; - object = NULL; - daemon = NULL; - block = NULL; - block_probed = NULL; - error_message = NULL; - fs_type_to_use = NULL; - mount_options_to_use = NULL; - mount_point_to_use = NULL; - escaped_fs_type_to_use = NULL; - escaped_mount_options_to_use = NULL; - escaped_mount_point_to_use = NULL; - auth_subject = NULL; - auth_details = NULL; - auth_result = NULL; - auth_no_user_interaction = FALSE; - - object = g_dbus_interface_get_object (G_DBUS_INTERFACE (filesystem)); - block = UDISKS_BLOCK_DEVICE (g_dbus_object_lookup_interface (object, "org.freedesktop.UDisks2.BlockDevice")); - iface = g_dbus_object_lookup_interface (object, "org.freedesktop.UDisks2.BlockDevice.Probed"); - block_probed = iface != NULL ? UDISKS_BLOCK_DEVICE_PROBED (iface) : NULL; - - daemon = udisks_linux_block_get_daemon (UDISKS_LINUX_BLOCK (object)); - store = udisks_daemon_get_persistent_store (daemon); - - /* TODO: check if mount point is managed by e.g. /etc/fstab or - * similar - if so, use that instead of managing mount points - * in /media - */ - - /* Fail if the device is already mounted */ - existing_mount_points = udisks_filesystem_get_mount_points (filesystem); - if (existing_mount_points != NULL && g_strv_length ((gchar **) existing_mount_points) > 0) - { - GString *str; - guint n; - str = g_string_new (NULL); - for (n = 0; existing_mount_points[n] != NULL; n++) - { - if (n > 0) - g_string_append (str, ", "); - g_string_append_printf (str, "`%s'", existing_mount_points[n]); - } - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_ALREADY_MOUNTED, - "Device %s is already mounted at %s.\n", - udisks_block_device_get_device (block), - str->str); - g_string_free (str, TRUE); - goto out; - } - - /* Fail if the device is not mountable - we actually allow mounting - * devices that are not probed since since it could be that we just - * don't have the data in the udev database but the device has a - * filesystem *anyway*... - * - * For example, this applies to PC floppy devices - automatically - * probing for media them creates annoying noise. So they won't - * appear in the udev database. - */ - probed_fs_usage = NULL; - probed_fs_type = NULL; - if (block_probed != NULL) - { - probed_fs_usage = udisks_block_device_probed_get_usage (block_probed); - probed_fs_type = udisks_block_device_probed_get_type (block_probed); - } - if (probed_fs_usage != NULL && strlen (probed_fs_usage) > 0 && - g_strcmp0 (probed_fs_usage, "filesystem") != 0) - { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Cannot mount block device %s with probed usage `%s' - expected `filesystem'", - udisks_block_device_get_device (block), - probed_fs_usage); - goto out; - } - - /* we need the uid of the caller to check mount options */ - error = NULL; - if (!get_uid_sync (invocation, NULL /* GCancellable */, &caller_uid, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - g_error_free (error); - goto out; - } - - /* calculate filesystem type (guaranteed to be valid UTF-8) */ - error = NULL; - fs_type_to_use = calculate_fs_type (block, - block_probed, - requested_fs_type, - &error); - if (fs_type_to_use == NULL) - { - g_dbus_method_invocation_return_gerror (invocation, error); - g_error_free (error); - goto out; - } - - /* calculate mount options (guaranteed to be valid UTF-8) */ - error = NULL; - mount_options_to_use = calculate_mount_options (block, - block_probed, - caller_uid, - fs_type_to_use, - requested_options, - &auth_no_user_interaction, - &error); - if (mount_options_to_use == NULL) - { - g_dbus_method_invocation_return_gerror (invocation, error); - g_error_free (error); - goto out; - } - - /* calculate mount point (guaranteed to be valid UTF-8) */ - error = NULL; - mount_point_to_use = calculate_mount_point (block, - block_probed, - fs_type_to_use, - &error); - if (mount_point_to_use == NULL) - { - g_dbus_method_invocation_return_gerror (invocation, error); - g_error_free (error); - goto out; - } - - /* now check that the user is actually authorized to mount the device - * - * (TODO: fill in details and pick the right action_id) - */ - auth_action_id = "org.freedesktop.udisks2.filesystem-mount", - auth_subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (invocation)); - auth_flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE; - if (!auth_no_user_interaction) - auth_flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; - error = NULL; - auth_result = polkit_authority_check_authorization_sync (udisks_daemon_get_authority (daemon), - auth_subject, - auth_action_id, - auth_details, - auth_flags, - NULL, /* GCancellable* */ - &error); - if (auth_result == NULL) - { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Error checking authorization: %s (%s, %d)", - error->message, - g_quark_to_string (error->domain), - error->code); - g_error_free (error); - goto out; - } - if (!polkit_authorization_result_get_is_authorized (auth_result)) - { - g_dbus_method_invocation_return_error_literal (invocation, - UDISKS_ERROR, - polkit_authorization_result_get_is_challenge (auth_result) ? - UDISKS_ERROR_NOT_AUTHORIZED_CAN_OBTAIN : - UDISKS_ERROR_NOT_AUTHORIZED, - "Not authorized to perform operation"); - goto out; - } - - /* create the mount point */ - if (g_mkdir (mount_point_to_use, 0700) != 0) - { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Error creating mount point `%s': %m", - mount_point_to_use); - goto out; - } - - /* update the mounted-fs file */ - if (!udisks_persistent_store_mounted_fs_add (store, - udisks_block_device_get_device (block), - mount_point_to_use, - caller_uid, - &error)) - goto out; - - escaped_fs_type_to_use = g_strescape (fs_type_to_use, NULL); - escaped_mount_options_to_use = g_strescape (mount_options_to_use, NULL); - escaped_mount_point_to_use = g_strescape (mount_point_to_use, NULL); - - /* run mount(8) */ - if (!udisks_daemon_launch_spawned_job_sync (daemon, - NULL, /* GCancellable */ - &error_message, - NULL, /* input_string */ - "mount -t \"%s\" -o \"%s\" \"%s\" \"%s\"", - escaped_fs_type_to_use, - escaped_mount_options_to_use, - udisks_block_device_get_device (block), - escaped_mount_point_to_use)) - { - /* ugh, something went wrong.. we need to clean up the created mount point - * and also remove the entry from our mounted-fs file - * - * Either of these operations shouldn't really fail... - */ - error = NULL; - if (!udisks_persistent_store_mounted_fs_remove (store, - mount_point_to_use, - &error)) - { - udisks_daemon_log (daemon, - UDISKS_LOG_LEVEL_WARNING, - "Error removing mount point %s from filesystems file: %s (%s, %d)", - mount_point_to_use, - error->message, - g_quark_to_string (error->domain), - error->code); - g_error_free (error); - } - if (g_rmdir (mount_point_to_use) != 0) - { - udisks_daemon_log (daemon, - UDISKS_LOG_LEVEL_WARNING, - "Error removing directory %s: %m", - mount_point_to_use); - } - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Error mounting %s at %s: %s", - udisks_block_device_get_device (block), - mount_point_to_use, - error_message); - goto out; - } - - udisks_daemon_log (daemon, - UDISKS_LOG_LEVEL_INFO, - "Mounted %s at %s on behalf of uid %d", - udisks_block_device_get_device (block), - mount_point_to_use, - caller_uid); - - udisks_filesystem_complete_mount (filesystem, invocation, mount_point_to_use); - - out: - if (auth_subject != NULL) - g_object_unref (auth_subject); - if (auth_details != NULL) - g_object_unref (auth_details); - if (auth_result != NULL) - g_object_unref (auth_result); - g_free (error_message); - g_free (escaped_fs_type_to_use); - g_free (escaped_mount_options_to_use); - g_free (escaped_mount_point_to_use); - g_free (fs_type_to_use); - g_free (mount_options_to_use); - g_free (mount_point_to_use); - if (object != NULL) - g_object_unref (object); - if (block != NULL) - g_object_unref (block); - if (block_probed != NULL) - g_object_unref (block_probed); - - return TRUE; /* returning TRUE means that we handled the method invocation */ -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/* runs in thread dedicated to handling method call */ -static gboolean -handle_unmount (UDisksFilesystem *filesystem, - GDBusMethodInvocation *invocation, - const gchar* const *options) -{ - GDBusObject *object; - UDisksBlockDevice *block; - UDisksDaemon *daemon; - UDisksPersistentStore *store; - gchar *mount_point; - gchar *escaped_mount_point; - GError *error; - uid_t mounted_by_uid; - uid_t caller_uid; - gchar *error_message; - const gchar *const *mount_points; - guint n; - gboolean opt_force; - gboolean rc; - - mount_point = NULL; - escaped_mount_point = NULL; - error_message = NULL; - opt_force = FALSE; - - object = g_dbus_interface_get_object (G_DBUS_INTERFACE (filesystem)); - block = UDISKS_BLOCK_DEVICE (g_dbus_object_lookup_interface (object, "org.freedesktop.UDisks2.BlockDevice")); - daemon = udisks_linux_block_get_daemon (UDISKS_LINUX_BLOCK (object)); - store = udisks_daemon_get_persistent_store (daemon); - - for (n = 0; options != NULL && options[n] != NULL; n++) - { - const gchar *option = options[n]; - if (g_strcmp0 (option, "force") == 0) - { - opt_force = TRUE; - } - else - { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_OPTION_NOT_PERMITTED, - "Unsupported option `%s'", - option); - goto out; - } - } - - mount_points = udisks_filesystem_get_mount_points (filesystem); - if (mount_points == NULL || g_strv_length ((gchar **) mount_points) == 0) - { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_NOT_MOUNTED, - "Device `%s' is not mounted", - udisks_block_device_get_device (block)); - goto out; - } - - error = NULL; - mount_point = udisks_persistent_store_mounted_fs_find (store, - udisks_block_device_get_device (block), - &mounted_by_uid, - &error); - if (error != NULL) - { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Error when looking for entry `%s' in mounted-fs: %s (%s, %d)", - udisks_block_device_get_device (block), - error->message, - g_quark_to_string (error->domain), - error->code); - g_error_free (error); - goto out; - } - if (mount_point == NULL) - { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Entry for `%s' not found in mounted-fs", - udisks_block_device_get_device (block)); - goto out; - } - - /* TODO: allow unmounting stuff not in the mounted-fs file? */ - - error = NULL; - if (!get_uid_sync (invocation, NULL, &caller_uid, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - g_error_free (error); - goto out; - } - - if (caller_uid != 0 && (caller_uid != mounted_by_uid)) - { - /* TODO: allow with special authorization (unmount-others) */ - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_MOUNTED_BY_OTHER_USER, - "Cannot unmount filesystem at `%s' mounted by other user with uid %d", - mount_point, - mounted_by_uid); - goto out; - } - - /* otherwise go ahead and unmount the filesystem */ - if (!udisks_persistent_store_mounted_fs_currently_unmounting_add (store, mount_point)) - { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_ALREADY_UNMOUNTING, - "Cannot unmount %s: Mount point `%s' is currently being unmounted", - udisks_block_device_get_device (block), - mount_point); - goto out; - } - - escaped_mount_point = g_strescape (mount_point, NULL); - if (opt_force) - { - /* right now -l is the only way to "force unmount" file systems... */ - rc = udisks_daemon_launch_spawned_job_sync (daemon, - NULL, /* GCancellable */ - &error_message, - NULL, /* input_string */ - "umount -l \"%s\"", - escaped_mount_point); - } - else - { - rc = udisks_daemon_launch_spawned_job_sync (daemon, - NULL, /* GCancellable */ - &error_message, - NULL, /* input_string */ - "umount \"%s\"", - escaped_mount_point); - } - if (!rc) - { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Error unmounting %s from %s: %s", - udisks_block_device_get_device (block), - mount_point, - error_message); - udisks_persistent_store_mounted_fs_currently_unmounting_remove (store, mount_point); - goto out; - } - - /* OK, filesystem unmounted.. now to remove the entry from mounted-fs as well as the mount point */ - error = NULL; - if (!udisks_persistent_store_mounted_fs_remove (store, - mount_point, - &error)) - { - if (error == NULL) - { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Error removing entry for `%s' from mounted-fs: Entry not found", - mount_point); - } - else - { - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Error removing entry for `%s' from mounted-fs: %s (%s, %d)", - mount_point, - error->message, - g_quark_to_string (error->domain), - error->code); - g_error_free (error); - } - udisks_persistent_store_mounted_fs_currently_unmounting_remove (store, mount_point); - goto out; - } - udisks_persistent_store_mounted_fs_currently_unmounting_remove (store, mount_point); - - /* OK, removed the entry. Finally: nuke the mount point */ - if (g_rmdir (mount_point) != 0) - { - udisks_daemon_log (daemon, - UDISKS_LOG_LEVEL_ERROR, - "Error removing mount point `%s': %m", - mount_point); - g_dbus_method_invocation_return_error (invocation, - UDISKS_ERROR, - UDISKS_ERROR_FAILED, - "Error removing mount point `%s': %m", - mount_point); - goto out; - } - - udisks_daemon_log (daemon, - UDISKS_LOG_LEVEL_INFO, - "Unmounted %s from %s on behalf of uid %d", - udisks_block_device_get_device (block), - mount_point, - caller_uid); - - udisks_filesystem_complete_unmount (filesystem, invocation); - - out: - g_free (error_message); - g_free (escaped_mount_point); - g_free (mount_point); - g_object_unref (block); - g_object_unref (object); - return TRUE; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -filesystem_iface_init (UDisksFilesystemIface *iface) -{ - iface->handle_mount = handle_mount; - iface->handle_unmount = handle_unmount; -} - -/* ---------------------------------------------------------------------------------------------------- */ diff --git a/src/udisksfilesystemimpl.h b/src/udisksfilesystemimpl.h deleted file mode 100644 index 1f17943..0000000 --- a/src/udisksfilesystemimpl.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2007-2010 David Zeuthen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef __UDISKS_FILESYSTEM_IMPL_H__ -#define __UDISKS_FILESYSTEM_IMPL_H__ - -#include "types.h" - -G_BEGIN_DECLS - -#define UDISKS_TYPE_FILESYSTEM_IMPL (udisks_filesystem_impl_get_type ()) -#define UDISKS_FILESYSTEM_IMPL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), UDISKS_TYPE_FILESYSTEM_IMPL, UDisksFilesystemImpl)) -#define UDISKS_IS_FILESYSTEM_IMPL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UDISKS_TYPE_FILESYSTEM_IMPL)) - -GType udisks_filesystem_impl_get_type (void) G_GNUC_CONST; - -G_END_DECLS - -#endif /* __UDISKS_FILESYSTEM_IMPL_H__ */ diff --git a/src/udiskslinuxblock.c b/src/udiskslinuxblock.c index f94b395..2778b53 100644 --- a/src/udiskslinuxblock.c +++ b/src/udiskslinuxblock.c @@ -20,13 +20,21 @@ #include "config.h" +#include +#include +#include + +#include +#include +#include + #include "udisksdaemon.h" #include "udisksdaemonutil.h" #include "udiskslinuxblock.h" -#include "udisksfilesystemimpl.h" #include "udisksmount.h" #include "udisksmountmonitor.h" #include "udiskslinuxdrive.h" +#include "udiskspersistentstore.h" /** * SECTION:udiskslinuxblock @@ -56,8 +64,6 @@ struct _UDisksLinuxBlock /* interface */ UDisksLinuxSysfsDevice *iface_linux_sysfs_device; UDisksBlockDevice *iface_block_device; - UDisksBlockDeviceProbed *iface_block_device_probed; - UDisksFilesystem *iface_filesystem; }; struct _UDisksLinuxBlockClass @@ -96,10 +102,6 @@ udisks_linux_block_finalize (GObject *object) g_object_unref (block->iface_linux_sysfs_device); if (block->iface_block_device != NULL) g_object_unref (block->iface_block_device); - if (block->iface_block_device_probed != NULL) - g_object_unref (block->iface_block_device_probed); - if (block->iface_filesystem != NULL) - g_object_unref (block->iface_filesystem); if (G_OBJECT_CLASS (udisks_linux_block_parent_class)->finalize != NULL) G_OBJECT_CLASS (udisks_linux_block_parent_class)->finalize (object); @@ -292,6 +294,7 @@ udisks_linux_block_get_device (UDisksLinuxBlock *block) /* ---------------------------------------------------------------------------------------------------- */ typedef gboolean (*HasInterfaceFunc) (UDisksLinuxBlock *block); +typedef void (*ConnectInterfaceFunc) (UDisksLinuxBlock *block); typedef void (*UpdateInterfaceFunc) (UDisksLinuxBlock *block, const gchar *uevent_action, GDBusInterface *interface); @@ -300,6 +303,7 @@ static void update_iface (UDisksLinuxBlock *block, const gchar *uevent_action, HasInterfaceFunc has_func, + ConnectInterfaceFunc connect_func, UpdateInterfaceFunc update_func, GType stub_type, gpointer _interface_pointer) @@ -323,6 +327,8 @@ update_iface (UDisksLinuxBlock *block, if (has) { *interface_pointer = g_object_new (stub_type, NULL); + if (connect_func != NULL) + connect_func (block); add = TRUE; } } @@ -356,6 +362,30 @@ block_device_check (UDisksLinuxBlock *block) return TRUE; } +static gboolean +handle_mount (UDisksBlockDevice *block, + GDBusMethodInvocation *invocation, + const gchar *requested_fs_type, + const gchar* const *requested_options); + +static gboolean +handle_unmount (UDisksBlockDevice *block, + GDBusMethodInvocation *invocation, + const gchar* const *options); + +static void +block_device_connect (UDisksLinuxBlock *block) +{ + g_signal_connect (block->iface_block_device, + "handle-filesystem-mount", + G_CALLBACK (handle_mount), + NULL); + g_signal_connect (block->iface_block_device, + "handle-filesystem-unmount", + G_CALLBACK (handle_unmount), + NULL); +} + static gchar * find_drive (GDBusObjectManager *object_manager, GUdevDevice *block_device) @@ -416,6 +446,10 @@ block_device_update (UDisksLinuxBlock *block, GUdevDeviceNumber dev; GDBusObjectManager *object_manager; gchar *drive_object_path; + gchar *s; + GList *mounts; + GList *l; + GPtrArray *p; dev = g_udev_device_get_device_number (block->device); @@ -439,36 +473,33 @@ block_device_update (UDisksLinuxBlock *block, { udisks_block_device_set_drive (iface, "/"); } -} - -/* ---------------------------------------------------------------------------------------------------- */ -/* org.freedesktop.UDisks.BlockDeviceProbed */ - -static gboolean -block_device_probed_check (UDisksLinuxBlock *block) -{ - return g_udev_device_has_property (block->device, "ID_FS_USAGE"); -} - -static void -block_device_probed_update (UDisksLinuxBlock *block, - const gchar *uevent_action, - GDBusInterface *_iface) -{ - UDisksBlockDeviceProbed *iface = UDISKS_BLOCK_DEVICE_PROBED (_iface); - gchar *s; - - udisks_block_device_probed_set_usage (iface, g_udev_device_get_property (block->device, "ID_FS_USAGE")); - udisks_block_device_probed_set_type (iface, g_udev_device_get_property (block->device, "ID_FS_TYPE")); - udisks_block_device_probed_set_version (iface, g_udev_device_get_property (block->device, "ID_FS_VERSION")); + udisks_block_device_set_id_usage (iface, g_udev_device_get_property (block->device, "ID_FS_USAGE")); + udisks_block_device_set_id_type (iface, g_udev_device_get_property (block->device, "ID_FS_TYPE")); + udisks_block_device_set_id_version (iface, g_udev_device_get_property (block->device, "ID_FS_VERSION")); s = udisks_decode_udev_string (g_udev_device_get_property (block->device, "ID_FS_LABEL_ENC")); - udisks_block_device_probed_set_label (iface, s); + udisks_block_device_set_id_label (iface, s); g_free (s); - s = udisks_decode_udev_string (g_udev_device_get_property (block->device, "ID_FS_UUID_ENC")); - udisks_block_device_probed_set_uuid (iface, s); + udisks_block_device_set_id_uuid (iface, s); g_free (s); + + p = g_ptr_array_new (); + mounts = udisks_mount_monitor_get_mounts_for_dev (block->mount_monitor, + g_udev_device_get_device_number (block->device)); + /* we are guaranteed that the list is sorted so if there are + * multiple mounts we'll always get the same order + */ + for (l = mounts; l != NULL; l = l->next) + { + UDisksMount *mount = UDISKS_MOUNT (l->data); + g_ptr_array_add (p, (gpointer) udisks_mount_get_mount_path (mount)); + } + g_ptr_array_add (p, NULL); + udisks_block_device_set_filesystem_mount_points (iface, (const gchar *const *) p->pdata); + g_ptr_array_free (p, TRUE); + g_list_foreach (mounts, (GFunc) g_object_unref, NULL); + g_list_free (mounts); } /* ---------------------------------------------------------------------------------------------------- */ @@ -495,44 +526,6 @@ linux_sysfs_device_update (UDisksLinuxBlock *block, } /* ---------------------------------------------------------------------------------------------------- */ -/* org.freedesktop.UDisks.Filesystem */ - -static gboolean -filesystem_check (UDisksLinuxBlock *block) -{ - return g_strcmp0 (g_udev_device_get_property (block->device, "ID_FS_USAGE"), "filesystem") == 0; -} - -static void -filesystem_update (UDisksLinuxBlock *block, - const gchar *uevent_action, - GDBusInterface *_iface) -{ - UDisksFilesystem *iface = UDISKS_FILESYSTEM (_iface); - GList *mounts; - GList *l; - GPtrArray *p; - - p = g_ptr_array_new (); - mounts = udisks_mount_monitor_get_mounts_for_dev (block->mount_monitor, - g_udev_device_get_device_number (block->device)); - /* we are guaranteed that the list is sorted so if there are - * multiple mounts we'll always get the same order - */ - for (l = mounts; l != NULL; l = l->next) - { - UDisksMount *mount = UDISKS_MOUNT (l->data); - g_ptr_array_add (p, (gpointer) udisks_mount_get_mount_path (mount)); - } - g_ptr_array_add (p, NULL); - udisks_filesystem_set_mount_points (iface, (const gchar *const *) p->pdata); - g_ptr_array_free (p, TRUE); - - g_list_foreach (mounts, (GFunc) g_object_unref, NULL); - g_list_free (mounts); -} - -/* ---------------------------------------------------------------------------------------------------- */ /** * udisks_linux_block_uevent: @@ -557,14 +550,10 @@ udisks_linux_block_uevent (UDisksLinuxBlock *block, g_object_notify (G_OBJECT (block), "device"); } - update_iface (block, action, linux_sysfs_device_check, linux_sysfs_device_update, + update_iface (block, action, linux_sysfs_device_check, NULL, linux_sysfs_device_update, UDISKS_TYPE_LINUX_SYSFS_DEVICE_STUB, &block->iface_linux_sysfs_device); - update_iface (block, action, block_device_check, block_device_update, + update_iface (block, action, block_device_check, block_device_connect, block_device_update, UDISKS_TYPE_BLOCK_DEVICE_STUB, &block->iface_block_device); - update_iface (block, action, block_device_probed_check, block_device_probed_update, - UDISKS_TYPE_BLOCK_DEVICE_PROBED_STUB, &block->iface_block_device_probed); - update_iface (block, action, filesystem_check, filesystem_update, - UDISKS_TYPE_FILESYSTEM_IMPL, &block->iface_filesystem); } /* ---------------------------------------------------------------------------------------------------- */ @@ -590,3 +579,1121 @@ on_mount_monitor_mount_removed (UDisksMountMonitor *monitor, } /* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +get_uid_sync (GDBusMethodInvocation *invocation, + GCancellable *cancellable, + uid_t *out_uid, + GError **error) +{ + gboolean ret; + const gchar *caller; + GVariant *value; + GError *local_error; + + ret = FALSE; + + caller = g_dbus_method_invocation_get_sender (invocation); + + local_error = NULL; + value = g_dbus_connection_call_sync (g_dbus_method_invocation_get_connection (invocation), + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface */ + "GetConnectionUnixUser", /* method */ + g_variant_new ("(s)", caller), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + -1, /* timeout_msec */ + cancellable, + &local_error); + if (value == NULL) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error determining uid of caller %s: %s (%s, %d)", + caller, + local_error->message, + g_quark_to_string (local_error->domain), + local_error->code); + g_error_free (local_error); + goto out; + } + + G_STATIC_ASSERT (sizeof (uid_t) == sizeof (guint32)); + g_variant_get (value, "(u)", out_uid); + + ret = TRUE; + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + const gchar *fstype; + const gchar * const *defaults; + const gchar * const *allow; + const gchar * const *allow_uid_self; + const gchar * const *allow_gid_self; +} FSMountOptions; + +/* ---------------------- vfat -------------------- */ + +static const gchar *vfat_defaults[] = { "uid=", "gid=", "shortname=mixed", "dmask=0077", "utf8=1", "showexec", NULL }; +static const gchar *vfat_allow[] = { "flush", "utf8=", "shortname=", "umask=", "dmask=", "fmask=", "codepage=", "iocharset=", "usefree", "showexec", NULL }; +static const gchar *vfat_allow_uid_self[] = { "uid=", NULL }; +static const gchar *vfat_allow_gid_self[] = { "gid=", NULL }; + +/* ---------------------- ntfs -------------------- */ +/* this is assuming that ntfs-3g is used */ + +static const gchar *ntfs_defaults[] = { "uid=", "gid=", "dmask=0077", "fmask=0177", NULL }; +static const gchar *ntfs_allow[] = { "umask=", "dmask=", "fmask=", NULL }; +static const gchar *ntfs_allow_uid_self[] = { "uid=", NULL }; +static const gchar *ntfs_allow_gid_self[] = { "gid=", NULL }; + +/* ---------------------- iso9660 -------------------- */ + +static const gchar *iso9660_defaults[] = { "uid=", "gid=", "iocharset=utf8", "mode=0400", "dmode=0500", NULL }; +static const gchar *iso9660_allow[] = { "norock", "nojoliet", "iocharset=", "mode=", "dmode=", NULL }; +static const gchar *iso9660_allow_uid_self[] = { "uid=", NULL }; +static const gchar *iso9660_allow_gid_self[] = { "gid=", NULL }; + +/* ---------------------- udf -------------------- */ + +static const gchar *udf_defaults[] = { "uid=", "gid=", "iocharset=utf8", "umask=0077", NULL }; +static const gchar *udf_allow[] = { "iocharset=", "umask=", NULL }; +static const gchar *udf_allow_uid_self[] = { "uid=", NULL }; +static const gchar *udf_allow_gid_self[] = { "gid=", NULL }; + +/* ------------------------------------------------ */ +/* TODO: support context= */ + +static const gchar *any_allow[] = { "exec", "noexec", "nodev", "nosuid", "atime", "noatime", "nodiratime", "ro", "rw", "sync", "dirsync", NULL }; + +static const FSMountOptions fs_mount_options[] = + { + { "vfat", vfat_defaults, vfat_allow, vfat_allow_uid_self, vfat_allow_gid_self }, + { "ntfs", ntfs_defaults, ntfs_allow, ntfs_allow_uid_self, ntfs_allow_gid_self }, + { "iso9660", iso9660_defaults, iso9660_allow, iso9660_allow_uid_self, iso9660_allow_gid_self }, + { "udf", udf_defaults, udf_allow, udf_allow_uid_self, udf_allow_gid_self }, + }; + +/* ------------------------------------------------ */ + +static int num_fs_mount_options = sizeof(fs_mount_options) / sizeof(FSMountOptions); + +static const FSMountOptions * +find_mount_options_for_fs (const gchar *fstype) +{ + int n; + const FSMountOptions *fsmo; + + for (n = 0; n < num_fs_mount_options; n++) + { + fsmo = fs_mount_options + n; + if (g_strcmp0 (fsmo->fstype, fstype) == 0) + goto out; + } + + fsmo = NULL; + out: + return fsmo; +} + +static gid_t +find_primary_gid (uid_t uid) +{ + struct passwd *pw; + gid_t gid; + + gid = (gid_t) - 1; + + pw = getpwuid (uid); + if (pw == NULL) + { + g_warning ("Couldn't look up uid %d: %m", uid); + goto out; + } + gid = pw->pw_gid; + + out: + return gid; +} + +static gboolean +is_uid_in_gid (uid_t uid, + gid_t gid) +{ + gboolean ret; + struct passwd *pw; + static gid_t supplementary_groups[128]; + int num_supplementary_groups = 128; + int n; + + /* TODO: use some #define instead of harcoding some random number like 128 */ + + ret = FALSE; + + pw = getpwuid (uid); + if (pw == NULL) + { + g_warning ("Couldn't look up uid %d: %m", uid); + goto out; + } + if (pw->pw_gid == gid) + { + ret = TRUE; + goto out; + } + + if (getgrouplist (pw->pw_name, pw->pw_gid, supplementary_groups, &num_supplementary_groups) < 0) + { + g_warning ("Couldn't find supplementary groups for uid %d: %m", uid); + goto out; + } + + for (n = 0; n < num_supplementary_groups; n++) + { + if (supplementary_groups[n] == gid) + { + ret = TRUE; + goto out; + } + } + + out: + return ret; +} + +static gboolean +is_mount_option_allowed (const FSMountOptions *fsmo, + const gchar *option, + uid_t caller_uid) +{ + int n; + gchar *endp; + uid_t uid; + gid_t gid; + gboolean allowed; + const gchar *ep; + gsize ep_len; + + allowed = FALSE; + + /* first run through the allowed mount options */ + if (fsmo != NULL) + { + for (n = 0; fsmo->allow != NULL && fsmo->allow[n] != NULL; n++) + { + ep = strstr (fsmo->allow[n], "="); + if (ep != NULL && ep[1] == '\0') + { + ep_len = ep - fsmo->allow[n] + 1; + if (strncmp (fsmo->allow[n], option, ep_len) == 0) + { + allowed = TRUE; + goto out; + } + } + else + { + if (strcmp (fsmo->allow[n], option) == 0) + { + allowed = TRUE; + goto out; + } + } + } + } + for (n = 0; any_allow[n] != NULL; n++) + { + ep = strstr (any_allow[n], "="); + if (ep != NULL && ep[1] == '\0') + { + ep_len = ep - any_allow[n] + 1; + if (strncmp (any_allow[n], option, ep_len) == 0) + { + allowed = TRUE; + goto out; + } + } + else + { + if (strcmp (any_allow[n], option) == 0) + { + allowed = TRUE; + goto out; + } + } + } + + /* .. then check for mount options where the caller is allowed to pass + * in his own uid + */ + if (fsmo != NULL) + { + for (n = 0; fsmo->allow_uid_self != NULL && fsmo->allow_uid_self[n] != NULL; n++) + { + const gchar *r_mount_option = fsmo->allow_uid_self[n]; + if (g_str_has_prefix (option, r_mount_option)) + { + uid = strtol (option + strlen (r_mount_option), &endp, 10); + if (*endp != '\0') + continue; + if (uid == caller_uid) + { + allowed = TRUE; + goto out; + } + } + } + } + + /* .. ditto for gid + */ + if (fsmo != NULL) + { + for (n = 0; fsmo->allow_gid_self != NULL && fsmo->allow_gid_self[n] != NULL; n++) + { + const gchar *r_mount_option = fsmo->allow_gid_self[n]; + if (g_str_has_prefix (option, r_mount_option)) + { + gid = strtol (option + strlen (r_mount_option), &endp, 10); + if (*endp != '\0') + continue; + if (is_uid_in_gid (caller_uid, gid)) + { + allowed = TRUE; + goto out; + } + } + } + } + + out: + return allowed; +} + +static gchar ** +prepend_default_mount_options (const FSMountOptions *fsmo, + uid_t caller_uid, + const gchar * const *given_options) +{ + GPtrArray *options; + int n; + gchar *s; + gid_t gid; + + options = g_ptr_array_new (); + if (fsmo != NULL) + { + for (n = 0; fsmo->defaults != NULL && fsmo->defaults[n] != NULL; n++) + { + const gchar *option = fsmo->defaults[n]; + + if (strcmp (option, "uid=") == 0) + { + s = g_strdup_printf ("uid=%d", caller_uid); + g_ptr_array_add (options, s); + } + else if (strcmp (option, "gid=") == 0) + { + gid = find_primary_gid (caller_uid); + if (gid != (gid_t) - 1) + { + s = g_strdup_printf ("gid=%d", gid); + g_ptr_array_add (options, s); + } + } + else + { + g_ptr_array_add (options, g_strdup (option)); + } + } + } + for (n = 0; given_options[n] != NULL; n++) + { + g_ptr_array_add (options, g_strdup (given_options[n])); + } + + g_ptr_array_add (options, NULL); + + return (char **) g_ptr_array_free (options, FALSE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* + * calculate_fs_type: + * @block: A #UDisksBlockDevice. + * @requested_fs_type: The requested file system type or %NULL. + * @error: Return location for error or %NULL. + * + * Calculates the file system type to use. + * + * Returns: A valid UTF-8 string with the filesystem type (may be "auto") or %NULL if @error is set. Free with g_free(). + */ +static gchar * +calculate_fs_type (UDisksBlockDevice *block, + const gchar *requested_fs_type, + GError **error) +{ + gchar *fs_type_to_use; + const gchar *probed_fs_usage; + const gchar *probed_fs_type; + + probed_fs_usage = NULL; + probed_fs_type = NULL; + if (block != NULL) + { + probed_fs_usage = udisks_block_device_get_id_usage (block); + probed_fs_type = udisks_block_device_get_id_type (block); + } + + fs_type_to_use = NULL; + if (requested_fs_type != NULL && strlen (requested_fs_type) > 0) + { + /* TODO: maybe check that it's compatible with probed_fs_type */ + fs_type_to_use = g_strdup (requested_fs_type); + } + else + { + if (probed_fs_type != NULL && strlen (probed_fs_type) > 0) + fs_type_to_use = g_strdup (probed_fs_type); + else + fs_type_to_use = g_strdup ("auto"); + } + + g_assert (fs_type_to_use == NULL || g_utf8_validate (fs_type_to_use, -1, NULL)); + + return fs_type_to_use; +} + +/* + * calculate_mount_options: + * @block: A #UDisksBlockDevice. + * @caller_uid: The uid of the caller making the request. + * @fs_type: The filesystem type to use or %NULL. + * @requested_options: Options requested by the caller. + * @out_auth_no_user_interaction: Return location for whether the 'auth_no_user_interaction' option was passed or %NULL. + * @error: Return location for error or %NULL. + * + * Calculates the mount option string to use. Ensures (by returning an + * error) that only safe options are used. + * + * Returns: A string with mount options or %NULL if @error is set. Free with g_free(). + */ +static gchar * +calculate_mount_options (UDisksBlockDevice *block, + uid_t caller_uid, + const gchar *fs_type, + const gchar *const *requested_options, + gboolean *out_auth_no_user_interaction, + GError **error) +{ + const FSMountOptions *fsmo; + gchar **options_to_use; + gchar *options_to_use_str; + GString *str; + guint n; + gboolean auth_no_user_interaction; + + options_to_use = NULL; + options_to_use_str = NULL; + auth_no_user_interaction = FALSE; + + fsmo = find_mount_options_for_fs (fs_type); + + /* always prepend some reasonable default mount options; these are + * chosen here; the user can override them if he wants to + */ + options_to_use = prepend_default_mount_options (fsmo, caller_uid, requested_options); + + /* validate mount options */ + str = g_string_new ("uhelper=udisks2,nodev,nosuid"); + for (n = 0; options_to_use[n] != NULL; n++) + { + const gchar *option = options_to_use[n]; + + if (g_strcmp0 (option, "auth_no_user_interaction") == 0) + { + auth_no_user_interaction = TRUE; + continue; + } + + /* avoid attacks like passing "shortname=lower,uid=0" as a single mount option */ + if (strstr (option, ",") != NULL) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_OPTION_NOT_PERMITTED, + "Malformed mount option `%s'", + option); + g_string_free (str, TRUE); + goto out; + } + + /* first check if the mount option is allowed */ + if (!is_mount_option_allowed (fsmo, option, caller_uid)) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_OPTION_NOT_PERMITTED, + "Mount option `%s' is not allowed", + option); + g_string_free (str, TRUE); + goto out; + } + + g_string_append_c (str, ','); + g_string_append (str, option); + } + options_to_use_str = g_string_free (str, FALSE); + + out: + g_strfreev (options_to_use); + + g_assert (options_to_use_str == NULL || g_utf8_validate (options_to_use_str, -1, NULL)); + + if (out_auth_no_user_interaction != NULL) + *out_auth_no_user_interaction = auth_no_user_interaction; + + return options_to_use_str; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar * +ensure_utf8 (const gchar *s) +{ + const gchar *end; + gchar *ret; + + if (!g_utf8_validate (s, -1, &end)) + { + gchar *tmp; + gint pos; + /* TODO: could possibly return a nicer UTF-8 string */ + pos = (gint) (end - s); + tmp = g_strndup (s, end - s); + ret = g_strdup_printf ("%s (Invalid UTF-8 at byte %d)", tmp, pos); + g_free (tmp); + } + else + { + ret = g_strdup (s); + } + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* + * calculate_mount_point: + * @block: A #UDisksBlockDevice. + * @fs_type: The file system type to mount with + * @error: Return location for error or %NULL. + * + * Calculates the mount point to use. + * + * Returns: A UTF-8 string with the mount point to use or %NULL if @error is set. Free with g_free(). + */ +static gchar * +calculate_mount_point (UDisksBlockDevice *block, + const gchar *fs_type, + GError **error) +{ + const gchar *label; + const gchar *uuid; + gchar *mount_point; + gchar *orig_mount_point; + GString *str; + gchar *s; + guint n; + + label = NULL; + uuid = NULL; + if (block != NULL) + { + label = udisks_block_device_get_id_label (block); + uuid = udisks_block_device_get_id_uuid (block); + } + + /* NOTE: UTF-8 has the nice property that valid UTF-8 strings only contains + * the byte 0x2F if it's for the '/' character (U+002F SOLIDUS). + * + * See http://en.wikipedia.org/wiki/UTF-8 for details. + */ + + if (label != NULL && strlen (label) > 0) + { + str = g_string_new ("/media/"); + s = ensure_utf8 (label); + for (n = 0; s[n] != '\0'; n++) + { + gint c = s[n]; + if (c == '/') + g_string_append_c (str, '_'); + else + g_string_append_c (str, c); + } + mount_point = g_string_free (str, FALSE); + g_free (s); + } + else if (uuid != NULL && strlen (uuid) > 0) + { + str = g_string_new ("/media/"); + s = ensure_utf8 (uuid); + for (n = 0; s[n] != '\0'; n++) + { + gint c = s[n]; + if (c == '/') + g_string_append_c (str, '_'); + else + g_string_append_c (str, c); + } + mount_point = g_string_free (str, FALSE); + g_free (s); + } + else + { + mount_point = g_strdup ("/media/disk"); + } + + /* ... then uniqify the mount point */ + orig_mount_point = g_strdup (mount_point); + n = 1; + while (TRUE) + { + if (!g_file_test (mount_point, G_FILE_TEST_EXISTS)) + { + break; + } + else + { + g_free (mount_point); + mount_point = g_strdup_printf ("%s%d", orig_mount_point, n++); + } + } + g_free (orig_mount_point); + + return mount_point; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* runs in thread dedicated to handling method call */ +static gboolean +handle_mount (UDisksBlockDevice *block, + GDBusMethodInvocation *invocation, + const gchar *requested_fs_type, + const gchar* const *requested_options) +{ + GDBusObject *object; + UDisksDaemon *daemon; + UDisksPersistentStore *store; + gboolean ret; + uid_t caller_uid; + const gchar * const *existing_mount_points; + const gchar *probed_fs_usage; + const gchar *probed_fs_type; + gchar *fs_type_to_use; + gchar *mount_options_to_use; + gchar *mount_point_to_use; + gchar *escaped_fs_type_to_use; + gchar *escaped_mount_options_to_use; + gchar *escaped_mount_point_to_use; + gchar *error_message; + GError *error; + PolkitSubject *auth_subject; + const gchar *auth_action_id; + PolkitDetails *auth_details; + gboolean auth_no_user_interaction; + PolkitCheckAuthorizationFlags auth_flags; + PolkitAuthorizationResult *auth_result; + + ret = FALSE; + object = NULL; + daemon = NULL; + error_message = NULL; + fs_type_to_use = NULL; + mount_options_to_use = NULL; + mount_point_to_use = NULL; + escaped_fs_type_to_use = NULL; + escaped_mount_options_to_use = NULL; + escaped_mount_point_to_use = NULL; + auth_subject = NULL; + auth_details = NULL; + auth_result = NULL; + auth_no_user_interaction = FALSE; + + object = g_dbus_interface_get_object (G_DBUS_INTERFACE (block)); + daemon = udisks_linux_block_get_daemon (UDISKS_LINUX_BLOCK (object)); + store = udisks_daemon_get_persistent_store (daemon); + + /* TODO: check if mount point is managed by e.g. /etc/fstab or + * similar - if so, use that instead of managing mount points + * in /media + */ + + /* Fail if the device is already mounted */ + existing_mount_points = udisks_block_device_get_filesystem_mount_points (block); + if (existing_mount_points != NULL && g_strv_length ((gchar **) existing_mount_points) > 0) + { + GString *str; + guint n; + str = g_string_new (NULL); + for (n = 0; existing_mount_points[n] != NULL; n++) + { + if (n > 0) + g_string_append (str, ", "); + g_string_append_printf (str, "`%s'", existing_mount_points[n]); + } + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_ALREADY_MOUNTED, + "Device %s is already mounted at %s.\n", + udisks_block_device_get_device (block), + str->str); + g_string_free (str, TRUE); + goto out; + } + + /* Fail if the device is not mountable - we actually allow mounting + * devices that are not probed since since it could be that we just + * don't have the data in the udev database but the device has a + * filesystem *anyway*... + * + * For example, this applies to PC floppy devices - automatically + * probing for media them creates annoying noise. So they won't + * appear in the udev database. + */ + probed_fs_usage = NULL; + probed_fs_type = NULL; + if (block != NULL) + { + probed_fs_usage = udisks_block_device_get_id_usage (block); + probed_fs_type = udisks_block_device_get_id_type (block); + } + if (probed_fs_usage != NULL && strlen (probed_fs_usage) > 0 && + g_strcmp0 (probed_fs_usage, "filesystem") != 0) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Cannot mount block device %s with probed usage `%s' - expected `filesystem'", + udisks_block_device_get_device (block), + probed_fs_usage); + goto out; + } + + /* we need the uid of the caller to check mount options */ + error = NULL; + if (!get_uid_sync (invocation, NULL /* GCancellable */, &caller_uid, &error)) + { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + goto out; + } + + /* calculate filesystem type (guaranteed to be valid UTF-8) */ + error = NULL; + fs_type_to_use = calculate_fs_type (block, + requested_fs_type, + &error); + if (fs_type_to_use == NULL) + { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + goto out; + } + + /* calculate mount options (guaranteed to be valid UTF-8) */ + error = NULL; + mount_options_to_use = calculate_mount_options (block, + caller_uid, + fs_type_to_use, + requested_options, + &auth_no_user_interaction, + &error); + if (mount_options_to_use == NULL) + { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + goto out; + } + + /* calculate mount point (guaranteed to be valid UTF-8) */ + error = NULL; + mount_point_to_use = calculate_mount_point (block, + fs_type_to_use, + &error); + if (mount_point_to_use == NULL) + { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + goto out; + } + + /* now check that the user is actually authorized to mount the device + * + * (TODO: fill in details and pick the right action_id) + */ + auth_action_id = "org.freedesktop.udisks2.filesystem-mount", + auth_subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (invocation)); + auth_flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE; + if (!auth_no_user_interaction) + auth_flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; + error = NULL; + auth_result = polkit_authority_check_authorization_sync (udisks_daemon_get_authority (daemon), + auth_subject, + auth_action_id, + auth_details, + auth_flags, + NULL, /* GCancellable* */ + &error); + if (auth_result == NULL) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error checking authorization: %s (%s, %d)", + error->message, + g_quark_to_string (error->domain), + error->code); + g_error_free (error); + goto out; + } + if (!polkit_authorization_result_get_is_authorized (auth_result)) + { + g_dbus_method_invocation_return_error_literal (invocation, + UDISKS_ERROR, + polkit_authorization_result_get_is_challenge (auth_result) ? + UDISKS_ERROR_NOT_AUTHORIZED_CAN_OBTAIN : + UDISKS_ERROR_NOT_AUTHORIZED, + "Not authorized to perform operation"); + goto out; + } + + /* create the mount point */ + if (g_mkdir (mount_point_to_use, 0700) != 0) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error creating mount point `%s': %m", + mount_point_to_use); + goto out; + } + + /* update the mounted-fs file */ + if (!udisks_persistent_store_mounted_fs_add (store, + udisks_block_device_get_device (block), + mount_point_to_use, + caller_uid, + &error)) + goto out; + + escaped_fs_type_to_use = g_strescape (fs_type_to_use, NULL); + escaped_mount_options_to_use = g_strescape (mount_options_to_use, NULL); + escaped_mount_point_to_use = g_strescape (mount_point_to_use, NULL); + + /* run mount(8) */ + if (!udisks_daemon_launch_spawned_job_sync (daemon, + NULL, /* GCancellable */ + &error_message, + NULL, /* input_string */ + "mount -t \"%s\" -o \"%s\" \"%s\" \"%s\"", + escaped_fs_type_to_use, + escaped_mount_options_to_use, + udisks_block_device_get_device (block), + escaped_mount_point_to_use)) + { + /* ugh, something went wrong.. we need to clean up the created mount point + * and also remove the entry from our mounted-fs file + * + * Either of these operations shouldn't really fail... + */ + error = NULL; + if (!udisks_persistent_store_mounted_fs_remove (store, + mount_point_to_use, + &error)) + { + udisks_daemon_log (daemon, + UDISKS_LOG_LEVEL_WARNING, + "Error removing mount point %s from filesystems file: %s (%s, %d)", + mount_point_to_use, + error->message, + g_quark_to_string (error->domain), + error->code); + g_error_free (error); + } + if (g_rmdir (mount_point_to_use) != 0) + { + udisks_daemon_log (daemon, + UDISKS_LOG_LEVEL_WARNING, + "Error removing directory %s: %m", + mount_point_to_use); + } + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error mounting %s at %s: %s", + udisks_block_device_get_device (block), + mount_point_to_use, + error_message); + goto out; + } + + udisks_daemon_log (daemon, + UDISKS_LOG_LEVEL_INFO, + "Mounted %s at %s on behalf of uid %d", + udisks_block_device_get_device (block), + mount_point_to_use, + caller_uid); + + udisks_block_device_complete_filesystem_mount (block, invocation, mount_point_to_use); + + out: + if (auth_subject != NULL) + g_object_unref (auth_subject); + if (auth_details != NULL) + g_object_unref (auth_details); + if (auth_result != NULL) + g_object_unref (auth_result); + g_free (error_message); + g_free (escaped_fs_type_to_use); + g_free (escaped_mount_options_to_use); + g_free (escaped_mount_point_to_use); + g_free (fs_type_to_use); + g_free (mount_options_to_use); + g_free (mount_point_to_use); + if (object != NULL) + g_object_unref (object); + + return TRUE; /* returning TRUE means that we handled the method invocation */ +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* runs in thread dedicated to handling method call */ +static gboolean +handle_unmount (UDisksBlockDevice *block, + GDBusMethodInvocation *invocation, + const gchar* const *options) +{ + GDBusObject *object; + UDisksDaemon *daemon; + UDisksPersistentStore *store; + gchar *mount_point; + gchar *escaped_mount_point; + GError *error; + uid_t mounted_by_uid; + uid_t caller_uid; + gchar *error_message; + const gchar *const *mount_points; + guint n; + gboolean opt_force; + gboolean rc; + + mount_point = NULL; + escaped_mount_point = NULL; + error_message = NULL; + opt_force = FALSE; + + object = g_dbus_interface_get_object (G_DBUS_INTERFACE (block)); + daemon = udisks_linux_block_get_daemon (UDISKS_LINUX_BLOCK (object)); + store = udisks_daemon_get_persistent_store (daemon); + + for (n = 0; options != NULL && options[n] != NULL; n++) + { + const gchar *option = options[n]; + if (g_strcmp0 (option, "force") == 0) + { + opt_force = TRUE; + } + else + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_OPTION_NOT_PERMITTED, + "Unsupported option `%s'", + option); + goto out; + } + } + + mount_points = udisks_block_device_get_filesystem_mount_points (block); + if (mount_points == NULL || g_strv_length ((gchar **) mount_points) == 0) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_NOT_MOUNTED, + "Device `%s' is not mounted", + udisks_block_device_get_device (block)); + goto out; + } + + error = NULL; + mount_point = udisks_persistent_store_mounted_fs_find (store, + udisks_block_device_get_device (block), + &mounted_by_uid, + &error); + if (error != NULL) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error when looking for entry `%s' in mounted-fs: %s (%s, %d)", + udisks_block_device_get_device (block), + error->message, + g_quark_to_string (error->domain), + error->code); + g_error_free (error); + goto out; + } + if (mount_point == NULL) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Entry for `%s' not found in mounted-fs", + udisks_block_device_get_device (block)); + goto out; + } + + /* TODO: allow unmounting stuff not in the mounted-fs file? */ + + error = NULL; + if (!get_uid_sync (invocation, NULL, &caller_uid, &error)) + { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + goto out; + } + + if (caller_uid != 0 && (caller_uid != mounted_by_uid)) + { + /* TODO: allow with special authorization (unmount-others) */ + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_MOUNTED_BY_OTHER_USER, + "Cannot unmount filesystem at `%s' mounted by other user with uid %d", + mount_point, + mounted_by_uid); + goto out; + } + + /* otherwise go ahead and unmount the filesystem */ + if (!udisks_persistent_store_mounted_fs_currently_unmounting_add (store, mount_point)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_ALREADY_UNMOUNTING, + "Cannot unmount %s: Mount point `%s' is currently being unmounted", + udisks_block_device_get_device (block), + mount_point); + goto out; + } + + escaped_mount_point = g_strescape (mount_point, NULL); + if (opt_force) + { + /* right now -l is the only way to "force unmount" file systems... */ + rc = udisks_daemon_launch_spawned_job_sync (daemon, + NULL, /* GCancellable */ + &error_message, + NULL, /* input_string */ + "umount -l \"%s\"", + escaped_mount_point); + } + else + { + rc = udisks_daemon_launch_spawned_job_sync (daemon, + NULL, /* GCancellable */ + &error_message, + NULL, /* input_string */ + "umount \"%s\"", + escaped_mount_point); + } + if (!rc) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error unmounting %s from %s: %s", + udisks_block_device_get_device (block), + mount_point, + error_message); + udisks_persistent_store_mounted_fs_currently_unmounting_remove (store, mount_point); + goto out; + } + + /* OK, filesystem unmounted.. now to remove the entry from mounted-fs as well as the mount point */ + error = NULL; + if (!udisks_persistent_store_mounted_fs_remove (store, + mount_point, + &error)) + { + if (error == NULL) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error removing entry for `%s' from mounted-fs: Entry not found", + mount_point); + } + else + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error removing entry for `%s' from mounted-fs: %s (%s, %d)", + mount_point, + error->message, + g_quark_to_string (error->domain), + error->code); + g_error_free (error); + } + udisks_persistent_store_mounted_fs_currently_unmounting_remove (store, mount_point); + goto out; + } + udisks_persistent_store_mounted_fs_currently_unmounting_remove (store, mount_point); + + /* OK, removed the entry. Finally: nuke the mount point */ + if (g_rmdir (mount_point) != 0) + { + udisks_daemon_log (daemon, + UDISKS_LOG_LEVEL_ERROR, + "Error removing mount point `%s': %m", + mount_point); + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error removing mount point `%s': %m", + mount_point); + goto out; + } + + udisks_daemon_log (daemon, + UDISKS_LOG_LEVEL_INFO, + "Unmounted %s from %s on behalf of uid %d", + udisks_block_device_get_device (block), + mount_point, + caller_uid); + + udisks_block_device_complete_filesystem_unmount (block, invocation); + + out: + g_free (error_message); + g_free (escaped_mount_point); + g_free (mount_point); + g_object_unref (object); + return TRUE; +} diff --git a/tools/udisksctl.c b/tools/udisksctl.c index c1a859e..8be94bc 100644 --- a/tools/udisksctl.c +++ b/tools/udisksctl.c @@ -570,8 +570,8 @@ handle_command_mount_unmount (gint *argc, GList *object_proxies; GDBusObjectProxy *object_proxy; UDisksBlockDevice *block; - UDisksFilesystem *filesystem; guint n; + const gchar * const *mount_points; ret = 1; opt_mount_unmount_object_path = NULL; @@ -637,17 +637,16 @@ handle_command_mount_unmount (gint *argc, object_proxies = g_dbus_proxy_manager_get_all (manager); for (l = object_proxies; l != NULL; l = l->next) { - const gchar * const *mount_points; gboolean is_mounted; object_proxy = G_DBUS_OBJECT_PROXY (l->data); - filesystem = UDISKS_PEEK_FILESYSTEM (object_proxy); + block = UDISKS_PEEK_BLOCK_DEVICE (object_proxy); - if (filesystem == NULL) + if (block == NULL) continue; is_mounted = FALSE; - mount_points = udisks_filesystem_get_mount_points (filesystem); + mount_points = udisks_block_device_get_filesystem_mount_points (block); if (mount_points != NULL && g_strv_length ((gchar **) mount_points) > 0) is_mounted = TRUE; @@ -670,15 +669,13 @@ handle_command_mount_unmount (gint *argc, { object_proxy = G_DBUS_OBJECT_PROXY (l->data); block = UDISKS_PEEK_BLOCK_DEVICE (object_proxy); - filesystem = UDISKS_PEEK_FILESYSTEM (object_proxy); - if (block != NULL && filesystem != NULL) + if (block != NULL) { - const gchar * const *mount_points; gboolean is_mounted; is_mounted = FALSE; - mount_points = udisks_filesystem_get_mount_points (filesystem); + mount_points = udisks_block_device_get_filesystem_mount_points (block); if (mount_points != NULL && g_strv_length ((gchar **) mount_points) > 0) is_mounted = TRUE; @@ -735,8 +732,9 @@ handle_command_mount_unmount (gint *argc, goto out; } - filesystem = UDISKS_PEEK_FILESYSTEM (object_proxy); - if (filesystem == NULL) + mount_points = udisks_block_device_get_filesystem_mount_points (block); + if (g_strv_length ((gchar **) mount_points) == 0 && + g_strcmp0 (udisks_block_device_get_id_usage (block), "filesystem") != 0) { g_printerr ("Device %s is not a filesystem.\n", udisks_block_device_get_device (block)); g_object_unref (object_proxy); @@ -755,12 +753,12 @@ handle_command_mount_unmount (gint *argc, gchar *mount_path; error = NULL; - if (!udisks_filesystem_call_mount_sync (filesystem, - opt_mount_filesystem_type, - (const gchar *const *) opt_mount_unmount_options, - &mount_path, - NULL, /* GCancellable */ - &error)) + if (!udisks_block_device_call_filesystem_mount_sync (block, + opt_mount_filesystem_type, + (const gchar *const *) opt_mount_unmount_options, + &mount_path, + NULL, /* GCancellable */ + &error)) { if (error->domain == UDISKS_ERROR && error->code == UDISKS_ERROR_NOT_AUTHORIZED_CAN_OBTAIN && @@ -786,10 +784,10 @@ handle_command_mount_unmount (gint *argc, GError *error; error = NULL; - if (!udisks_filesystem_call_unmount_sync (filesystem, - (const gchar *const *) opt_mount_unmount_options, - NULL, /* GCancellable */ - &error)) + if (!udisks_block_device_call_filesystem_unmount_sync (block, + (const gchar *const *) opt_mount_unmount_options, + NULL, /* GCancellable */ + &error)) { if (error->domain == UDISKS_ERROR && error->code == UDISKS_ERROR_NOT_AUTHORIZED_CAN_OBTAIN && diff --git a/tools/umount-udisks.c b/tools/umount-udisks.c index 797210f..1c369f4 100644 --- a/tools/umount-udisks.c +++ b/tools/umount-udisks.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -89,14 +90,14 @@ main (int argc, char *argv[]) GError *error; struct stat statbuf; GDBusObjectProxy *object_proxy; - UDisksFilesystem *filesystem; + UDisksBlockDevice *block; const gchar *unmount_options[1] = {NULL}; ret = 1; manager = NULL; block_device_file = NULL; object_proxy = NULL; - filesystem = NULL; + block = NULL; g_type_init (); @@ -142,18 +143,13 @@ main (int argc, char *argv[]) goto out; } - filesystem = UDISKS_GET_FILESYSTEM (object_proxy); - if (filesystem == NULL) - { - g_printerr ("Object for block device file %s does not appear to be a file system\n", block_device_file); - goto out; - } + block = UDISKS_PEEK_BLOCK_DEVICE (object_proxy); error = NULL; - if (!udisks_filesystem_call_unmount_sync (filesystem, - unmount_options, - NULL, /* GCancellable */ - &error)) + if (!udisks_block_device_call_filesystem_unmount_sync (block, + unmount_options, + NULL, /* GCancellable */ + &error)) { g_printerr ("Error unmounting %s: %s\n", block_device_file, error->message); g_error_free (error); @@ -163,8 +159,6 @@ main (int argc, char *argv[]) ret = 0; out: - if (filesystem != NULL) - g_object_unref (filesystem); if (object_proxy != NULL) g_object_unref (object_proxy); g_free (block_device_file);