]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - Assemble.c
Allow domain_test to report that no domains were found.
[thirdparty/mdadm.git] / Assemble.c
index 7da090506a224d0f6efdf4288ee8537c2ac10cb1..b3f54ce43a811b28f425bf1f92b9cb96361af19e 100644 (file)
@@ -70,9 +70,72 @@ static int is_member_busy(char *metadata_version)
        return busy;
 }
 
+static int ident_matches(struct mddev_ident *ident,
+                        struct mdinfo *content,
+                        struct supertype *tst,
+                        char *homehost,
+                        char *update, char *devname)
+{
+
+       if (ident->uuid_set && (!update || strcmp(update, "uuid")!= 0) &&
+           same_uuid(content->uuid, ident->uuid, tst->ss->swapuuid)==0 &&
+           memcmp(content->uuid, uuid_zero, sizeof(int[4])) != 0) {
+               if (devname)
+                       fprintf(stderr, Name ": %s has wrong uuid.\n",
+                               devname);
+               return 0;
+       }
+       if (ident->name[0] && (!update || strcmp(update, "name")!= 0) &&
+           name_matches(content->name, ident->name, homehost)==0) {
+               if (devname)
+                       fprintf(stderr, Name ": %s has wrong name.\n",
+                               devname);
+               return 0;
+       }
+       if (ident->super_minor != UnSet &&
+           ident->super_minor != content->array.md_minor) {
+               if (devname)
+                       fprintf(stderr, Name ": %s has wrong super-minor.\n",
+                               devname);
+               return 0;
+       }
+       if (ident->level != UnSet &&
+           ident->level != content->array.level) {
+               if (devname)
+                       fprintf(stderr, Name ": %s has wrong raid level.\n",
+                               devname);
+               return 0;
+       }
+       if (ident->raid_disks != UnSet &&
+           ident->raid_disks!= content->array.raid_disks) {
+               if (devname)
+                       fprintf(stderr, Name ": %s requires wrong number of drives.\n",
+                               devname);
+               return 0;
+       }
+       if (ident->member && ident->member[0]) {
+               /* content->text_version must match */
+               char *s = strchr(content->text_version+1, '/');
+               if (s == NULL) {
+                       if (devname)
+                               fprintf(stderr, Name ": %s is not a container and one is required.\n",
+                                       devname);
+                       return 0;
+               } else if (strcmp(ident->member, s+1) != 0) {
+                       if (devname)
+                               fprintf(stderr, Name ": skipping wrong member %s is %s\n",
+                                       content->text_version, devname);
+                       return 0;
+               }
+       }
+       return 1;
+}
+                        
+
 int Assemble(struct supertype *st, char *mddev,
-            mddev_ident_t ident,
-            mddev_dev_t devlist, char *backup_file,
+            struct mddev_ident *ident,
+            struct mddev_dev *devlist,
+            char *backup_file, int invalid_backup,
             int readonly, int runstop,
             char *update, char *homehost, int require_homehost,
             int verbose, int force)
@@ -145,12 +208,13 @@ int Assemble(struct supertype *st, char *mddev,
                               */
                struct mdinfo i;
        } *devices;
+       char *devmap;
        int *best = NULL; /* indexed by raid_disk */
-       unsigned int bestcnt = 0;
+       int bestcnt = 0;
        int devcnt = 0;
-       unsigned int okcnt, sparecnt;
+       unsigned int okcnt, sparecnt, rebuilding_cnt;
        unsigned int req_cnt;
-       unsigned int i;
+       int i;
        int most_recent = 0;
        int chosen_drive;
        int change = 0;
@@ -160,7 +224,7 @@ int Assemble(struct supertype *st, char *mddev,
        int start_partial_ok = (runstop >= 0) && 
                (force || devlist==NULL || auto_assem);
        unsigned int num_devs;
-       mddev_dev_t tmpdev;
+       struct mddev_dev *tmpdev;
        struct mdinfo info;
        struct mdinfo *content = NULL;
        char *avail;
@@ -168,6 +232,7 @@ int Assemble(struct supertype *st, char *mddev,
        char *name = NULL;
        int trustworthy;
        char chosen_name[1024];
+       struct domainlist *domains = NULL;
 
        if (get_linux_version() < 2004000)
                old_linux = 1;
@@ -182,7 +247,7 @@ int Assemble(struct supertype *st, char *mddev,
 
        if (!devlist &&
            ident->uuid_set == 0 &&
-           ident->super_minor < 0 &&
+           (ident->super_minor < 0 || ident->super_minor == UnSet) &&
            ident->name[0] == 0 &&
            (ident->container == NULL || ident->member == NULL) &&
            ident->devices == NULL) {
@@ -211,7 +276,6 @@ int Assemble(struct supertype *st, char *mddev,
                        num_devs++;
                tmpdev = tmpdev->next;
        }
-       devices = malloc(num_devs * sizeof(*devices));
 
        if (!st && ident->st) st = ident->st;
 
@@ -225,11 +289,13 @@ int Assemble(struct supertype *st, char *mddev,
         */
        for (tmpdev = devlist;
             tmpdev;
-            tmpdev = tmpdev->next) {
+            tmpdev = tmpdev ? tmpdev->next : NULL) {
                char *devname = tmpdev->devname;
                int dfd;
                struct stat stb;
                struct supertype *tst = dup_super(st);
+               struct dev_policy *pol = NULL;
+               int found_container = 0;
 
                if (tmpdev->used > 1) continue;
 
@@ -255,34 +321,7 @@ int Assemble(struct supertype *st, char *mddev,
                        fprintf(stderr, Name ": %s is not a block device.\n",
                                devname);
                        tmpdev->used = 2;
-               } else if (!tst && (tst = guess_super(dfd)) == NULL) {
-                       if (report_missmatch)
-                               fprintf(stderr, Name ": no recogniseable superblock on %s\n",
-                                       devname);
-                       tmpdev->used = 2;
-               } else if (auto_assem && st == NULL &&
-                          !conf_test_metadata(tst->ss->name)) {
-                       if (report_missmatch)
-                               fprintf(stderr, Name ": %s has metadata type %s for which "
-                                       "auto-assembly is disabled\n",
-                                       devname, tst->ss->name);
-                       tmpdev->used = 2;
-               } else if (tst->ss->load_super(tst,dfd, NULL)) {
-                       if (report_missmatch)
-                               fprintf( stderr, Name ": no RAID superblock on %s\n",
-                                        devname);
-               } else {
-                       content = &info;
-                       memset(content, 0, sizeof(*content));
-                       tst->ss->getinfo_super(tst, content);
-               }
-               if (dfd >= 0) close(dfd);
-
-               if (tst && tst->sb && tst->ss->container_content
-                   && tst->loaded_container) {
-                       /* tmpdev is a container.  We need to be either
-                        * looking for a member, or auto-assembling
-                        */
+               } else if (must_be_container(dfd)) {
                        if (st) {
                                /* already found some components, this cannot
                                 * be another one.
@@ -290,8 +329,79 @@ int Assemble(struct supertype *st, char *mddev,
                                if (report_missmatch)
                                        fprintf(stderr, Name ": %s is a container, but we are looking for components\n",
                                                devname);
-                               goto loop;
+                               tmpdev->used = 2;
+                       } if (!tst && (tst = super_by_fd(dfd, NULL)) == NULL) {
+                               if (report_missmatch)
+                                       fprintf(stderr, Name ": not a recognisable container: %s\n",
+                                               devname);
+                               tmpdev->used = 2;
+                       } else if (!tst->ss->load_container
+                                  || tst->ss->load_container(tst, dfd, NULL)) {
+                               if (report_missmatch)
+                                       fprintf(stderr, Name ": no correct container type: %s\n",
+                                               devname);
+                               tmpdev->used = 2;
+                       } else if (auto_assem &&
+                                  !conf_test_metadata(tst->ss->name, (pol = devnum_policy(stb.st_rdev)),
+                                                      tst->ss->match_home(tst, homehost) == 1)) {
+                               if (report_missmatch)
+                                       fprintf(stderr, Name ": %s has metadata type %s for which "
+                                               "auto-assembly is disabled\n",
+                                               devname, tst->ss->name);
+                               tmpdev->used = 2;
+                       } else
+                               found_container = 1;
+               } else {
+                       if (!tst && (tst = guess_super(dfd)) == NULL) {
+                               if (report_missmatch)
+                                       fprintf(stderr, Name ": no recogniseable superblock on %s\n",
+                                               devname);
+                               tmpdev->used = 2;
+                       } else if (tst->ss->load_super(tst,dfd, NULL)) {
+                               if (report_missmatch)
+                                       fprintf(stderr, Name ": no RAID superblock on %s\n",
+                                               devname);
+                               tmpdev->used = 2;
+                       } else if (tst->ss->compare_super == NULL) {
+                               if (report_missmatch)
+                                       fprintf(stderr, Name ": Cannot assemble %s metadata on %s\n",
+                                               tst->ss->name, devname);
+                               tmpdev->used = 2;
+                       } else if (auto_assem && st == NULL &&
+                                  !conf_test_metadata(tst->ss->name, (pol = devnum_policy(stb.st_rdev)),
+                                                      tst->ss->match_home(tst, homehost) == 1)) {
+                               if (report_missmatch)
+                                       fprintf(stderr, Name ": %s has metadata type %s for which "
+                                               "auto-assembly is disabled\n",
+                                               devname, tst->ss->name);
+                               tmpdev->used = 2;
                        }
+               }
+               if (dfd >= 0) close(dfd);
+               if (tmpdev->used == 2) {
+                       if (auto_assem || !inargv)
+                               /* Ignore unrecognised devices during auto-assembly */
+                               goto loop;
+                       if (ident->uuid_set || ident->name[0] ||
+                           ident->super_minor != UnSet)
+                               /* Ignore unrecognised device if looking for
+                                * specific array */
+                               goto loop;
+                           
+
+                       fprintf(stderr, Name ": %s has no superblock - assembly aborted\n",
+                               devname);
+                       if (st)
+                               st->ss->free_super(st);
+                       dev_policy_free(pol);
+                       domain_free(domains);
+                       return 1;
+               }
+
+               if (found_container) {
+                       /* tmpdev is a container.  We need to be either
+                        * looking for a member, or auto-assembling
+                        */
 
                        if (ident->container) {
                                if (ident->container[0] == '/' &&
@@ -304,6 +414,11 @@ int Assemble(struct supertype *st, char *mddev,
                                if (ident->container[0] != '/') {
                                        /* we have a uuid */
                                        int uuid[4];
+
+                                       content = &info;
+                                       memset(content, 0, sizeof(*content));
+                                       tst->ss->getinfo_super(tst, content, NULL);
+
                                        if (!parse_uuid(ident->container, uuid) ||
                                            !same_uuid(content->uuid, uuid, tst->ss->swapuuid)) {
                                                if (report_missmatch)
@@ -315,173 +430,190 @@ int Assemble(struct supertype *st, char *mddev,
                        }
                        /* It is worth looking inside this container.
                         */
-               next_member:
-                       if (tmpdev->content)
-                               content = tmpdev->content;
-                       else
-                               content = tst->ss->container_content(tst);
-                       if (!content)
-                               goto loop; /* empty container */
-
-                       tmpdev->content = content->next;
-                       if (tmpdev->content == NULL)
-                               tmpdev->used = 2;
-
-               } else if (ident->container || ident->member) {
-                       /* No chance of this matching if we don't have
-                        * a container */
-                       if (report_missmatch)
-                               fprintf(stderr, Name "%s is not a container, and one is required.\n",
+                       if (verbose > 0)
+                               fprintf(stderr, Name ": looking in container %s\n",
                                        devname);
-                       goto loop;
-               }
-
-               if (ident->uuid_set && (!update || strcmp(update, "uuid")!= 0) &&
-                   (!tst || !tst->sb ||
-                    same_uuid(content->uuid, ident->uuid, tst->ss->swapuuid)==0)) {
-                       if (report_missmatch)
-                               fprintf(stderr, Name ": %s has wrong uuid.\n",
-                                       devname);
-                       goto loop;
-               }
-               if (ident->name[0] && (!update || strcmp(update, "name")!= 0) &&
-                   (!tst || !tst->sb ||
-                    name_matches(content->name, ident->name, homehost)==0)) {
-                       if (report_missmatch)
-                               fprintf(stderr, Name ": %s has wrong name.\n",
-                                       devname);
-                       goto loop;
-               }
-               if (ident->super_minor != UnSet &&
-                   (!tst || !tst->sb ||
-                    ident->super_minor != content->array.md_minor)) {
-                       if (report_missmatch)
-                               fprintf(stderr, Name ": %s has wrong super-minor.\n",
-                                       devname);
-                       goto loop;
-               }
-               if (ident->level != UnSet &&
-                   (!tst || !tst->sb ||
-                    ident->level != content->array.level)) {
-                       if (report_missmatch)
-                               fprintf(stderr, Name ": %s has wrong raid level.\n",
-                                       devname);
-                       goto loop;
-               }
-               if (ident->raid_disks != UnSet &&
-                   (!tst || !tst->sb ||
-                    ident->raid_disks!= content->array.raid_disks)) {
-                       if (report_missmatch)
-                               fprintf(stderr, Name ": %s requires wrong number of drives.\n",
-                                       devname);
-                       goto loop;
-               }
-               if (auto_assem) {
-                       if (tst == NULL || tst->sb == NULL)
-                               continue;
-               }
-               /* If we are this far, then we are nearly commited to this device.
-                * If the super_block doesn't exist, or doesn't match others,
-                * then we probably cannot continue
-                * However if one of the arrays is for the homehost, and
-                * the other isn't that can disambiguate.
-                */
 
-               if (!tst || !tst->sb) {
-                       fprintf(stderr, Name ": %s has no superblock - assembly aborted\n",
-                               devname);
-                       if (st)
-                               st->ss->free_super(st);
-                       return 1;
-               }
+                       for (content = tst->ss->container_content(tst, NULL);
+                            content;
+                            content = content->next) {
 
-               if (tst && tst->sb && tst->ss->container_content
-                   && tst->loaded_container) {
-                       /* we have the one container we need, don't keep
-                        * looking.  If the chosen member is active, skip.
-                        */
-                       if (is_member_busy(content->text_version)) {
-                               if (report_missmatch)
-                                       fprintf(stderr, Name ": member %s in %s is already assembled\n",
-                                               content->text_version,
-                                               devname);
-                               tst->ss->free_super(tst);
-                               tst = NULL;
-                               content = NULL;
-                               if (auto_assem)
+                               /* do not assemble arrays that might have bad blocks */
+                               if (content->array.state & (1<<MD_SB_BBM_ERRORS)) {
+                                       fprintf(stderr, Name ": BBM log found in metadata. "
+                                                               "Cannot activate array(s).\n");
+                                       tmpdev->used = 2;
                                        goto loop;
-                               return 1;
+                               }
+                               if (!ident_matches(ident, content, tst,
+                                                  homehost, update,
+                                                  report_missmatch ? devname : NULL))
+                                       /* message already printed */;
+                               else if (is_member_busy(content->text_version)) {
+                                       if (report_missmatch)
+                                               fprintf(stderr, Name ": member %s in %s is already assembled\n",
+                                                       content->text_version,
+                                                       devname);
+                               } else
+                                       break;
+                       }
+                       if (!content) {
+                               tmpdev->used = 2;
+                               goto loop; /* empty container */
                        }
+
                        st = tst; tst = NULL;
-                       if (!auto_assem && tmpdev->next != NULL) {
+                       if (!auto_assem && inargv && tmpdev->next != NULL) {
                                fprintf(stderr, Name ": %s is a container, but is not "
                                        "only device given: confused and aborting\n",
                                        devname);
                                st->ss->free_super(st);
+                               dev_policy_free(pol);
+                               domain_free(domains);
                                return 1;
                        }
-                       break;
-               }
-               if (st == NULL)
-                       st = dup_super(tst);
-               if (st->minor_version == -1)
-                       st->minor_version = tst->minor_version;
-               if (st->ss != tst->ss ||
-                   st->minor_version != tst->minor_version ||
-                   st->ss->compare_super(st, tst) != 0) {
-                       /* Some mismatch. If exactly one array matches this host,
-                        * we can resolve on that one.
-                        * Or, if we are auto assembling, we just ignore the second
-                        * for now.
-                        */
-                       if (auto_assem)
+                       if (verbose > 0)
+                               fprintf(stderr, Name ": found match on member %s in %s\n",
+                                       content->text_version, devname);
+
+                       /* make sure we finished the loop */
+                       tmpdev = NULL;
+                       goto loop;
+               } else {
+
+                       content = &info;
+                       memset(content, 0, sizeof(*content));
+                       tst->ss->getinfo_super(tst, content, NULL);
+
+                       if (!ident_matches(ident, content, tst,
+                                          homehost, update,
+                                          report_missmatch ? devname : NULL))
                                goto loop;
-                       if (homehost) {
-                               int first = st->ss->match_home(st, homehost);
-                               int last = tst->ss->match_home(tst, homehost);
-                               if (first != last &&
-                                   (first == 1 || last == 1)) {
-                                       /* We can do something */
-                                       if (first) {/* just ignore this one */
-                                               if (report_missmatch)
-                                                       fprintf(stderr, Name ": %s misses out due to wrong homehost\n",
-                                                               devname);
-                                               goto loop;
-                                       } else { /* reject all those sofar */
-                                               mddev_dev_t td;
-                                               if (report_missmatch)
-                                                       fprintf(stderr, Name ": %s overrides previous devices due to good homehost\n",
-                                                               devname);
-                                               for (td=devlist; td != tmpdev; td=td->next)
-                                                       if (td->used == 1)
-                                                               td->used = 0;
-                                               tmpdev->used = 1;
-                                               goto loop;
+                               
+                       if (st == NULL)
+                               st = dup_super(tst);
+                       if (st->minor_version == -1)
+                               st->minor_version = tst->minor_version;
+
+                       if (memcmp(content->uuid, uuid_zero,
+                                  sizeof(int[4])) == 0) {
+                               /* this is a floating spare.  It cannot define
+                                * an array unless there are no more arrays of
+                                * this type to be found.  It can be included
+                                * in an array of this type though.
+                                */
+                               tmpdev->used = 3;
+                               goto loop;
+                       }
+
+                       if (st->ss != tst->ss ||
+                           st->minor_version != tst->minor_version ||
+                           st->ss->compare_super(st, tst) != 0) {
+                               /* Some mismatch. If exactly one array matches this host,
+                                * we can resolve on that one.
+                                * Or, if we are auto assembling, we just ignore the second
+                                * for now.
+                                */
+                               if (auto_assem)
+                                       goto loop;
+                               if (homehost) {
+                                       int first = st->ss->match_home(st, homehost);
+                                       int last = tst->ss->match_home(tst, homehost);
+                                       if (first != last &&
+                                           (first == 1 || last == 1)) {
+                                               /* We can do something */
+                                               if (first) {/* just ignore this one */
+                                                       if (report_missmatch)
+                                                               fprintf(stderr, Name ": %s misses out due to wrong homehost\n",
+                                                                       devname);
+                                                       goto loop;
+                                               } else { /* reject all those sofar */
+                                                       struct mddev_dev *td;
+                                                       if (report_missmatch)
+                                                               fprintf(stderr, Name ": %s overrides previous devices due to good homehost\n",
+                                                                       devname);
+                                                       for (td=devlist; td != tmpdev; td=td->next)
+                                                               if (td->used == 1)
+                                                                       td->used = 0;
+                                                       tmpdev->used = 1;
+                                                       goto loop;
+                                               }
                                        }
                                }
+                               fprintf(stderr, Name ": superblock on %s doesn't match others - assembly aborted\n",
+                                       devname);
+                               tst->ss->free_super(tst);
+                               st->ss->free_super(st);
+                               dev_policy_free(pol);
+                               domain_free(domains);
+                               return 1;
                        }
-                       fprintf(stderr, Name ": superblock on %s doesn't match others - assembly aborted\n",
-                               devname);
-                       tst->ss->free_super(tst);
-                       st->ss->free_super(st);
-                       return 1;
+                       tmpdev->used = 1;
                }
-
-               tmpdev->used = 1;
-
        loop:
-               if (tmpdev->content)
-                       goto next_member;
+               /* Collect domain information from members only */
+               if (tmpdev && tmpdev->used == 1) {
+                       if (!pol)
+                               pol = devnum_policy(stb.st_rdev);
+                       domain_merge(&domains, pol, tst?tst->ss->name:NULL);
+               }
+               dev_policy_free(pol);
+               pol = NULL;
                if (tst)
                        tst->ss->free_super(tst);
        }
 
+       /* Check if we found some imsm spares but no members */
+       if ((auto_assem ||
+            (ident->uuid_set &&
+             memcmp(uuid_zero, ident->uuid,sizeof(uuid_zero)) == 0)) &&
+           (!st || !st->sb))
+               for (tmpdev = devlist; tmpdev; tmpdev = tmpdev->next) {
+                       if (tmpdev->used != 3)
+                               continue;
+                       tmpdev->used = 1;
+                       content = &info;
+
+                       if (!st->sb) {
+                               /* we need sb from one of the spares */
+                               int dfd = dev_open(tmpdev->devname, O_RDONLY);
+                               if (dfd < 0 ||
+                                   st->ss->load_super(st, dfd, NULL))
+                                       tmpdev->used = 2;
+                               if (dfd > 0)
+                                       close(dfd);
+                       }
+               }
+
+       /* Now reject spares that don't match domains of identified members */
+       for (tmpdev = devlist; tmpdev; tmpdev = tmpdev->next) {
+               struct stat stb;
+               if (tmpdev->used != 3)
+                       continue;
+               if (stat(tmpdev->devname, &stb)< 0) {
+                       fprintf(stderr, Name ": fstat failed for %s: %s\n",
+                               tmpdev->devname, strerror(errno));
+                       tmpdev->used = 2;
+               } else {
+                       struct dev_policy *pol = NULL;
+                       pol = devnum_policy(stb.st_rdev);
+                       if (domain_test(domains, pol, NULL) == 1)
+                               /* take this spare if domains match */
+                               tmpdev->used = 1;
+                       else
+                               /* if domains don't match mark as unused */
+                               tmpdev->used = 0;
+                       dev_policy_free(pol);
+               }
+       }
+       domain_free(domains);
+       
        if (!st || !st->sb || !content)
                return 2;
 
        /* Now need to open the array device.  Use create_mddev */
        if (content == &info)
-               st->ss->getinfo_super(st, content);
+               st->ss->getinfo_super(st, content, NULL);
 
        trustworthy = FOREIGN;
        name = content->name;
@@ -523,7 +655,6 @@ int Assemble(struct supertype *st, char *mddev,
                            chosen_name);
        if (mdfd < 0) {
                st->ss->free_super(st);
-               free(devices);
                if (auto_assem)
                        goto try_again;
                return 1;
@@ -549,7 +680,6 @@ int Assemble(struct supertype *st, char *mddev,
                close(mdfd);
                mdfd = -3;
                st->ss->free_super(st);
-               free(devices);
                if (auto_assem)
                        goto try_again;
                return 1;
@@ -566,6 +696,8 @@ int Assemble(struct supertype *st, char *mddev,
        /* Ok, no bad inconsistancy, we can try updating etc */
        bitmap_done = 0;
        content->update_private = NULL;
+       devices = malloc(num_devs * sizeof(*devices));
+       devmap = calloc(num_devs * content->array.raid_disks, 1);
        for (tmpdev = devlist; tmpdev; tmpdev=tmpdev->next) if (tmpdev->used == 1) {
                char *devname = tmpdev->devname;
                struct stat stb;
@@ -576,6 +708,7 @@ int Assemble(struct supertype *st, char *mddev,
                        /* prepare useful information in info structures */
                        struct stat stb2;
                        struct supertype *tst;
+                       int err;
                        fstat(mdfd, &stb2);
 
                        if (strcmp(update, "uuid")==0 &&
@@ -592,32 +725,52 @@ int Assemble(struct supertype *st, char *mddev,
                        }
                        dfd = dev_open(devname, O_RDWR|O_EXCL);
 
-                       remove_partitions(dfd);
-
                        tst = dup_super(st);
-                       tst->ss->load_super(tst, dfd, NULL);
-                       tst->ss->getinfo_super(tst, content);
+                       if (dfd < 0 || tst->ss->load_super(tst, dfd, NULL) != 0) {
+                               fprintf(stderr, Name ": cannot re-read metadata from %s - aborting\n",
+                                       devname);
+                               if (dfd >= 0)
+                                       close(dfd);
+                               close(mdfd);
+                               free(devices);
+                               free(devmap);
+                               return 1;
+                       }
+                       tst->ss->getinfo_super(tst, content, devmap + devcnt * content->array.raid_disks);
 
                        memcpy(content->uuid, ident->uuid, 16);
                        strcpy(content->name, ident->name);
                        content->array.md_minor = minor(stb2.st_rdev);
 
-                       tst->ss->update_super(tst, content, update,
-                                             devname, verbose,
-                                             ident->uuid_set, homehost);
+                       if (strcmp(update, "byteorder") == 0)
+                               err = 0;
+                       else
+                               err = tst->ss->update_super(tst, content, update,
+                                                           devname, verbose,
+                                                           ident->uuid_set,
+                                                           homehost);
+                       if (err < 0) {
+                               fprintf(stderr,
+                                       Name ": --update=%s not understood"
+                                       " for %s metadata\n",
+                                       update, tst->ss->name);
+                               tst->ss->free_super(tst);
+                               free(tst);
+                               close(mdfd);
+                               close(dfd);
+                               free(devices);
+                               free(devmap);
+                               return 1;
+                       }
                        if (strcmp(update, "uuid")==0 &&
                            !ident->uuid_set) {
                                ident->uuid_set = 1;
                                memcpy(ident->uuid, content->uuid, 16);
                        }
-                       if (dfd < 0)
-                               fprintf(stderr, Name ": Cannot open %s for superblock update\n",
-                                       devname);
-                       else if (tst->ss->store_super(tst, dfd))
+                       if (tst->ss->store_super(tst, dfd))
                                fprintf(stderr, Name ": Could not re-write superblock on %s.\n",
                                        devname);
-                       if (dfd >= 0)
-                               close(dfd);
+                       close(dfd);
 
                        if (strcmp(update, "uuid")==0 &&
                            ident->bitmap_fd >= 0 && !bitmap_done) {
@@ -636,10 +789,17 @@ int Assemble(struct supertype *st, char *mddev,
                        int dfd;
                        dfd = dev_open(devname, O_RDWR|O_EXCL);
 
-                       remove_partitions(dfd);
-
-                       tst->ss->load_super(tst, dfd, NULL);
-                       tst->ss->getinfo_super(tst, content);
+                       if (dfd < 0 || tst->ss->load_super(tst, dfd, NULL) != 0) {
+                               fprintf(stderr, Name ": cannot re-read metadata from %s - aborting\n",
+                                       devname);
+                               if (dfd >= 0)
+                                       close(dfd);
+                               close(mdfd);
+                               free(devices);
+                               free(devmap);
+                               return 1;
+                       }
+                       tst->ss->getinfo_super(tst, content, devmap + devcnt * content->array.raid_disks);
                        tst->ss->free_super(tst);
                        close(dfd);
                }
@@ -659,7 +819,7 @@ int Assemble(struct supertype *st, char *mddev,
                            > devices[most_recent].i.events)
                                most_recent = devcnt;
                }
-               if (content->array.level == -4)
+               if (content->array.level == LEVEL_MULTIPATH)
                        /* with multipath, the raid_disk from the superblock is meaningless */
                        i = devcnt;
                else
@@ -675,9 +835,9 @@ int Assemble(struct supertype *st, char *mddev,
                }
                if (i < 10000) {
                        if (i >= bestcnt) {
-                               unsigned int newbestcnt = i+10;
+                               int newbestcnt = i+10;
                                int *newbest = malloc(sizeof(int)*newbestcnt);
-                               unsigned int c;
+                               int c;
                                for (c=0; c < newbestcnt; c++)
                                        if (c < bestcnt)
                                                newbest[c] = best[c];
@@ -709,6 +869,8 @@ int Assemble(struct supertype *st, char *mddev,
                                           "the\n      DEVICE list in mdadm.conf"
                                        );
                                close(mdfd);
+                               free(devices);
+                               free(devmap);
                                return 1;
                        }
                        if (best[i] == -1
@@ -727,13 +889,15 @@ int Assemble(struct supertype *st, char *mddev,
                if (st)
                        st->ss->free_super(st);
                close(mdfd);
+               free(devices);
+               free(devmap);
                return 1;
        }
 
        if (update && strcmp(update, "byteorder")==0)
                st->minor_version = 90;
 
-       st->ss->getinfo_super(st, content);
+       st->ss->getinfo_super(st, content, NULL);
        clean = content->array.state & 1;
 
        /* now we have some devices that might be suitable.
@@ -743,7 +907,8 @@ int Assemble(struct supertype *st, char *mddev,
        memset(avail, 0, content->array.raid_disks);
        okcnt = 0;
        sparecnt=0;
-       for (i=0; i< bestcnt ;i++) {
+       rebuilding_cnt=0;
+       for (i=0; i< bestcnt; i++) {
                int j = best[i];
                int event_margin = 1; /* always allow a difference of '1'
                                       * like the kernel does
@@ -752,23 +917,44 @@ int Assemble(struct supertype *st, char *mddev,
                /* note: we ignore error flags in multipath arrays
                 * as they don't make sense
                 */
-               if (content->array.level != -4)
-                       if (!(devices[j].i.disk.state & (1<<MD_DISK_SYNC))) {
+               if (content->array.level != LEVEL_MULTIPATH)
+                       if (!(devices[j].i.disk.state & (1<<MD_DISK_ACTIVE))) {
                                if (!(devices[j].i.disk.state
-                                     & (1<<MD_DISK_FAULTY)))
+                                     & (1<<MD_DISK_FAULTY))) {
+                                       devices[j].uptodate = 1;
                                        sparecnt++;
+                               }
                                continue;
                        }
+               /* If this devices thinks that 'most_recent' has failed, then
+                * we must reject this device.
+                */
+               if (j != most_recent &&
+                   content->array.raid_disks > 0 &&
+                   devices[most_recent].i.disk.raid_disk >= 0 &&
+                   devmap[j * content->array.raid_disks + devices[most_recent].i.disk.raid_disk] == 0) {
+                       if (verbose > -1)
+                               fprintf(stderr, Name ": ignoring %s as it reports %s as failed\n",
+                                       devices[j].devname, devices[most_recent].devname);
+                       best[i] = -1;
+                       continue;
+               }
                if (devices[j].i.events+event_margin >=
                    devices[most_recent].i.events) {
                        devices[j].uptodate = 1;
                        if (i < content->array.raid_disks) {
-                               okcnt++;
-                               avail[i]=1;
+                               if (devices[j].i.recovery_start == MaxSector ||
+                                   (content->reshape_active &&
+                                    j >= content->array.raid_disks - content->delta_disks)) {
+                                       okcnt++;
+                                       avail[i]=1;
+                               } else
+                                       rebuilding_cnt++;
                        } else
                                sparecnt++;
                }
        }
+       free(devmap);
        while (force && !enough(content->array.level, content->array.raid_disks,
                                content->array.layout, 1,
                                avail, okcnt)) {
@@ -778,12 +964,13 @@ int Assemble(struct supertype *st, char *mddev,
                 */
                int fd;
                struct supertype *tst;
-               long long current_events;
+               unsigned long long current_events;
                chosen_drive = -1;
-               for (i=0; i<content->array.raid_disks && i < bestcnt; i++) {
+               for (i = 0; i < content->array.raid_disks && i < bestcnt; i++) {
                        int j = best[i];
                        if (j>=0 &&
                            !devices[j].uptodate &&
+                           devices[j].i.recovery_start == MaxSector &&
                            (chosen_drive < 0 ||
                             devices[j].i.events
                             > devices[chosen_drive].i.events))
@@ -837,7 +1024,7 @@ int Assemble(struct supertype *st, char *mddev,
                /* If there are any other drives of the same vintage,
                 * add them in as well.  We can't lose and we might gain
                 */
-               for (i=0; i<content->array.raid_disks && i < bestcnt ; i++) {
+               for (i = 0; i < content->array.raid_disks && i < bestcnt ; i++) {
                        int j = best[i];
                        if (j >= 0 &&
                            !devices[j].uptodate &&
@@ -869,6 +1056,7 @@ int Assemble(struct supertype *st, char *mddev,
                        fprintf(stderr, Name ": Cannot open %s: %s\n",
                                devices[j].devname, strerror(errno));
                        close(mdfd);
+                       free(devices);
                        return 1;
                }
                if (st->ss->load_super(st,fd, NULL)) {
@@ -876,6 +1064,7 @@ int Assemble(struct supertype *st, char *mddev,
                        fprintf(stderr, Name ": RAID superblock has disappeared from %s\n",
                                devices[j].devname);
                        close(mdfd);
+                       free(devices);
                        return 1;
                }
                close(fd);
@@ -883,9 +1072,10 @@ int Assemble(struct supertype *st, char *mddev,
        if (st->sb == NULL) {
                fprintf(stderr, Name ": No suitable drives found for %s\n", mddev);
                close(mdfd);
+               free(devices);
                return 1;
        }
-       st->ss->getinfo_super(st, content);
+       st->ss->getinfo_super(st, content, NULL);
 #ifndef MDASSEMBLE
        sysfs_init(content, mdfd, 0);
 #endif
@@ -946,6 +1136,7 @@ int Assemble(struct supertype *st, char *mddev,
                        fprintf(stderr, Name ": Could not open %s for write - cannot Assemble array.\n",
                                devices[chosen_drive].devname);
                        close(mdfd);
+                       free(devices);
                        return 1;
                }
                if (st->ss->store_super(st, fd)) {
@@ -953,6 +1144,7 @@ int Assemble(struct supertype *st, char *mddev,
                        fprintf(stderr, Name ": Could not re-write superblock on %s\n",
                                devices[chosen_drive].devname);
                        close(mdfd);
+                       free(devices);
                        return 1;
                }
                close(fd);
@@ -966,6 +1158,10 @@ int Assemble(struct supertype *st, char *mddev,
        if (content->reshape_active) {
                int err = 0;
                int *fdlist = malloc(sizeof(int)* bestcnt);
+               if (verbose > 0)
+                       fprintf(stderr, Name ":%s has an active reshape - checking "
+                               "if critical section needs to be restored\n",
+                               chosen_name);
                for (i=0; i<bestcnt; i++) {
                        int j = best[i];
                        if (j >= 0) {
@@ -979,15 +1175,26 @@ int Assemble(struct supertype *st, char *mddev,
                        } else
                                fdlist[i] = -1;
                }
-               if (!err)
-                       err = Grow_restart(st, content, fdlist, bestcnt, backup_file);
+               if (!err) {
+                       err = Grow_restart(st, content, fdlist, bestcnt,
+                                          backup_file, verbose > 0);
+                       if (err && invalid_backup) {
+                               if (verbose > 0)
+                                       fprintf(stderr, Name ": continuing"
+                                               " without restoring backup\n");
+                               err = 0;
+                       }
+               }
                while (i>0) {
                        i--;
                        if (fdlist[i]>=0) close(fdlist[i]);
                }
                if (err) {
                        fprintf(stderr, Name ": Failed to restore critical section for reshape, sorry.\n");
+                       if (backup_file == NULL)
+                               fprintf(stderr,"      Possibly you needed to specify the --backup-file\n");
                        close(mdfd);
+                       free(devices);
                        return err;
                }
        }
@@ -1011,13 +1218,17 @@ int Assemble(struct supertype *st, char *mddev,
                if (rv) {
                        fprintf(stderr, Name ": failed to set array info for %s: %s\n",
                                mddev, strerror(errno));
+                       ioctl(mdfd, STOP_ARRAY, NULL);
                        close(mdfd);
+                       free(devices);
                        return 1;
                }
                if (ident->bitmap_fd >= 0) {
                        if (ioctl(mdfd, SET_BITMAP_FILE, ident->bitmap_fd) != 0) {
                                fprintf(stderr, Name ": SET_BITMAP_FILE failed.\n");
+                               ioctl(mdfd, STOP_ARRAY, NULL);
                                close(mdfd);
+                               free(devices);
                                return 1;
                        }
                } else if (ident->bitmap_file) {
@@ -1026,13 +1237,17 @@ int Assemble(struct supertype *st, char *mddev,
                        if (bmfd < 0) {
                                fprintf(stderr, Name ": Could not open bitmap file %s\n",
                                        ident->bitmap_file);
+                               ioctl(mdfd, STOP_ARRAY, NULL);
                                close(mdfd);
+                               free(devices);
                                return 1;
                        }
                        if (ioctl(mdfd, SET_BITMAP_FILE, bmfd) != 0) {
                                fprintf(stderr, Name ": Failed to set bitmapfile for %s\n", mddev);
                                close(bmfd);
+                               ioctl(mdfd, STOP_ARRAY, NULL);
                                close(mdfd);
+                               free(devices);
                                return 1;
                        }
                        close(bmfd);
@@ -1049,6 +1264,12 @@ int Assemble(struct supertype *st, char *mddev,
                                j = chosen_drive;
 
                        if (j >= 0 /* && devices[j].uptodate */) {
+                               int dfd = dev_open(devices[j].devname,
+                                                  O_RDWR|O_EXCL);
+                               if (dfd >= 0) {
+                                       remove_partitions(dfd);
+                                       close(dfd);
+                               }
                                rv = add_disk(mdfd, st, content, &devices[j].i);
 
                                if (rv) {
@@ -1078,14 +1299,16 @@ int Assemble(struct supertype *st, char *mddev,
                                fprintf(stderr, Name ": Container %s has been "
                                        "assembled with %d drive%s",
                                        mddev, okcnt+sparecnt, okcnt+sparecnt==1?"":"s");
-                               if (okcnt < content->array.raid_disks)
+                               if (okcnt < (unsigned)content->array.raid_disks)
                                        fprintf(stderr, " (out of %d)",
                                                content->array.raid_disks);
                                fprintf(stderr, "\n");
                        }
+                       st->ss->free_super(st);
                        sysfs_uevent(content, "change");
                        wait_for(chosen_name, mdfd);
                        close(mdfd);
+                       free(devices);
                        return 0;
                }
 
@@ -1093,14 +1316,29 @@ int Assemble(struct supertype *st, char *mddev,
                    (runstop <= 0 &&
                     ( enough(content->array.level, content->array.raid_disks,
                              content->array.layout, clean, avail, okcnt) &&
-                      (okcnt >= req_cnt || start_partial_ok)
+                      (okcnt + rebuilding_cnt >= req_cnt || start_partial_ok)
                             ))) {
-                       if (ioctl(mdfd, RUN_ARRAY, NULL)==0) {
+                       /* This array is good-to-go.
+                        * If a reshape is in progress then we might need to
+                        * continue monitoring it.  In that case we start
+                        * it read-only and let the grow code make it writable.
+                        */
+                       int rv;
+#ifndef MDASSEMBLE
+                       if (content->reshape_active &&
+                           content->delta_disks <= 0)
+                               rv = Grow_continue(mdfd, st, content, backup_file);
+                       else
+#endif
+                               rv = ioctl(mdfd, RUN_ARRAY, NULL);
+                       if (rv == 0) {
                                if (verbose >= 0) {
                                        fprintf(stderr, Name ": %s has been started with %d drive%s",
                                                mddev, okcnt, okcnt==1?"":"s");
-                                       if (okcnt < content->array.raid_disks)
+                                       if (okcnt < (unsigned)content->array.raid_disks)
                                                fprintf(stderr, " (out of %d)", content->array.raid_disks);
+                                       if (rebuilding_cnt)
+                                               fprintf(stderr, "%s %d rebuilding", sparecnt?",":" and", rebuilding_cnt);
                                        if (sparecnt)
                                                fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s");
                                        fprintf(stderr, ".\n");
@@ -1119,6 +1357,29 @@ int Assemble(struct supertype *st, char *mddev,
                                                                      (4 * content->array.chunk_size / 4096) + 1);
                                        }
                                }
+                               if (okcnt < (unsigned)content->array.raid_disks) {
+                                       /* If any devices did not get added
+                                        * because the kernel rejected them based
+                                        * on event count, try adding them
+                                        * again providing the action policy is
+                                        * 're-add' or greater.  The bitmap
+                                        * might allow them to be included, or
+                                        * they will become spares.
+                                        */
+                                       for (i = 0; i <= bestcnt; i++) {
+                                               int j = best[i];
+                                               if (j >= 0 && !devices[j].uptodate) {
+                                                       if (!disk_action_allows(&devices[j].i, st->ss->name, act_re_add))
+                                                               continue;
+                                                       rv = add_disk(mdfd, st, content,
+                                                                     &devices[j].i);
+                                                       if (rv == 0 && verbose >= 0)
+                                                               fprintf(stderr,
+                                                                       Name ": %s has been re-added.\n",
+                                                                       devices[j].devname);
+                                               }
+                                       }
+                               }
                                wait_for(mddev, mdfd);
                                close(mdfd);
                                if (auto_assem) {
@@ -1147,6 +1408,7 @@ int Assemble(struct supertype *st, char *mddev,
                                                usecs <<= 1;
                                        }
                                }
+                               free(devices);
                                return 0;
                        }
                        fprintf(stderr, Name ": failed to RUN_ARRAY %s: %s\n",
@@ -1167,19 +1429,23 @@ int Assemble(struct supertype *st, char *mddev,
                        if (auto_assem)
                                ioctl(mdfd, STOP_ARRAY, NULL);
                        close(mdfd);
+                       free(devices);
                        return 1;
                }
                if (runstop == -1) {
                        fprintf(stderr, Name ": %s assembled from %d drive%s",
                                mddev, okcnt, okcnt==1?"":"s");
-                       if (okcnt != content->array.raid_disks)
+                       if (okcnt != (unsigned)content->array.raid_disks)
                                fprintf(stderr, " (out of %d)", content->array.raid_disks);
                        fprintf(stderr, ", but not started.\n");
                        close(mdfd);
+                       free(devices);
                        return 0;
                }
                if (verbose >= -1) {
                        fprintf(stderr, Name ": %s assembled from %d drive%s", mddev, okcnt, okcnt==1?"":"s");
+                       if (rebuilding_cnt)
+                               fprintf(stderr, "%s %d rebuilding", sparecnt?", ":" and ", rebuilding_cnt);
                        if (sparecnt)
                                fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s");
                        if (!enough(content->array.level, content->array.raid_disks,
@@ -1193,7 +1459,7 @@ int Assemble(struct supertype *st, char *mddev,
                                        "array while not clean - consider "
                                        "--force.\n");
                        else {
-                               if (req_cnt == content->array.raid_disks)
+                               if (req_cnt == (unsigned)content->array.raid_disks)
                                        fprintf(stderr, " - need all %d to start it", req_cnt);
                                else
                                        fprintf(stderr, " - need %d of %d to start", req_cnt, content->array.raid_disks);
@@ -1203,6 +1469,7 @@ int Assemble(struct supertype *st, char *mddev,
                if (auto_assem)
                        ioctl(mdfd, STOP_ARRAY, NULL);
                close(mdfd);
+               free(devices);
                return 1;
        } else {
                /* The "chosen_drive" is a good choice, and if necessary, the superblock has
@@ -1219,6 +1486,7 @@ int Assemble(struct supertype *st, char *mddev,
 
        }
        close(mdfd);
+       free(devices);
        return 0;
 }