dm ioctl: allow rename to fill empty uuid
authorPeter Jones <pjones@redhat.com>
Thu, 13 Jan 2011 19:59:47 +0000 (19:59 +0000)
committerAlasdair G Kergon <agk@redhat.com>
Thu, 13 Jan 2011 19:59:47 +0000 (19:59 +0000)
Allow the uuid of a mapped device to be set after device creation.
Previously the uuid (which is optional) could only be set by
DM_DEV_CREATE.  If no uuid was supplied it could not be set later.

Sometimes it's necessary to create the device before the uuid is known,
and in such cases the uuid must be filled in after the creation.

This patch extends DM_DEV_RENAME to accept a uuid accompanied by
a new flag DM_UUID_FLAG.  This can only be done once and if no
uuid was previously supplied.  It cannot be used to change an
existing uuid.

DM_VERSION_MINOR is also bumped to 19 to indicate this interface
extension is available.

Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
drivers/md/dm-ioctl.c
include/linux/dm-ioctl.h

index 4b54618b4159368eed4e3febee680b2ff94724df..f3481d922206127f646ccfc6a3423530cc046774 100644 (file)
@@ -295,19 +295,55 @@ retry:
                DMWARN("remove_all left %d open device(s)", dev_skipped);
 }
 
+/*
+ * Set the uuid of a hash_cell that isn't already set.
+ */
+static void __set_cell_uuid(struct hash_cell *hc, char *new_uuid)
+{
+       mutex_lock(&dm_hash_cells_mutex);
+       hc->uuid = new_uuid;
+       mutex_unlock(&dm_hash_cells_mutex);
+
+       list_add(&hc->uuid_list, _uuid_buckets + hash_str(new_uuid));
+}
+
+/*
+ * Changes the name of a hash_cell and returns the old name for
+ * the caller to free.
+ */
+static char *__change_cell_name(struct hash_cell *hc, char *new_name)
+{
+       char *old_name;
+
+       /*
+        * Rename and move the name cell.
+        */
+       list_del(&hc->name_list);
+       old_name = hc->name;
+
+       mutex_lock(&dm_hash_cells_mutex);
+       hc->name = new_name;
+       mutex_unlock(&dm_hash_cells_mutex);
+
+       list_add(&hc->name_list, _name_buckets + hash_str(new_name));
+
+       return old_name;
+}
+
 static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
                                            const char *new)
 {
-       char *new_name, *old_name;
+       char *new_data, *old_name = NULL;
        struct hash_cell *hc;
        struct dm_table *table;
        struct mapped_device *md;
+       unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
 
        /*
         * duplicate new.
         */
-       new_name = kstrdup(new, GFP_KERNEL);
-       if (!new_name)
+       new_data = kstrdup(new, GFP_KERNEL);
+       if (!new_data)
                return ERR_PTR(-ENOMEM);
 
        down_write(&_hash_lock);
@@ -315,13 +351,19 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
        /*
         * Is new free ?
         */
-       hc = __get_name_cell(new);
+       if (change_uuid)
+               hc = __get_uuid_cell(new);
+       else
+               hc = __get_name_cell(new);
+
        if (hc) {
-               DMWARN("asked to rename to an already-existing name %s -> %s",
+               DMWARN("Unable to change %s on mapped device %s to one that "
+                      "already exists: %s",
+                      change_uuid ? "uuid" : "name",
                       param->name, new);
                dm_put(hc->md);
                up_write(&_hash_lock);
-               kfree(new_name);
+               kfree(new_data);
                return ERR_PTR(-EBUSY);
        }
 
@@ -330,22 +372,30 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
         */
        hc = __get_name_cell(param->name);
        if (!hc) {
-               DMWARN("asked to rename a non-existent device %s -> %s",
-                      param->name, new);
+               DMWARN("Unable to rename non-existent device, %s to %s%s",
+                      param->name, change_uuid ? "uuid " : "", new);
                up_write(&_hash_lock);
-               kfree(new_name);
+               kfree(new_data);
                return ERR_PTR(-ENXIO);
        }
 
        /*
-        * rename and move the name cell.
+        * Does this device already have a uuid?
         */
-       list_del(&hc->name_list);
-       old_name = hc->name;
-       mutex_lock(&dm_hash_cells_mutex);
-       hc->name = new_name;
-       mutex_unlock(&dm_hash_cells_mutex);
-       list_add(&hc->name_list, _name_buckets + hash_str(new_name));
+       if (change_uuid && hc->uuid) {
+               DMWARN("Unable to change uuid of mapped device %s to %s "
+                      "because uuid is already set to %s",
+                      param->name, new, hc->uuid);
+               dm_put(hc->md);
+               up_write(&_hash_lock);
+               kfree(new_data);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (change_uuid)
+               __set_cell_uuid(hc, new_data);
+       else
+               old_name = __change_cell_name(hc, new_data);
 
        /*
         * Wake up any dm event waiters.
@@ -774,21 +824,24 @@ static int invalid_str(char *str, void *end)
 static int dev_rename(struct dm_ioctl *param, size_t param_size)
 {
        int r;
-       char *new_name = (char *) param + param->data_start;
+       char *new_data = (char *) param + param->data_start;
        struct mapped_device *md;
+       unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
 
-       if (new_name < param->data ||
-           invalid_str(new_name, (void *) param + param_size) ||
-           strlen(new_name) > DM_NAME_LEN - 1) {
-               DMWARN("Invalid new logical volume name supplied.");
+       if (new_data < param->data ||
+           invalid_str(new_data, (void *) param + param_size) ||
+           strlen(new_data) > (change_uuid ? DM_UUID_LEN - 1 : DM_NAME_LEN - 1)) {
+               DMWARN("Invalid new mapped device name or uuid string supplied.");
                return -EINVAL;
        }
 
-       r = check_name(new_name);
-       if (r)
-               return r;
+       if (!change_uuid) {
+               r = check_name(new_data);
+               if (r)
+                       return r;
+       }
 
-       md = dm_hash_rename(param, new_name);
+       md = dm_hash_rename(param, new_data);
        if (IS_ERR(md))
                return PTR_ERR(md);
 
index 49eab360d5d487395c7abefd700ffe294913495c..e453e1174ae181501ed38a26265e57861fac28e8 100644 (file)
@@ -44,7 +44,7 @@
  * Remove a device, destroy any tables.
  *
  * DM_DEV_RENAME:
- * Rename a device.
+ * Rename a device or set its uuid if none was previously supplied.
  *
  * DM_SUSPEND:
  * This performs both suspend and resume, depending which flag is
@@ -267,9 +267,9 @@ enum {
 #define DM_DEV_SET_GEOMETRY    _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
 
 #define DM_VERSION_MAJOR       4
-#define DM_VERSION_MINOR       18
+#define DM_VERSION_MINOR       19
 #define DM_VERSION_PATCHLEVEL  0
-#define DM_VERSION_EXTRA       "-ioctl (2010-06-29)"
+#define DM_VERSION_EXTRA       "-ioctl (2010-10-14)"
 
 /* Status bits */
 #define DM_READONLY_FLAG       (1 << 0) /* In/Out */
@@ -322,4 +322,10 @@ enum {
  */
 #define DM_UEVENT_GENERATED_FLAG       (1 << 13) /* Out */
 
+/*
+ * If set, rename changes the uuid not the name.  Only permitted
+ * if no uuid was previously supplied: an existing uuid cannot be changed.
+ */
+#define DM_UUID_FLAG                   (1 << 14) /* In */
+
 #endif                         /* _LINUX_DM_IOCTL_H */