]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - util.c
Disk removal support for Raid10->Raid0 takeover
[thirdparty/mdadm.git] / util.c
diff --git a/util.c b/util.c
index c2169d68dfa77d4590c918267f12e194a19fb470..fde58ebbeffa6271c93e01abae14c5880221e0f6 100644 (file)
--- a/util.c
+++ b/util.c
@@ -160,6 +160,31 @@ int get_linux_version()
        return (a*1000000)+(b*1000)+c;
 }
 
+int mdadm_version(char *version)
+{
+       int a, b, c;
+       char *cp;
+
+       if (!version)
+               version = Version;
+
+       cp = strchr(version, '-');
+       if (!cp || *(cp+1) != ' ' || *(cp+2) != 'v')
+               return -1;
+       cp += 3;
+       a = strtoul(cp, &cp, 10);
+       if (*cp != '.')
+               return -1;
+       b = strtoul(cp+1, &cp, 10);
+       if (*cp == '.')
+               c = strtoul(cp+1, &cp, 10);
+       else
+               c = 0;
+       if (*cp != ' ' && *cp != '-')
+               return -1;
+       return (a*1000000)+(b*1000)+c;
+}
+
 #ifndef MDASSEMBLE
 long long parse_size(char *size)
 {
@@ -320,6 +345,36 @@ int enough(int level, int raid_disks, int layout, int clean,
        }
 }
 
+int enough_fd(int fd)
+{
+       struct mdu_array_info_s array;
+       struct mdu_disk_info_s disk;
+       int avail_disks = 0;
+       int i;
+       char *avail;
+
+       if (ioctl(fd, GET_ARRAY_INFO, &array) != 0 ||
+           array.raid_disks <= 0)
+               return 0;
+       avail = calloc(array.raid_disks, 1);
+       for (i=0; i<array.raid_disks + array.nr_disks; i++) {
+               disk.number = i;
+               if (ioctl(fd, GET_DISK_INFO, &disk) != 0)
+                       continue;
+               if (! (disk.state & (1<<MD_DISK_SYNC)))
+                       continue;
+               if (disk.raid_disk < 0 || disk.raid_disk >= array.raid_disks)
+                       continue;
+               avail_disks++;
+               avail[disk.raid_disk] = 1;
+       }
+       /* This is used on an active array, so assume it is clean */
+       return enough(array.level, array.raid_disks, array.layout,
+                     1,
+                     avail, avail_disks);
+}
+
+
 const int uuid_match_any[4] = { ~0, ~0, ~0, ~0 };
 int same_uuid(int a[4], int b[4], int swapuuid)
 {
@@ -470,7 +525,7 @@ int check_raid(int fd, char *name)
        /* Looks like a raid array .. */
        fprintf(stderr, Name ": %s appears to be part of a raid array:\n",
                name);
-       st->ss->getinfo_super(st, &info);
+       st->ss->getinfo_super(st, &info, NULL);
        st->ss->free_super(st);
        crtime = info.array.ctime;
        level = map_num(pers, info.array.level);
@@ -1002,7 +1057,7 @@ struct superswitch *superlist[] =
 
 #if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO)
 
-struct supertype *super_by_fd(int fd)
+struct supertype *super_by_fd(int fd, char **subarrayp)
 {
        mdu_array_info_t array;
        int vers;
@@ -1013,6 +1068,7 @@ struct supertype *super_by_fd(int fd)
        char version[20];
        int i;
        char *subarray = NULL;
+       int container = NoMdDev;
 
        sra = sysfs_read(fd, 0, GET_VERSION);
 
@@ -1034,15 +1090,15 @@ struct supertype *super_by_fd(int fd)
        }
        if (minor == -2 && is_subarray(verstr)) {
                char *dev = verstr+1;
+
                subarray = strchr(dev, '/');
-               int devnum;
                if (subarray)
                        *subarray++ = '\0';
-               devnum = devname2devnum(dev);
                subarray = strdup(subarray);
+               container = devname2devnum(dev);
                if (sra)
                        sysfs_free(sra);
-               sra = sysfs_read(-1, devnum, GET_VERSION);
+               sra = sysfs_read(-1, container, GET_VERSION);
                if (sra && sra->text_version[0])
                        verstr = sra->text_version;
                else
@@ -1056,17 +1112,33 @@ struct supertype *super_by_fd(int fd)
                sysfs_free(sra);
        if (st) {
                st->sb = NULL;
-               if (subarray) {
-                       strncpy(st->subarray, subarray, 32);
-                       st->subarray[31] = 0;
-                       free(subarray);
-               } else
-                       st->subarray[0] = 0;
-       }
+               if (subarrayp)
+                       *subarrayp = subarray;
+               st->container_dev = container;
+               st->devnum = fd2devnum(fd);
+       } else
+               free(subarray);
+
        return st;
 }
 #endif /* !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO) */
 
+int dev_size_from_id(dev_t id, unsigned long long *size)
+{
+       char buf[20];
+       int fd;
+
+       sprintf(buf, "%d:%d", major(id), minor(id));
+       fd = dev_open(buf, O_RDONLY);
+       if (fd < 0)
+               return 0;
+       if (get_dev_size(fd, NULL, size)) {
+               close(fd);
+               return 1;
+       }
+       close(fd);
+       return 0;
+}
 
 struct supertype *dup_super(struct supertype *orig)
 {
@@ -1081,13 +1153,12 @@ struct supertype *dup_super(struct supertype *orig)
        st->ss = orig->ss;
        st->max_devs = orig->max_devs;
        st->minor_version = orig->minor_version;
-       strcpy(st->subarray, orig->subarray);
        st->sb = NULL;
        st->info = NULL;
        return st;
 }
 
-struct supertype *guess_super(int fd)
+struct supertype *guess_super_type(int fd, enum guess_types guess_type)
 {
        /* try each load_super to find the best match,
         * and return the best superswitch
@@ -1099,14 +1170,21 @@ struct supertype *guess_super(int fd)
        int i;
 
        st = malloc(sizeof(*st));
+       memset(st, 0, sizeof(*st));
+       st->container_dev = NoMdDev;
+
        for (i=0 ; superlist[i]; i++) {
                int rv;
                ss = superlist[i];
+               if (guess_type == guess_array && ss->add_to_super == NULL)
+                       continue;
+               if (guess_type == guess_partitions && ss->add_to_super != NULL)
+                       continue;
                memset(st, 0, sizeof(*st));
                rv = ss->load_super(st, fd, NULL);
                if (rv == 0) {
                        struct mdinfo info;
-                       st->ss->getinfo_super(st, &info);
+                       st->ss->getinfo_super(st, &info, NULL);
                        if (bestsuper == -1 ||
                            besttime < info.array.ctime) {
                                bestsuper = i;
@@ -1156,6 +1234,20 @@ int get_dev_size(int fd, char *dname, unsigned long long *sizep)
        return 1;
 }
 
+/* Return true if this can only be a container, not a member device.
+ * i.e. is and md device and size is zero
+ */
+int must_be_container(int fd)
+{
+       unsigned long long size;
+       if (md_get_version(fd) < 0)
+               return 0;
+       if (get_dev_size(fd, NULL, &size) == 0)
+               return 1;
+       if (size == 0)
+               return 1;
+       return 0;
+}
 
 /* Sets endofpart parameter to the last block used by the last GPT partition on the device.
  * Returns: 1 if successful
@@ -1386,14 +1478,11 @@ int is_subarray_active(char *subarray, char *container)
        struct mdstat_ent *mdstat = mdstat_read(0, 0);
        struct mdstat_ent *ent;
 
-       for (ent = mdstat; ent; ent = ent->next) {
-               if (is_container_member(ent, container)) {
-                       char *inst = &ent->metadata_version[10+strlen(container)+1];
-
-                       if (!subarray || strcmp(inst, subarray) == 0)
+       for (ent = mdstat; ent; ent = ent->next)
+               if (is_container_member(ent, container))
+                       if (!subarray ||
+                           strcmp(to_subarray(ent, container), subarray) == 0)
                                break;
-               }
-       }
 
        free_mdstat(mdstat);
 
@@ -1407,14 +1496,15 @@ int is_container_active(char *container)
 
 /* open_subarray - opens a subarray in a container
  * @dev: container device name
- * @st: supertype with only ->subarray set
+ * @st: empty supertype
  * @quiet: block reporting errors flag
  *
  * On success returns an fd to a container and fills in *st
  */
-int open_subarray(char *dev, struct supertype *st, int quiet)
+int open_subarray(char *dev, char *subarray, struct supertype *st, int quiet)
 {
        struct mdinfo *mdi;
+       struct mdinfo *info;
        int fd, err = 1;
 
        fd = open(dev, O_RDWR|O_EXCL);
@@ -1464,18 +1554,27 @@ int open_subarray(char *dev, struct supertype *st, int quiet)
                goto free_sysfs;
        }
 
-       if (st->ss->load_super(st, fd, NULL)) {
+       if (!st->ss->load_container) {
                if (!quiet)
-                       fprintf(stderr, Name ": Failed to find subarray-%s in %s\n",
-                               st->subarray, dev);
+                       fprintf(stderr, Name ": %s is not a container\n", dev);
                goto free_name;
        }
 
-       if (!st->loaded_container) {
+       if (st->ss->load_container(st, fd, NULL)) {
                if (!quiet)
-                       fprintf(stderr, Name ": %s is not a container\n", dev);
+                       fprintf(stderr, Name ": Failed to load metadata for %s\n",
+                               dev);
+               goto free_name;
+       }
+
+       info = st->ss->container_content(st, subarray);
+       if (!info) {
+               if (!quiet)
+                       fprintf(stderr, Name ": Failed to find subarray-%s in %s\n",
+                               subarray, dev);
                goto free_super;
        }
+       free(info);
 
        err = 0;
 
@@ -1527,6 +1626,21 @@ int add_disk(int mdfd, struct supertype *st,
        return rv;
 }
 
+int remove_disk(int mdfd, struct supertype *st,
+               struct mdinfo *sra, struct mdinfo *info)
+{
+       int rv;
+       /* Remove the disk given by 'info' from the array */
+#ifndef MDASSEMBLE
+       if (st->ss->external)
+               rv = sysfs_set_str(sra, info, "slot", "none");
+       else
+#endif
+               rv = ioctl(mdfd, HOT_REMOVE_DISK, makedev(info->disk.major,
+                                                         info->disk.minor));
+       return rv;
+}
+
 int set_array_info(int mdfd, struct supertype *st, struct mdinfo *info)
 {
        /* Initialise kernel's knowledge of array.