Add facilities for adding/removing a block device to /etc/fstab
authorDavid Zeuthen <davidz@redhat.com>
Tue, 9 Aug 2011 19:14:17 +0000 (15:14 -0400)
committerDavid Zeuthen <davidz@redhat.com>
Tue, 9 Aug 2011 19:14:17 +0000 (15:14 -0400)
http://people.freedesktop.org/~david/palimpsest-fstab-1.png
http://people.freedesktop.org/~david/palimpsest-fstab-2.png

Signed-off-by: David Zeuthen <davidz@redhat.com>
13 files changed:
data/org.freedesktop.UDisks2.xml
doc/udisks2-docs.xml
doc/udisks2-sections.txt
doc/udisks2.types
policy/org.freedesktop.udisks2.policy.in
src/Makefile.am
src/udisksdaemon.c
src/udisksdaemon.h
src/udisksdaemontypes.h
src/udiskslinuxblock.c
src/udiskslinuxfilesystem.c
src/udiskslinuxprovider.c
src/udisksprivate.h

index f019b4a..7048cee 100644 (file)
     -->
     <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>
 
   <!-- ********************************************************************** -->
index 896cbf7..8cd93c6 100644 (file)
     <xi:include href="xml/udiskslogging.xml"/>
     <xi:include href="xml/udisksmount.xml"/>
     <xi:include href="xml/udisksmountmonitor.xml"/>
+    <xi:include href="xml/udisksfstabentry.xml"/>
+    <xi:include href="xml/udisksfstabmonitor.xml"/>
     <xi:include href="xml/udiskspersistentstore.xml"/>
     <xi:include href="xml/udiskscleanup.xml"/>
     <xi:include href="xml/udisksprovider.xml"/>
index 39b2bb1..b29bcc1 100644 (file)
@@ -58,6 +58,7 @@ udisks_daemon_new
 udisks_daemon_get_connection
 udisks_daemon_get_object_manager
 udisks_daemon_get_mount_monitor
+udisks_daemon_get_fstab_monitor
 udisks_daemon_get_linux_provider
 udisks_daemon_get_persistent_store
 udisks_daemon_get_authority
@@ -760,6 +761,15 @@ UDisksBlockDevice
 UDisksBlockDeviceIface
 udisks_block_device_interface_info
 udisks_block_device_override_properties
+udisks_block_device_call_add_configuration_item
+udisks_block_device_call_add_configuration_item_finish
+udisks_block_device_call_add_configuration_item_sync
+udisks_block_device_complete_add_configuration_item
+udisks_block_device_call_remove_configuration_item
+udisks_block_device_call_remove_configuration_item_finish
+udisks_block_device_call_remove_configuration_item_sync
+udisks_block_device_complete_remove_configuration_item
+udisks_block_device_get_configuration
 udisks_block_device_get_crypto_backing_device
 udisks_block_device_get_device
 udisks_block_device_get_drive
@@ -790,6 +800,7 @@ udisks_block_device_get_hint_ignore
 udisks_block_device_get_hint_auto
 udisks_block_device_get_hint_name
 udisks_block_device_get_hint_icon_name
+udisks_block_device_set_configuration
 udisks_block_device_set_crypto_backing_device
 udisks_block_device_set_device
 udisks_block_device_set_drive
@@ -974,3 +985,36 @@ udisks_loop_get_type
 udisks_loop_proxy_get_type
 udisks_loop_skeleton_get_type
 </SECTION>
+
+<SECTION>
+<FILE>udisksfstabentry</FILE>
+<TITLE>UDisksFstabEntry</TITLE>
+UDisksFstabEntry
+udisks_fstab_entry_get_fsname
+udisks_fstab_entry_get_dir
+udisks_fstab_entry_get_fstype
+udisks_fstab_entry_get_opts
+udisks_fstab_entry_get_freq
+udisks_fstab_entry_get_passno
+udisks_fstab_entry_compare
+<SUBSECTION Standard>
+UDISKS_TYPE_FSTAB_ENTRY
+UDISKS_FSTAB_ENTRY
+UDISKS_IS_FSTAB_ENTRY
+<SUBSECTION Private>
+udisks_fstab_entry_get_type
+</SECTION>
+
+<SECTION>
+<FILE>udisksfstabmonitor</FILE>
+<TITLE>UDisksFstabMonitor</TITLE>
+UDisksFstabMonitor
+udisks_fstab_monitor_new
+udisks_fstab_monitor_get_entries
+<SUBSECTION Standard>
+UDISKS_TYPE_FSTAB_MONITOR
+UDISKS_FSTAB_MONITOR
+UDISKS_IS_FSTAB_MONITOR
+<SUBSECTION Private>
+udisks_fstab_monitor_get_type
+</SECTION>
index 7a99f3d..79031a2 100644 (file)
@@ -18,6 +18,8 @@ udisks_linux_swapspace_get_type
 udisks_linux_loop_get_type
 udisks_linux_manager_get_type
 udisks_cleanup_get_type
+udisks_fstab_entry_get_type
+udisks_fstab_monitor_get_type
 
 udisks_drive_get_type
 udisks_drive_proxy_get_type
index 65e8858..9d76a85 100644 (file)
     </defaults>
   </action>
 
+  <!-- Manage system-wide configuration files such as /etc/fstab -->
+  <action id="org.freedesktop.udisks2.modify-system-configuration">
+    <_description>Modify system-wide configuration</_description>
+    <_message>Authentication is required to modify system-wide configuration</_message>
+    <defaults>
+      <allow_any>auth_admin</allow_any>
+      <allow_inactive>auth_admin</allow_inactive>
+      <allow_active>auth_admin_keep</allow_active>
+    </defaults>
+  </action>
+
 </policyconfig>
index 63f0d8b..b23e7de 100644 (file)
@@ -65,6 +65,8 @@ libudisks_daemon_la_SOURCES =                                         \
        udiskslogging.h                 udiskslogging.c                 \
        udiskscleanup.h                 udiskscleanup.c                 \
        udisksprivate.h                                                 \
+       udisksfstabentry.h              udisksfstabentry.c              \
+       udisksfstabmonitor.h            udisksfstabmonitor.c            \
        $(BUILT_SOURCES)                                                \
        $(NULL)
 
index 2b048e0..3b72b26 100644 (file)
@@ -33,6 +33,8 @@
 #include "udisksthreadedjob.h"
 #include "udiskssimplejob.h"
 #include "udiskscleanup.h"
+#include "udisksfstabmonitor.h"
+#include "udisksfstabentry.h"
 
 /**
  * SECTION:udisksdaemon
@@ -65,6 +67,8 @@ struct _UDisksDaemon
   PolkitAuthority *authority;
 
   UDisksCleanup *cleanup;
+
+  UDisksFstabMonitor *fstab_monitor;
 };
 
 struct _UDisksDaemonClass
@@ -77,7 +81,8 @@ enum
   PROP_0,
   PROP_CONNECTION,
   PROP_OBJECT_MANAGER,
-  PROP_MOUNT_MONITOR
+  PROP_MOUNT_MONITOR,
+  PROP_FSTAB_MONITOR,
 };
 
 G_DEFINE_TYPE (UDisksDaemon, udisks_daemon, G_TYPE_OBJECT);
@@ -96,6 +101,7 @@ udisks_daemon_finalize (GObject *object)
   g_object_unref (daemon->linux_provider);
   g_object_unref (daemon->mount_monitor);
   g_object_unref (daemon->connection);
+  g_object_unref (daemon->fstab_monitor);
 
   if (G_OBJECT_CLASS (udisks_daemon_parent_class)->finalize != NULL)
     G_OBJECT_CLASS (udisks_daemon_parent_class)->finalize (object);
@@ -123,6 +129,10 @@ udisks_daemon_get_property (GObject    *object,
       g_value_set_object (value, udisks_daemon_get_mount_monitor (daemon));
       break;
 
+    case PROP_FSTAB_MONITOR:
+      g_value_set_object (value, udisks_daemon_get_fstab_monitor (daemon));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -207,6 +217,8 @@ udisks_daemon_constructed (GObject *object)
                     G_CALLBACK (mount_monitor_on_mount_removed),
                     daemon);
 
+  daemon->fstab_monitor = udisks_fstab_monitor_new ();
+
   /* now add providers */
   daemon->linux_provider = udisks_linux_provider_new (daemon);
 
@@ -344,6 +356,21 @@ udisks_daemon_get_mount_monitor (UDisksDaemon *daemon)
 }
 
 /**
+ * udisks_daemon_get_fstab_monitor:
+ * @daemon: A #UDisksDaemon
+ *
+ * Gets the fstab monitor used by @daemon.
+ *
+ * Returns: A #UDisksFstabMonitor. Do not free, the object is owned by @daemon.
+ */
+UDisksFstabMonitor *
+udisks_daemon_get_fstab_monitor (UDisksDaemon *daemon)
+{
+  g_return_val_if_fail (UDISKS_IS_DAEMON (daemon), NULL);
+  return daemon->fstab_monitor;
+}
+
+/**
  * udisks_daemon_get_linux_provider:
  * @daemon: A #UDisksDaemon.
  *
index 305b79a..684e673 100644 (file)
@@ -34,6 +34,7 @@ UDisksDaemon             *udisks_daemon_new                   (GDBusConnection *
 GDBusConnection          *udisks_daemon_get_connection        (UDisksDaemon    *daemon);
 GDBusObjectManagerServer *udisks_daemon_get_object_manager    (UDisksDaemon    *daemon);
 UDisksMountMonitor       *udisks_daemon_get_mount_monitor     (UDisksDaemon    *daemon);
+UDisksFstabMonitor       *udisks_daemon_get_fstab_monitor     (UDisksDaemon    *daemon);
 UDisksLinuxProvider      *udisks_daemon_get_linux_provider    (UDisksDaemon    *daemon);
 UDisksPersistentStore    *udisks_daemon_get_persistent_store  (UDisksDaemon    *daemon);
 PolkitAuthority          *udisks_daemon_get_authority         (UDisksDaemon    *daemon);
index 526e078..fe4b1ba 100644 (file)
@@ -76,6 +76,12 @@ typedef struct _UDisksLinuxManager UDisksLinuxManager;
 struct _UDisksLinuxSwapspace;
 typedef struct _UDisksLinuxSwapspace UDisksLinuxSwapspace;
 
+struct _UDisksFstabMonitor;
+typedef struct _UDisksFstabMonitor UDisksFstabMonitor;
+
+struct _UDisksFstabEntry;
+typedef struct _UDisksFstabEntry UDisksFstabEntry;
+
 /**
  * UDisksThreadedJobFunc:
  * @job: A #UDisksThreadedJob.
index 845ead4..027c940 100644 (file)
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 #include <pwd.h>
 #include <grp.h>
+#include <mntent.h>
 
 #include <string.h>
 #include <stdlib.h>
@@ -42,6 +43,8 @@
 #include "udiskslinuxloop.h"
 #include "udiskspersistentstore.h"
 #include "udiskslinuxprovider.h"
+#include "udisksfstabmonitor.h"
+#include "udisksfstabentry.h"
 
 /**
  * SECTION:udiskslinuxblock
@@ -368,6 +371,322 @@ update_iface (UDisksLinuxBlock           *block,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+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 */
@@ -381,6 +700,14 @@ block_device_check (UDisksLinuxBlock *block)
 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 *
@@ -556,6 +883,109 @@ block_device_update_hints (UDisksLinuxBlock  *block,
   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,
@@ -778,6 +1208,7 @@ block_device_update (UDisksLinuxBlock *block,
     }
 
   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);
index ef67db5..728feab 100644 (file)
@@ -829,6 +829,19 @@ handle_mount (UDisksFilesystem       *filesystem,
    */
   if (system_managed)
     {
+      if (!g_file_test (mount_point_to_use, G_FILE_TEST_IS_DIR))
+        {
+          if (g_mkdir_with_parents (mount_point_to_use, 0755) != 0)
+            {
+              g_dbus_method_invocation_return_error (invocation,
+                                                     UDISKS_ERROR,
+                                                     UDISKS_ERROR_FAILED,
+                                                     "Error creating directory `%s' to be used for mounting %s: %m",
+                                                     mount_point_to_use,
+                                                     udisks_block_device_get_device (block));
+              goto out;
+            }
+        }
       escaped_mount_point_to_use   = g_strescape (mount_point_to_use, NULL);
       if (!udisks_daemon_launch_spawned_job_sync (daemon,
                                                   NULL,  /* GCancellable */
index 8f29311..3a4f5be 100644 (file)
@@ -85,12 +85,23 @@ static void udisks_linux_provider_handle_uevent (UDisksLinuxProvider *provider,
 
 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);
@@ -103,6 +114,13 @@ udisks_linux_provider_finalize (GObject *object)
   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);
 }
@@ -182,6 +200,16 @@ udisks_linux_provider_start (UDisksProvider *_provider)
   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);
 }
 
 
@@ -517,3 +545,44 @@ on_housekeeping_timeout (gpointer user_data)
 
   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);
+}
index 114fbc7..3cc2bf8 100644 (file)
@@ -29,6 +29,8 @@ UDisksMount *_udisks_mount_new (dev_t dev,
                                 const gchar *mount_path,
                                 UDisksMountType type);
 
+UDisksFstabEntry *_udisks_fstab_entry_new (const struct mntent *mntent);
+
 G_END_DECLS
 
 #endif /* __UDISKS_PRIVATE_H__ */