-->
<property name="IdUUID" type="ay" access="read"/>
+ <!-- Configuration:
+ The configuration sources for the device.
+
+ This is an array of pairs of (@type, @details) where @type is
+ a string identifying the configuration source
+ (e.g. <literal>fstab</literal>) and @details contains the
+ actual configuration data.
+
+ For entries of type <literal>fstab</literal>, it means that
+ the block device is referenced in the system-wide
+ <filename>/etc/fstab</filename> file. Known configuration
+ items for type <literal>fstab</literal> are
+ <variablelist>
+ <varlistentry>
+ <term>fsname (type <literal>'ay'</literal>)</term>
+ <listitem><para>The special device</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>dir (type <literal>'ay'</literal>)</term>
+ <listitem><para>The mount point</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>type (type <literal>'ay'</literal>)</term>
+ <listitem><para>The filesystem type</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>opts (type <literal>'ay'</literal>)</term>
+ <listitem><para>Options</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>freq (type <literal>'i'</literal>)</term>
+ <listitem><para>Dump frequency in days</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>passno (type <literal>'i'</literal>)</term>
+ <listitem><para>Pass number of parallel fsck</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ Use the
+ org.freedesktop.UDisks2.BlockDevice.AddConfigurationItem()
+ and
+ org.freedesktop.UDisks2.BlockDevice.RemoveConfigurationItem()
+ methods to add/remove configuration items.
+ -->
+ <property name="Configuration" type="a(sa{sv})" access="read"/>
+
<!-- CryptoBackingDevice:
The #org.freedesktop.UDisks2.BlockDevice object that is
backing the device or <literal>/</literal> if unknown or if
<literal>/</literal> if the device is not a partition.
-->
<property name="PartEntryTable" type="o" access="read"/>
+
+ <!--
+ AddConfigurationItem:
+ @item: The configuration item to add.
+ @options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>).
+
+ Adds a new configuration item. See the
+ #org.freedesktop.UDisks2.BlockDevice:Configuration property
+ for details about valid configuration items.
+ -->
+ <method name="AddConfigurationItem">
+ <arg name="item" direction="in" type="(sa{sv})"/>
+ <arg name="options" direction="in" type="a{sv}"/>
+ </method>
+
+ <!--
+ RemoveConfigurationItem:
+ @item: The configuration item to remove.
+ @options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>).
+
+ Removes a new configuration item. See the
+ #org.freedesktop.UDisks2.BlockDevice:Configuration property
+ for details about valid configuration items.
+ -->
+ <method name="RemoveConfigurationItem">
+ <arg name="item" direction="in" type="(sa{sv})"/>
+ <arg name="options" direction="in" type="a{sv}"/>
+ </method>
+
</interface>
<!-- ********************************************************************** -->
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
+#include <mntent.h>
#include <string.h>
#include <stdlib.h>
#include "udiskslinuxloop.h"
#include "udiskspersistentstore.h"
#include "udiskslinuxprovider.h"
+#include "udisksfstabmonitor.h"
+#include "udisksfstabentry.h"
/**
* SECTION:udiskslinuxblock
/* ---------------------------------------------------------------------------------------------------- */
+static gchar *
+escape_fstab (const gchar *source)
+{
+ GString *s;
+ guint n;
+ s = g_string_new (NULL);
+ for (n = 0; source[n] != '\0'; n++)
+ {
+ switch (source[n])
+ {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\\':
+ g_string_append_printf (s, "\\%03o", source[n]);
+ break;
+
+ default:
+ g_string_append_c (s, source[n]);
+ break;
+ }
+ }
+ return g_string_free (s, FALSE);
+}
+
+/* based on g_strcompress() */
+static gchar *
+unescape_fstab (const gchar *source)
+{
+ const gchar *p = source, *octal;
+ gchar *dest = g_malloc (strlen (source) + 1);
+ gchar *q = dest;
+
+ while (*p)
+ {
+ if (*p == '\\')
+ {
+ p++;
+ switch (*p)
+ {
+ case '\0':
+ g_warning ("unescape_fstab: trailing \\");
+ goto out;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ *q = 0;
+ octal = p;
+ while ((p < octal + 3) && (*p >= '0') && (*p <= '7'))
+ {
+ *q = (*q * 8) + (*p - '0');
+ p++;
+ }
+ q++;
+ p--;
+ break;
+ default: /* Also handles \" and \\ */
+ *q++ = *p;
+ break;
+ }
+ }
+ else
+ *q++ = *p;
+ p++;
+ }
+out:
+ *q = 0;
+
+ return dest;
+}
+
+static gboolean
+add_remove_fstab_entry (GVariant *add,
+ GVariant *remove,
+ GError **error)
+{
+ struct mntent mntent_remove;
+ struct mntent mntent_add;
+ gboolean ret;
+ gchar *contents;
+ gchar **lines;
+ GString *str;
+ gboolean removed;
+ guint n;
+
+ contents = NULL;
+ lines = NULL;
+ str = NULL;
+ ret = FALSE;
+
+ if (remove != NULL)
+ {
+ if (!g_variant_lookup (remove, "fsname", "^&ay", &mntent_remove.mnt_fsname) ||
+ !g_variant_lookup (remove, "dir", "^&ay", &mntent_remove.mnt_dir) ||
+ !g_variant_lookup (remove, "type", "^&ay", &mntent_remove.mnt_type) ||
+ !g_variant_lookup (remove, "opts", "^&ay", &mntent_remove.mnt_opts) ||
+ !g_variant_lookup (remove, "freq", "i", &mntent_remove.mnt_freq) ||
+ !g_variant_lookup (remove, "passno", "i", &mntent_remove.mnt_passno))
+ {
+ g_set_error (error,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "Missing fsname, dir, type, opts, freq or passno parameter in entry to remove");
+ goto out;
+ }
+ }
+
+ if (add != NULL)
+ {
+ if (!g_variant_lookup (add, "fsname", "^&ay", &mntent_add.mnt_fsname) ||
+ !g_variant_lookup (add, "dir", "^&ay", &mntent_add.mnt_dir) ||
+ !g_variant_lookup (add, "type", "^&ay", &mntent_add.mnt_type) ||
+ !g_variant_lookup (add, "opts", "^&ay", &mntent_add.mnt_opts) ||
+ !g_variant_lookup (add, "freq", "i", &mntent_add.mnt_freq) ||
+ !g_variant_lookup (add, "passno", "i", &mntent_add.mnt_passno))
+ {
+ g_set_error (error,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "Missing fsname, dir, type, opts, freq or passno parameter in entry to add");
+ goto out;
+ }
+ }
+
+ if (!g_file_get_contents ("/etc/fstab",
+ &contents,
+ NULL,
+ error))
+ goto out;
+
+ lines = g_strsplit (contents, "\n", 0);
+
+ str = g_string_new (NULL);
+ removed = FALSE;
+ for (n = 0; lines != NULL && lines[n] != NULL; n++)
+ {
+ const gchar *line = lines[n];
+ if (strlen (line) == 0 && lines[n+1] == NULL)
+ break;
+ if (remove != NULL && !removed)
+ {
+ gchar parsed_fsname[512];
+ gchar parsed_dir[512];
+ gchar parsed_type[512];
+ gchar parsed_opts[512];
+ gint parsed_freq;
+ gint parsed_passno;
+ if (sscanf (line, "%511s %511s %511s %511s %d %d",
+ parsed_fsname,
+ parsed_dir,
+ parsed_type,
+ parsed_opts,
+ &parsed_freq,
+ &parsed_passno) == 6)
+ {
+ gchar *unescaped_fsname = unescape_fstab (parsed_fsname);
+ gchar *unescaped_dir = unescape_fstab (parsed_dir);
+ gchar *unescaped_type = unescape_fstab (parsed_type);
+ gchar *unescaped_opts = unescape_fstab (parsed_opts);
+ gboolean matches = FALSE;
+ if (g_strcmp0 (unescaped_fsname, mntent_remove.mnt_fsname) == 0 &&
+ g_strcmp0 (unescaped_dir, mntent_remove.mnt_dir) == 0 &&
+ g_strcmp0 (unescaped_type, mntent_remove.mnt_type) == 0 &&
+ g_strcmp0 (unescaped_opts, mntent_remove.mnt_opts) == 0 &&
+ parsed_freq == mntent_remove.mnt_freq &&
+ parsed_passno == mntent_remove.mnt_passno)
+ {
+ matches = TRUE;
+ }
+ g_free (unescaped_fsname);
+ g_free (unescaped_dir);
+ g_free (unescaped_type);
+ g_free (unescaped_opts);
+ if (matches)
+ {
+ removed = TRUE;
+ continue;
+ }
+ }
+ }
+ g_string_append (str, line);
+ g_string_append_c (str, '\n');
+ }
+
+ if (remove != NULL && !removed)
+ {
+ g_set_error (error,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "Didn't find entry to remove");
+ goto out;
+ }
+
+ if (add != NULL)
+ {
+ gchar *escaped_fsname = escape_fstab (mntent_add.mnt_fsname);
+ gchar *escaped_dir = escape_fstab (mntent_add.mnt_dir);
+ gchar *escaped_type = escape_fstab (mntent_add.mnt_type);
+ gchar *escaped_opts = escape_fstab (mntent_add.mnt_opts);
+ g_string_append_printf (str, "%s %s %s %s %d %d\n",
+ escaped_fsname,
+ escaped_dir,
+ escaped_type,
+ escaped_opts,
+ mntent_add.mnt_freq,
+ mntent_add.mnt_passno);
+ g_free (escaped_fsname);
+ g_free (escaped_dir);
+ g_free (escaped_type);
+ g_free (escaped_opts);
+ }
+
+ if (!g_file_set_contents ("/etc/fstab",
+ str->str,
+ -1,
+ error) != 0)
+ goto out;
+
+ ret = TRUE;
+
+ out:
+ g_strfreev (lines);
+ g_free (contents);
+ if (str != NULL)
+ g_string_free (str, TRUE);
+ return ret;
+}
+
+static gboolean
+on_add_configuration_item (UDisksBlockDevice *block,
+ GDBusMethodInvocation *invocation,
+ GVariant *item,
+ GVariant *options,
+ gpointer user_data)
+{
+ UDisksLinuxBlock *object = UDISKS_LINUX_BLOCK (user_data);
+ const gchar *type;
+ GVariant *details;
+ GError *error;
+
+ g_variant_get (item, "(&s@a{sv})", &type, &details);
+
+ if (g_strcmp0 (type, "fstab") != 0)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "Only fstab items can be added");
+ goto out;
+ }
+
+ if (!udisks_daemon_util_check_authorization_sync (object->daemon,
+ NULL,
+ "org.freedesktop.udisks2.modify-system-configuration",
+ options,
+ N_("Authentication is required to modify the /etc/fstab file"),
+ invocation))
+ goto out;
+
+ error = NULL;
+ if (!add_remove_fstab_entry (details, NULL, &error))
+ {
+ g_dbus_method_invocation_take_error (invocation, error);
+ goto out;
+ }
+
+ udisks_block_device_complete_add_configuration_item (block, invocation);
+
+ out:
+ g_variant_unref (details);
+ return TRUE; /* returning TRUE means that we handled the method invocation */
+}
+
+static gboolean
+on_remove_configuration_item (UDisksBlockDevice *block,
+ GDBusMethodInvocation *invocation,
+ GVariant *item,
+ GVariant *options,
+ gpointer user_data)
+{
+ UDisksLinuxBlock *object = UDISKS_LINUX_BLOCK (user_data);
+ const gchar *type;
+ GVariant *details;
+ GError *error;
+
+ g_variant_get (item, "(&s@a{sv})", &type, &details);
+
+ if (g_strcmp0 (type, "fstab") != 0)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "Only fstab items can be removed");
+ goto out;
+ }
+
+ if (!udisks_daemon_util_check_authorization_sync (object->daemon,
+ NULL,
+ "org.freedesktop.udisks2.modify-system-configuration",
+ options,
+ N_("Authentication is required to modify the /etc/fstab file"),
+ invocation))
+ goto out;
+
+ error = NULL;
+ if (!add_remove_fstab_entry (NULL, details, &error))
+ {
+ g_dbus_method_invocation_take_error (invocation, error);
+ goto out;
+ }
+
+ udisks_block_device_complete_add_configuration_item (block, invocation);
+
+ out:
+ g_variant_unref (details);
+ return TRUE; /* returning TRUE means that we handled the method invocation */
+}
/* ---------------------------------------------------------------------------------------------------- */
/* org.freedesktop.UDisks.BlockDevice */
static void
block_device_connect (UDisksLinuxBlock *block)
{
+ g_signal_connect (block->iface_block_device,
+ "handle-add-configuration-item",
+ G_CALLBACK (on_add_configuration_item),
+ block);
+ g_signal_connect (block->iface_block_device,
+ "handle-remove-configuration-item",
+ G_CALLBACK (on_remove_configuration_item),
+ block);
}
static gchar *
udisks_block_device_set_hint_icon_name (iface, hint_icon_name);
}
+static GList *
+find_fstab_entries_for_device (UDisksLinuxBlock *block)
+{
+ GList *entries;
+ GList *l;
+ GList *ret;
+
+ ret = NULL;
+
+ /* if this is too slow, we could add lookup methods to UDisksFstabMonitor... */
+ entries = udisks_fstab_monitor_get_entries (udisks_daemon_get_fstab_monitor (block->daemon));
+ for (l = entries; l != NULL; l = l->next)
+ {
+ UDisksFstabEntry *entry = UDISKS_FSTAB_ENTRY (l->data);
+ const gchar *const *symlinks;
+ const gchar *fsname;
+ gchar *device;
+ guint n;
+
+ fsname = udisks_fstab_entry_get_fsname (entry);
+ device = NULL;
+ if (g_str_has_prefix (fsname, "UUID="))
+ {
+ device = g_strdup_printf ("/dev/disk/by-uuid/%s", fsname + 5);
+ }
+ else if (g_str_has_prefix (fsname, "LABEL="))
+ {
+ device = g_strdup_printf ("/dev/disk/by-label/%s", fsname + 6);
+ }
+ else if (g_str_has_prefix (fsname, "/dev"))
+ {
+ device = g_strdup (fsname);
+ }
+ else
+ {
+ /* ignore non-device entries */
+ goto continue_loop;
+ }
+
+ symlinks = udisks_block_device_get_symlinks (block->iface_block_device);
+ if (symlinks != NULL)
+ {
+ for (n = 0; symlinks[n] != NULL; n++)
+ {
+ if (g_strcmp0 (device, symlinks[n]) == 0)
+ {
+ ret = g_list_prepend (ret, g_object_ref (entry));
+ }
+ }
+ }
+
+ continue_loop:
+ g_free (device);
+ }
+
+ g_list_foreach (entries, (GFunc) g_object_unref, NULL);
+ g_list_free (entries);
+ return ret;
+}
+
+static void
+block_device_update_configuration (UDisksLinuxBlock *block,
+ const gchar *uevent_action,
+ UDisksBlockDevice *iface,
+ const gchar *device_file,
+ UDisksDrive *drive)
+{
+ GList *entries;
+ GList *l;
+ GVariantBuilder builder;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sa{sv})"));
+ entries = find_fstab_entries_for_device (block);
+ for (l = entries; l != NULL; l = l->next)
+ {
+ UDisksFstabEntry *entry = UDISKS_FSTAB_ENTRY (l->data);
+ GVariantBuilder dict_builder;
+
+ g_variant_builder_init (&dict_builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add (&dict_builder, "{sv}", "fsname",
+ g_variant_new_bytestring (udisks_fstab_entry_get_fsname (entry)));
+ g_variant_builder_add (&dict_builder, "{sv}", "dir",
+ g_variant_new_bytestring (udisks_fstab_entry_get_dir (entry)));
+ g_variant_builder_add (&dict_builder, "{sv}", "type",
+ g_variant_new_bytestring (udisks_fstab_entry_get_fstype (entry)));
+ g_variant_builder_add (&dict_builder, "{sv}", "opts",
+ g_variant_new_bytestring (udisks_fstab_entry_get_opts (entry)));
+ g_variant_builder_add (&dict_builder, "{sv}", "freq",
+ g_variant_new_int32 (udisks_fstab_entry_get_freq (entry)));
+ g_variant_builder_add (&dict_builder, "{sv}", "passno",
+ g_variant_new_int32 (udisks_fstab_entry_get_passno (entry)));
+ g_variant_builder_add (&builder,
+ "(sa{sv})",
+ "fstab", &dict_builder);
+ }
+
+ udisks_block_device_set_configuration (block->iface_block_device,
+ g_variant_builder_end (&builder));
+
+ g_list_foreach (entries, (GFunc) g_object_unref, NULL);
+ g_list_free (entries);
+}
+
static void
block_device_update (UDisksLinuxBlock *block,
const gchar *uevent_action,
}
block_device_update_hints (block, uevent_action, iface, device_file, drive);
+ block_device_update_configuration (block, uevent_action, iface, device_file, drive);
if (drive != NULL)
g_object_unref (drive);
static gboolean on_housekeeping_timeout (gpointer user_data);
+static void fstab_monitor_on_entry_added (UDisksFstabMonitor *monitor,
+ UDisksFstabEntry *entry,
+ gpointer user_data);
+
+static void fstab_monitor_on_entry_removed (UDisksFstabMonitor *monitor,
+ UDisksFstabEntry *entry,
+ gpointer user_data);
+
G_DEFINE_TYPE (UDisksLinuxProvider, udisks_linux_provider, UDISKS_TYPE_PROVIDER);
static void
udisks_linux_provider_finalize (GObject *object)
{
UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (object);
+ UDisksDaemon *daemon;
+
+ daemon = udisks_provider_get_daemon (UDISKS_PROVIDER (provider));
g_hash_table_unref (provider->sysfs_to_block);
g_hash_table_unref (provider->vpd_to_drive);
if (provider->housekeeping_timeout > 0)
g_source_remove (provider->housekeeping_timeout);
+ g_signal_handlers_disconnect_by_func (udisks_daemon_get_fstab_monitor (daemon),
+ G_CALLBACK (fstab_monitor_on_entry_added),
+ provider);
+ g_signal_handlers_disconnect_by_func (udisks_daemon_get_fstab_monitor (daemon),
+ G_CALLBACK (fstab_monitor_on_entry_removed),
+ provider);
+
if (G_OBJECT_CLASS (udisks_linux_provider_parent_class)->finalize != NULL)
G_OBJECT_CLASS (udisks_linux_provider_parent_class)->finalize (object);
}
on_housekeeping_timeout (provider);
provider->coldplug = FALSE;
+
+ /* update BlockDevice:FstabEntries whenever fstab entries are added or removed */
+ g_signal_connect (udisks_daemon_get_fstab_monitor (daemon),
+ "entry-added",
+ G_CALLBACK (fstab_monitor_on_entry_added),
+ provider);
+ g_signal_connect (udisks_daemon_get_fstab_monitor (daemon),
+ "entry-removed",
+ G_CALLBACK (fstab_monitor_on_entry_removed),
+ provider);
}
return TRUE; /* keep timeout around */
}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_all_block_devices (UDisksLinuxProvider *provider)
+{
+ GList *block_devices;
+ GList *l;
+
+ G_LOCK (provider_lock);
+ block_devices = g_hash_table_get_values (provider->sysfs_to_block);
+ g_list_foreach (block_devices, (GFunc) g_object_ref, NULL);
+ G_UNLOCK (provider_lock);
+
+ for (l = block_devices; l != NULL; l = l->next)
+ {
+ UDisksLinuxBlock *block = UDISKS_LINUX_BLOCK (l->data);
+ udisks_linux_block_uevent (block, "change", NULL);
+ }
+
+ g_list_foreach (block_devices, (GFunc) g_object_unref, NULL);
+ g_list_free (block_devices);
+}
+
+static void
+fstab_monitor_on_entry_added (UDisksFstabMonitor *monitor,
+ UDisksFstabEntry *entry,
+ gpointer user_data)
+{
+ UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data);
+ update_all_block_devices (provider);
+}
+
+static void
+fstab_monitor_on_entry_removed (UDisksFstabMonitor *monitor,
+ UDisksFstabEntry *entry,
+ gpointer user_data)
+{
+ UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data);
+ update_all_block_devices (provider);
+}