]> git.ipfire.org Git - thirdparty/mdadm.git/commitdiff
Add support for auto-partitioning base devices.
authorNeilBrown <neilb@suse.de>
Thu, 2 Sep 2010 04:30:03 +0000 (14:30 +1000)
committerNeilBrown <neilb@suse.de>
Mon, 6 Sep 2010 01:26:28 +0000 (11:26 +1000)
If a device is bare and policy suggests that it can be used as a spare
for virtual 'partitions' array, find an appropriate partition table
and write it to the device.

Signed-off-by: NeilBrown <neilb@suse.de>
Incremental.c

index a6c98bdc5a53794f1c66b3de5b13d9022e56e114..3a41a1e18260950a2948ffe2a64243310b255205 100644 (file)
@@ -29,6 +29,8 @@
  */
 
 #include       "mdadm.h"
+#include       <dirent.h>
+#include       <ctype.h>
 
 static int count_active(struct supertype *st, int mdfd, char **availp,
                        struct mdinfo *info);
@@ -857,7 +859,143 @@ static int array_try_spare(char *devname, int *dfdp, struct dev_policy *pol,
 static int partition_try_spare(char *devname, int *dfdp, struct dev_policy *pol,
                               struct supertype *st, int verbose)
 {
-       return 1;
+       /* we know that at least one partition virtual-metadata is
+        * allowed to incorporate spares like this device.  We need to
+        * find a suitable device to copy partition information from.
+        *
+        * Getting a list of all disk (not partition) devices is
+        * slightly non-trivial.  We could look at /sys/block, but
+        * that is theoretically due to be removed.  Maybe best to use
+        * /dev/disk/by-path/?* and ignore names ending '-partNN' as
+        * we depend on this directory of 'path' info.  But that fails
+        * to find loop devices and probably others.  Maybe don't
+        * worry about that, they aren't the real target.
+        *
+        * So: check things in /dev/disk/by-path to see if they are in
+        * a compatible domain, then load the partition table and see
+        * if it is OK for the new device, and choose the largest
+        * partition table that fits.
+        */
+       DIR *dir;
+       struct dirent *de;
+       char *chosen = NULL;
+       unsigned long long chosen_size;
+       struct supertype *chosen_st = NULL;
+       int fd;
+
+       dir = opendir("/dev/disk/by-path");
+       if (!dir)
+               return 1;
+       while ((de = readdir(dir)) != NULL) {
+               char *ep;
+               struct dev_policy *pol2 = NULL;
+               struct domainlist *domlist = NULL;
+               int fd = -1;
+               struct mdinfo info;
+               struct supertype *st2 = NULL;
+               char *devname = NULL;
+               unsigned long long devsectors;
+
+               if (de->d_ino == 0 ||
+                   de->d_name[0] == '.' ||
+                   (de->d_type != DT_LNK && de->d_type != DT_UNKNOWN))
+                       goto next;
+
+               ep = de->d_name + strlen(de->d_name);
+               while (ep > de->d_name &&
+                      isdigit(ep[-1]))
+                       ep--;
+               if (ep > de->d_name + 5 &&
+                   strncmp(ep-5, "-part", 5) == 0)
+                       /* This is a partition - skip it */
+                       goto next;
+
+               pol2 = path_policy(de->d_name, type_disk);
+
+               domain_merge(&domlist, pol2, st ? st->ss->name : NULL);
+               if (domain_test(domlist, pol, st ? st->ss->name : NULL) == 0)
+                       /* new device is incompatible with this device. */
+                       goto next;
+
+               domain_free(domlist);
+               domlist = NULL;
+
+               asprintf(&devname, "/dev/disk/by-path/%s", de->d_name);
+               fd = open(devname, O_RDONLY);
+               if (fd < 0)
+                       goto next;
+               if (get_dev_size(fd, devname, &devsectors) == 0)
+                       goto next;
+               devsectors >>= 9;
+
+               if (st)
+                       st2 = dup_super(st);
+               else
+                       st2 = guess_super_type(fd, guess_partitions);
+               if (st2 == NULL ||
+                   st2->ss->load_super(st2, fd, NULL) < 0)
+                       goto next;
+
+               if (!st) {
+                       /* Check domain policy again, this time referring to metadata */
+                       domain_merge(&domlist, pol2, st2->ss->name);
+                       if (domain_test(domlist, pol, st2->ss->name) == 0)
+                               /* Incompatible devices for this metadata type */
+                               goto next;
+               }
+
+               st2->ss->getinfo_super(st2, &info);
+               if (info.component_size > devsectors)
+                       /* This partitioning doesn't fit in the device */
+                       goto next;
+
+               /* This is an acceptable device to copy partition
+                * metadata from.  We could just stop here, but I
+                * think I want to keep looking incase a larger
+                * metadata which makes better use of the device can
+                * be found.
+                */
+               if (chosen == NULL ||
+                   chosen_size < info.component_size) {
+                       chosen_size = info.component_size;
+                       free(chosen);
+                       chosen = devname;
+                       devname = NULL;
+                       if (chosen_st) {
+                               chosen_st->ss->free_super(chosen_st);
+                               free(chosen_st);
+                       }
+                       chosen_st = st2;
+                       st2 = NULL;
+               }
+
+       next:
+               free(devname);
+               domain_free(domlist);
+               dev_policy_free(pol2);
+               if (st2)
+                       st2->ss->free_super(st2);
+               free(st2);
+
+               if (fd >= 0)
+                       close(fd);
+       }
+
+       if (!chosen)
+               return 1;
+
+       /* 'chosen' is the best device we can find.  Let's write its
+        * metadata to devname dfd is read-only so don't use that
+        */
+       fd = open(devname, O_RDWR);
+       if (fd >= 0) {
+               chosen_st->ss->store_super(chosen_st, fd);
+               close(fd);
+       }
+       free(chosen);
+       chosen_st->ss->free_super(chosen_st);
+       free(chosen_st);
+       return 0;
 }