Bug 41862 — Add label changing for previously supported file systems
authorMartin Pitt <martin.pitt@ubuntu.com>
Mon, 17 Oct 2011 09:53:39 +0000 (11:53 +0200)
committerMartin Pitt <martin.pitt@ubuntu.com>
Mon, 24 Oct 2011 14:37:00 +0000 (16:37 +0200)
Add support for label changing on vfat, ntfs, xfs, reiserfs, and nilfs2 file
systems.

Instead of having another huge if-then-else ladder as in udisks1, use a lookup
table _fs_info. For now this contains the commands for label changing and
erasing, but will be extended in the future to contain the commands for
creating and checking file systems.

https://bugs.freedesktop.org/show_bug.cgi?id=41862

src/Makefile.am
src/udiskslinuxfilesystem.c
src/udiskslinuxfsinfo.c [new file with mode: 0644]
src/udiskslinuxfsinfo.h [new file with mode: 0644]

index 2b57111..b47c4a3 100644 (file)
@@ -59,6 +59,7 @@ libudisks_daemon_la_SOURCES =                                         \
        udiskslinuxdrive.h              udiskslinuxdrive.c              \
        udiskslinuxdriveata.h           udiskslinuxdriveata.c           \
        udiskslinuxmanager.h            udiskslinuxmanager.c            \
+       udiskslinuxfsinfo.h             udiskslinuxfsinfo.c             \
        udisksbasejob.h                 udisksbasejob.c                 \
        udisksspawnedjob.h              udisksspawnedjob.c              \
        udisksthreadedjob.h             udisksthreadedjob.c             \
index 833c469..d2e54e9 100644 (file)
@@ -34,6 +34,7 @@
 #include "udiskslogging.h"
 #include "udiskslinuxfilesystem.h"
 #include "udiskslinuxblockobject.h"
+#include "udiskslinuxfsinfo.h"
 #include "udisksdaemon.h"
 #include "udiskscleanup.h"
 #include "udisksdaemonutil.h"
@@ -445,6 +446,20 @@ prepend_default_mount_options (const FSMountOptions *fsmo,
   return (char **) g_ptr_array_free (options, FALSE);
 }
 
+static gchar *
+subst_str (const gchar *str,
+           const gchar *from,
+           const gchar *to)
+{
+    gchar **parts;
+    gchar *result;
+
+    parts = g_strsplit (str, from, 0);
+    result = g_strjoinv (to, parts);
+    g_strfreev (parts);
+    return result;
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 /*
@@ -1437,15 +1452,17 @@ handle_set_label (UDisksFilesystem       *filesystem,
   UDisksDaemon *daemon;
   const gchar *probed_fs_usage;
   const gchar *probed_fs_type;
-  gboolean supports_online;
+  const FSInfo *fs_info;
   UDisksBaseJob *job;
   gchar *escaped_label;
   const gchar *action_id;
+  gchar *command;
+  gchar *tmp;
 
   object = NULL;
   daemon = NULL;
-  supports_online = FALSE;
   escaped_label = NULL;
+  command = NULL;
 
   object = UDISKS_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (filesystem)));
   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
@@ -1454,11 +1471,19 @@ handle_set_label (UDisksFilesystem       *filesystem,
   probed_fs_usage = udisks_block_get_id_usage (block);
   probed_fs_type = udisks_block_get_id_type (block);
 
-  /* TODO: add support for other fstypes */
-  if (!(g_strcmp0 (probed_fs_usage, "filesystem") == 0 &&
-        (g_strcmp0 (probed_fs_type, "ext2") == 0 ||
-         g_strcmp0 (probed_fs_type, "ext3") == 0 ||
-         g_strcmp0 (probed_fs_type, "ext4") == 0)))
+  if (g_strcmp0 (probed_fs_usage, "filesystem") != 0)
+    {
+      g_dbus_method_invocation_return_error (invocation,
+                                             UDISKS_ERROR,
+                                             UDISKS_ERROR_NOT_SUPPORTED,
+                                             "Cannot change label on device of type %s",
+                                             probed_fs_usage);
+      goto out;
+    }
+
+  fs_info = get_fs_info (probed_fs_type);
+
+  if (fs_info == NULL || fs_info->command_change_label == NULL)
     {
       g_dbus_method_invocation_return_error (invocation,
                                              UDISKS_ERROR,
@@ -1468,12 +1493,29 @@ handle_set_label (UDisksFilesystem       *filesystem,
                                              probed_fs_type);
       goto out;
     }
-  supports_online = TRUE;
+
+  /* VFAT does not allow some characters; as mlabel hangs with interactive
+   * question in this case, check in advance */
+  if (g_strcmp0 (probed_fs_type, "vfat") == 0)
+    {
+      for (tmp = "\"*/:<>?\\|"; *tmp; ++tmp)
+        {
+          if (strchr (label, *tmp) != NULL)
+            {
+              g_dbus_method_invocation_return_error (invocation,
+                                                     UDISKS_ERROR,
+                                                     UDISKS_ERROR_NOT_SUPPORTED,
+                                                     "character '%c' not supported in VFAT labels",
+                                                     *tmp);
+               goto out;
+            }
+        }
+    }
 
   /* Fail if the device is already mounted and the tools/drivers doesn't
    * support changing the label in that case
    */
-  if (filesystem != NULL && !supports_online)
+  if (filesystem != NULL && !fs_info->supports_online_label_rename)
     {
       const gchar * const *existing_mount_points;
       existing_mount_points = udisks_filesystem_get_mount_points (filesystem);
@@ -1505,14 +1547,24 @@ handle_set_label (UDisksFilesystem       *filesystem,
     goto out;
 
   escaped_label = g_shell_quote (label);
+
+  if (fs_info->command_clear_label != NULL && strlen (label) == 0)
+    {
+      command = subst_str (fs_info->command_clear_label, "$DEVICE", udisks_block_get_device (block));
+    }
+  else
+    {
+      tmp = subst_str (fs_info->command_change_label, "$DEVICE", udisks_block_get_device (block));
+      command = subst_str (tmp, "$LABEL", escaped_label);
+      g_free (tmp);
+    }
+
   job = udisks_daemon_launch_spawned_job (daemon,
                                           NULL, /* cancellable */
                                           0,    /* uid_t run_as_uid */
                                           0,    /* uid_t run_as_euid */
                                           NULL, /* input_string */
-                                          "e2label %s %s",
-                                          udisks_block_get_device (block),
-                                          escaped_label);
+                                          "%s", command);
   g_signal_connect (job,
                     "completed",
                     G_CALLBACK (on_set_label_job_completed),
@@ -1520,6 +1572,7 @@ handle_set_label (UDisksFilesystem       *filesystem,
 
  out:
   g_free (escaped_label);
+  g_free (command);
   return TRUE; /* returning TRUE means that we handled the method invocation */
 }
 
diff --git a/src/udiskslinuxfsinfo.c b/src/udiskslinuxfsinfo.c
new file mode 100644 (file)
index 0000000..8a485dc
--- /dev/null
@@ -0,0 +1,119 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Martin Pitt <martin.pitt@ubuntu.com>
+ *
+ * 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 <string.h>
+#include <glib.h>
+
+#include "config.h"
+#include "udiskslinuxfsinfo.h"
+
+const FSInfo _fs_info[] =
+  {
+    { 
+      "ext2", 
+      "e2label $DEVICE $LABEL", 
+      NULL,
+      TRUE,
+    },
+    { 
+      "ext3", 
+      "e2label $DEVICE $LABEL", 
+      NULL,
+      TRUE,
+    },
+    { 
+      "ext4", 
+      "e2label $DEVICE $LABEL", 
+      NULL,
+      TRUE,
+    },
+    {
+      "vfat", 
+      "mlabel -i $DEVICE ::$LABEL", 
+      "mlabel -i $DEVICE -c ::",
+      FALSE,
+    },
+    { 
+      "ntfs",
+      "ntfslabel $DEVICE $LABEL",
+      NULL,
+      FALSE,
+    },
+    { 
+      "xfs",
+      "xfs_admin -L $LABEL $DEVICE", 
+      "xfs_admin -L -- $DEVICE", 
+      FALSE,
+    },
+    { 
+      "reiserfs",
+      "reiserfstune -l $LABEL $DEVICE", 
+      NULL,
+      FALSE,
+    },
+    { 
+      "nilfs2",
+      "nilfs-tune -L $LABEL $DEVICE",
+      NULL,
+      FALSE,
+    },
+    {
+      "btrfs", 
+      NULL, 
+      NULL,
+      FALSE,
+    },
+    {
+      "minix", 
+      NULL,
+      NULL,
+      FALSE,
+    },
+    {
+      "swap", 
+      NULL,
+      NULL,
+      FALSE,
+    },
+  };
+
+/**
+ * get_fs_info:
+ *
+ * Look up #FSInfo record for a particular file system.
+ * @fstype: file system type name
+ *
+ * Returns: (transfer none): #FSInfo struct for @fstype, or %NULL if that file
+ *   system is unknown. Do not free or modify.
+ */
+const FSInfo *
+get_fs_info (const gchar *fstype)
+{
+  gint n;
+  g_return_val_if_fail (fstype != NULL, NULL);
+
+  for (n = 0; n < sizeof(_fs_info)/sizeof(FSInfo); n++)
+    {
+      if (strcmp (_fs_info[n].fstype, fstype) == 0)
+        return &_fs_info[n];
+    }
+
+  return NULL;
+}
diff --git a/src/udiskslinuxfsinfo.h b/src/udiskslinuxfsinfo.h
new file mode 100644 (file)
index 0000000..bb34082
--- /dev/null
@@ -0,0 +1,40 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Martin Pitt <martin.pitt@ubuntu.com>
+ *
+ * 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_LINUX_FSINFO_H__
+#define __UDISKS_LINUX_FSINFO_H__
+
+#include <glib/gtypes.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+  const gchar *fstype;
+  const gchar *command_change_label; /* should have $DEVICE and $LABEL */
+  const gchar *command_clear_label; /* should have $DEVICE; if NULL, call command_change_label with $LABEL == '' */
+  gboolean     supports_online_label_rename;
+} FSInfo;
+
+const FSInfo *get_fs_info (const gchar *fstype);
+
+G_END_DECLS
+
+#endif /* __UDISKS_LINUX_FSINFO_H__ */