]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - Assemble.c
Add mbr pseudo metadata handler.
[thirdparty/mdadm.git] / Assemble.c
index 6b1e8df10e844a4b901a3c0da11ae9be2e655435..23d0b826f4304db1c5453cf09795b4ecf75579fe 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * mdadm - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001-2006 Neil Brown <neilb@suse.de>
+ * Copyright (C) 2001-2009 Neil Brown <neilb@suse.de>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  *    Author: Neil Brown
- *    Email: <neilb@cse.unsw.edu.au>
- *    Paper: Neil Brown
- *           School of Computer Science and Engineering
- *           The University of New South Wales
- *           Sydney, 2052
- *           Australia
+ *    Email: <neilb@suse.de>
  */
 
 #include       "mdadm.h"
@@ -50,11 +45,36 @@ static int name_matches(char *found, char *required, char *homehost)
        return 0;
 }
 
+static int is_member_busy(char *metadata_version)
+{
+       /* check if the given member array is active */
+       struct mdstat_ent *mdstat = mdstat_read(1, 0);
+       struct mdstat_ent *ent;
+       int busy = 0;
+
+       for (ent = mdstat; ent; ent = ent->next) {
+               if (ent->metadata_version == NULL)
+                       continue;
+               if (strncmp(ent->metadata_version, "external:", 9) != 0)
+                       continue;
+               if (!is_subarray(&ent->metadata_version[9]))
+                       continue;
+               /* Skip first char - it can be '/' or '-' */
+               if (strcmp(&ent->metadata_version[10], metadata_version+1) == 0) {
+                       busy = 1;
+                       break;
+               }
+       }
+       free_mdstat(mdstat);
+
+       return busy;
+}
+
 int Assemble(struct supertype *st, char *mddev,
             mddev_ident_t ident,
             mddev_dev_t devlist, char *backup_file,
             int readonly, int runstop,
-            char *update, char *homehost,
+            char *update, char *homehost, int require_homehost,
             int verbose, int force)
 {
        /*
@@ -113,7 +133,9 @@ int Assemble(struct supertype *st, char *mddev,
         */
        int mdfd;
        int clean;
-       int auto_assem = (mddev == NULL);
+       int auto_assem = (mddev == NULL && !ident->uuid_set &&
+                         ident->super_minor == UnSet && ident->name[0] == 0
+                         && (ident->container == NULL || ident->member == NULL));
        int old_linux = 0;
        int vers = vers; /* Keep gcc quite - it really is initialised */
        struct {
@@ -124,15 +146,16 @@ int Assemble(struct supertype *st, char *mddev,
                struct mdinfo i;
        } *devices;
        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;
        int inargv = 0;
+       int report_missmatch;
        int bitmap_done;
        int start_partial_ok = (runstop >= 0) && 
                (force || devlist==NULL || auto_assem);
@@ -140,7 +163,6 @@ int Assemble(struct supertype *st, char *mddev,
        mddev_dev_t tmpdev;
        struct mdinfo info;
        struct mdinfo *content = NULL;
-       mdu_array_info_t tmp_inf;
        char *avail;
        int nextspare = 0;
        char *name = NULL;
@@ -160,7 +182,9 @@ 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) {
                fprintf(stderr, Name ": No identity information available for %s - cannot assemble.\n",
                        mddev ? mddev : "further assembly");
@@ -172,6 +196,7 @@ int Assemble(struct supertype *st, char *mddev,
        else if (mddev)
                inargv = 1;
 
+       report_missmatch = ((inargv && verbose >= 0) || verbose > 0);
  try_again:
        /* We come back here when doing auto-assembly and attempting some
         * set of devices failed.  Those are now marked as ->used==2 and
@@ -210,14 +235,14 @@ int Assemble(struct supertype *st, char *mddev,
 
                if (ident->devices &&
                    !match_oneof(ident->devices, devname)) {
-                       if ((inargv && verbose>=0) || verbose > 0)
+                       if (report_missmatch)
                                fprintf(stderr, Name ": %s is not one of %s\n", devname, ident->devices);
                        continue;
                }
 
                dfd = dev_open(devname, O_RDONLY|O_EXCL);
                if (dfd < 0) {
-                       if ((inargv && verbose >= 0) || verbose > 0)
+                       if (report_missmatch)
                                fprintf(stderr, Name ": cannot open device %s: %s\n",
                                        devname, strerror(errno));
                        tmpdev->used = 2;
@@ -231,14 +256,29 @@ int Assemble(struct supertype *st, char *mddev,
                                devname);
                        tmpdev->used = 2;
                } else if (!tst && (tst = guess_super(dfd)) == NULL) {
-                       if ((inargv && verbose >= 0) || verbose > 0)
+                       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 ((inargv && verbose >= 0) || verbose > 0)
-                               fprintf( stderr, Name ": no RAID superblock on %s\n",
-                                        devname);
+                       if (report_missmatch)
+                               fprintf(stderr, Name ": no RAID superblock on %s\n",
+                                       devname);
+               } else if (tst->ss->compare_super == NULL) {
+                       if (report_missmatch)
+                               fprintf(stderr, Name ": Cannot assemble %s metadata on %s\n",
+                                       tst->ss->name, devname);
+                       tst->ss->free_super(tst);
+                       tmpdev->used = 2;
+               } else if (auto_assem && st == NULL &&
+                          !conf_test_metadata(tst->ss->name,
+                                              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);
+                       tst->ss->free_super(tst);
+                       tmpdev->used = 2;
                } else {
                        content = &info;
                        memset(content, 0, sizeof(*content));
@@ -246,10 +286,71 @@ int Assemble(struct supertype *st, char *mddev,
                }
                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
+                        */
+                       if (st) {
+                               /* already found some components, this cannot
+                                * be another one.
+                                */
+                               if (report_missmatch)
+                                       fprintf(stderr, Name ": %s is a container, but we are looking for components\n",
+                                               devname);
+                               goto loop;
+                       }
+
+                       if (ident->container) {
+                               if (ident->container[0] == '/' &&
+                                   !same_dev(ident->container, devname)) {
+                                       if (report_missmatch)
+                                               fprintf(stderr, Name ": %s is not the container required (%s)\n",
+                                                       devname, ident->container);
+                                       goto loop;
+                               }
+                               if (ident->container[0] != '/') {
+                                       /* we have a uuid */
+                                       int uuid[4];
+                                       if (!parse_uuid(ident->container, uuid) ||
+                                           !same_uuid(content->uuid, uuid, tst->ss->swapuuid)) {
+                                               if (report_missmatch)
+                                                       fprintf(stderr, Name ": %s has wrong UUID to be required container\n",
+                                                               devname);
+                                               goto loop;
+                                       }
+                               }
+                       }
+                       /* It is worth looking inside this container.
+                        */
+                       if (verbose > 0)
+                               fprintf(stderr, Name ": looking in container %s\n",
+                                       devname);
+               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",
+                                       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 ((inargv && verbose >= 0) || verbose > 0)
+                       if (report_missmatch)
                                fprintf(stderr, Name ": %s has wrong uuid.\n",
                                        devname);
                        goto loop;
@@ -257,7 +358,7 @@ int Assemble(struct supertype *st, char *mddev,
                if (ident->name[0] && (!update || strcmp(update, "name")!= 0) &&
                    (!tst || !tst->sb ||
                     name_matches(content->name, ident->name, homehost)==0)) {
-                       if ((inargv && verbose >= 0) || verbose > 0)
+                       if (report_missmatch)
                                fprintf(stderr, Name ": %s has wrong name.\n",
                                        devname);
                        goto loop;
@@ -265,7 +366,7 @@ int Assemble(struct supertype *st, char *mddev,
                if (ident->super_minor != UnSet &&
                    (!tst || !tst->sb ||
                     ident->super_minor != content->array.md_minor)) {
-                       if ((inargv && verbose >= 0) || verbose > 0)
+                       if (report_missmatch)
                                fprintf(stderr, Name ": %s has wrong super-minor.\n",
                                        devname);
                        goto loop;
@@ -273,7 +374,7 @@ int Assemble(struct supertype *st, char *mddev,
                if (ident->level != UnSet &&
                    (!tst || !tst->sb ||
                     ident->level != content->array.level)) {
-                       if ((inargv && verbose >= 0) || verbose > 0)
+                       if (report_missmatch)
                                fprintf(stderr, Name ": %s has wrong raid level.\n",
                                        devname);
                        goto loop;
@@ -281,7 +382,7 @@ int Assemble(struct supertype *st, char *mddev,
                if (ident->raid_disks != UnSet &&
                    (!tst || !tst->sb ||
                     ident->raid_disks!= content->array.raid_disks)) {
-                       if ((inargv && verbose >= 0) || verbose > 0)
+                       if (report_missmatch)
                                fprintf(stderr, Name ": %s requires wrong number of drives.\n",
                                        devname);
                        goto loop;
@@ -305,6 +406,54 @@ int Assemble(struct supertype *st, char *mddev,
                        return 1;
                }
 
+               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);
+                       skip:
+                               if (tmpdev->content)
+                                       goto next_member;
+                               tst->ss->free_super(tst);
+                               tst = NULL;
+                               content = NULL;
+                               if (auto_assem)
+                                       goto loop;
+                               return 1;
+                       }
+                       if (ident->member && ident->member[0]) {
+                               char *s = strchr(content->text_version+1, '/');
+                               if (s == NULL) {
+                                       fprintf(stderr, Name ": badly formatted version: %s\n",
+                                               content->text_version);
+                                       goto skip;
+                               }
+                               if (strcmp(ident->member, s+1) != 0) {
+                                       if (report_missmatch)
+                                               fprintf(stderr,
+                                                       Name ": skipping wrong member %s\n",
+                                                       content->text_version);
+                                       goto skip;
+                               }
+                       }
+                       st = tst; tst = 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);
+                               return 1;
+                       }
+                       if (verbose > 0)
+                               fprintf(stderr, Name ": found match on member %s in %s\n",
+                                       content->text_version, devname);
+                       break;
+               }
                if (st == NULL)
                        st = dup_super(tst);
                if (st->minor_version == -1)
@@ -326,13 +475,13 @@ int Assemble(struct supertype *st, char *mddev,
                                    (first == 1 || last == 1)) {
                                        /* We can do something */
                                        if (first) {/* just ignore this one */
-                                               if ((inargv && verbose >= 0) || verbose > 0)
+                                               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 ((inargv && verbose >= 0) || verbose > 0)
+                                               if (report_missmatch)
                                                        fprintf(stderr, Name ": %s overrides previous devices due to good homehost\n",
                                                                devname);
                                                for (td=devlist; td != tmpdev; td=td->next)
@@ -353,6 +502,8 @@ int Assemble(struct supertype *st, char *mddev,
                tmpdev->used = 1;
 
        loop:
+               if (tmpdev->content)
+                       goto next_member;
                if (tst)
                        tst->ss->free_super(tst);
        }
@@ -360,16 +511,14 @@ int Assemble(struct supertype *st, char *mddev,
        if (!st || !st->sb || !content)
                return 2;
 
-       /* Now need to open array the device.  Use create_mddev */
+       /* Now need to open the array device.  Use create_mddev */
        if (content == &info)
                st->ss->getinfo_super(st, content);
 
        trustworthy = FOREIGN;
-       switch (st->ss->match_home(st, homehost)) {
-       case 0:
-               trustworthy = FOREIGN;
-               name = content->name;
-               break;
+       name = content->name;
+       switch (st->ss->match_home(st, homehost)
+               ?: st->ss->match_home(st, "any")) {
        case 1:
                trustworthy = LOCAL;
                name = strchr(content->name, ':');
@@ -378,22 +527,30 @@ int Assemble(struct supertype *st, char *mddev,
                else
                        name = content->name;
                break;
-       case -1:
-               trustworthy = FOREIGN;
-               break;
        }
-       if (!auto_assem && trustworthy == FOREIGN)
-               /* If the array is listed in mdadm or on
+       if (!auto_assem)
+               /* If the array is listed in mdadm.conf or on
                 * command line, then we trust the name
                 * even if the array doesn't look local
                 */
                trustworthy = LOCAL;
 
-       if (content->name[0] == 0 &&
+       if (name[0] == 0 &&
            content->array.level == LEVEL_CONTAINER) {
                name = content->text_version;
                trustworthy = METADATA;
        }
+
+       if (name[0] && trustworthy != LOCAL &&
+           ! require_homehost &&
+           conf_name_is_free(name))
+               trustworthy = LOCAL;
+
+       if (trustworthy == LOCAL &&
+           strchr(name, ':'))
+               /* Ignore 'host:' prefix of name */
+               name = strchr(name, ':')+1;
+
        mdfd = create_mddev(mddev, name, ident->autof, trustworthy,
                            chosen_name);
        if (mdfd < 0) {
@@ -411,7 +568,7 @@ int Assemble(struct supertype *st, char *mddev,
                close(mdfd);
                return 1;
        }
-       if (ioctl(mdfd, GET_ARRAY_INFO, &tmp_inf)==0) {
+       if (mddev_busy(fd2devnum(mdfd))) {
                fprintf(stderr, Name ": %s already active, cannot restart it!\n",
                        mddev);
                for (tmpdev = devlist ;
@@ -431,8 +588,16 @@ int Assemble(struct supertype *st, char *mddev,
        }
        ioctl(mdfd, STOP_ARRAY, NULL); /* just incase it was started but has no content */
 
+#ifndef MDASSEMBLE
+       if (content != &info) {
+               /* This is a member of a container.  Try starting the array. */
+               return assemble_container_content(st, mdfd, content, runstop,
+                                          chosen_name, verbose);
+       }
+#endif
        /* Ok, no bad inconsistancy, we can try updating etc */
        bitmap_done = 0;
+       content->update_private = NULL;
        for (tmpdev = devlist; tmpdev; tmpdev=tmpdev->next) if (tmpdev->used == 1) {
                char *devname = tmpdev->devname;
                struct stat stb;
@@ -462,7 +627,14 @@ int Assemble(struct supertype *st, char *mddev,
                        remove_partitions(dfd);
 
                        tst = dup_super(st);
-                       tst->ss->load_super(tst, dfd, NULL);
+                       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);
+                               return 1;
+                       }
                        tst->ss->getinfo_super(tst, content);
 
                        memcpy(content->uuid, ident->uuid, 16);
@@ -505,7 +677,14 @@ int Assemble(struct supertype *st, char *mddev,
 
                        remove_partitions(dfd);
 
-                       tst->ss->load_super(tst, dfd, NULL);
+                       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);
+                               return 1;
+                       }
                        tst->ss->getinfo_super(tst, content);
                        tst->ss->free_super(tst);
                        close(dfd);
@@ -526,7 +705,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
@@ -542,9 +721,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];
@@ -585,6 +764,8 @@ int Assemble(struct supertype *st, char *mddev,
                }
                devcnt++;
        }
+       free(content->update_private);
+       content->update_private = NULL;
 
        if (devcnt == 0) {
                fprintf(stderr, Name ": no devices found for %s\n",
@@ -608,7 +789,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
@@ -617,8 +799,8 @@ 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)))
                                        sparecnt++;
@@ -628,8 +810,11 @@ int Assemble(struct supertype *st, char *mddev,
                    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) {
+                                       okcnt++;
+                                       avail[i]=1;
+                               } else
+                                       rebuilding_cnt++;
                        } else
                                sparecnt++;
                }
@@ -643,13 +828,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.events > 0 &&
+                           devices[j].i.recovery_start == MaxSector &&
                            (chosen_drive < 0 ||
                             devices[j].i.events
                             > devices[chosen_drive].i.events))
@@ -703,11 +888,10 @@ 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 &&
-                           devices[j].i.events > 0 &&
                            devices[j].i.events == current_events) {
                                chosen_drive = j;
                                goto add_another;
@@ -771,6 +955,8 @@ int Assemble(struct supertype *st, char *mddev,
                        continue;
 
                devices[j].i.disk.state = desired_state;
+               if (!(devices[j].i.array.state & 1))
+                       clean = 0;
 
                if (st->ss->update_super(st, &devices[j].i, "assemble", NULL,
                                         verbose, 0, NULL)) {
@@ -831,6 +1017,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) {
@@ -845,13 +1035,15 @@ int Assemble(struct supertype *st, char *mddev,
                                fdlist[i] = -1;
                }
                if (!err)
-                       err = Grow_restart(st, content, fdlist, bestcnt, backup_file);
+                       err = Grow_restart(st, content, fdlist, bestcnt, backup_file, verbose > 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);
                        return err;
                }
@@ -876,12 +1068,14 @@ 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);
                        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);
                                return 1;
                        }
@@ -891,12 +1085,14 @@ 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);
                                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);
                                return 1;
                        }
@@ -943,12 +1139,13 @@ 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");
                        }
                        sysfs_uevent(content, "change");
+                       wait_for(chosen_name, mdfd);
                        close(mdfd);
                        return 0;
                }
@@ -957,18 +1154,71 @@ 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");
                                }
+                               if (content->reshape_active &&
+                                   content->array.level >= 4 &&
+                                   content->array.level <= 6) {
+                                       /* might need to increase the size
+                                        * of the stripe cache - default is 256
+                                        */
+                                       if (256 < 4 * (content->array.chunk_size/4096)) {
+                                               struct mdinfo *sra = sysfs_read(mdfd, 0, 0);
+                                               if (sra)
+                                                       sysfs_set_num(sra, NULL,
+                                                                     "stripe_cache_size",
+                                                                     (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) {
                                        int usecs = 1;
@@ -1021,7 +1271,7 @@ int Assemble(struct supertype *st, char *mddev,
                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);
@@ -1029,6 +1279,8 @@ int Assemble(struct supertype *st, char *mddev,
                }
                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,
@@ -1042,7 +1294,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);
@@ -1051,6 +1303,7 @@ int Assemble(struct supertype *st, char *mddev,
                }
                if (auto_assem)
                        ioctl(mdfd, STOP_ARRAY, NULL);
+               close(mdfd);
                return 1;
        } else {
                /* The "chosen_drive" is a good choice, and if necessary, the superblock has
@@ -1069,3 +1322,92 @@ int Assemble(struct supertype *st, char *mddev,
        close(mdfd);
        return 0;
 }
+
+#ifndef MDASSEMBLE
+int assemble_container_content(struct supertype *st, int mdfd,
+                              struct mdinfo *content, int runstop,
+                              char *chosen_name, int verbose)
+{
+       struct mdinfo *dev, *sra;
+       int working = 0, preexist = 0;
+       struct map_ent *map = NULL;
+
+       sysfs_init(content, mdfd, 0);
+
+       sra = sysfs_read(mdfd, 0, GET_VERSION);
+       if (sra == NULL || strcmp(sra->text_version, content->text_version) != 0)
+               if (sysfs_set_array(content, md_get_version(mdfd)) != 0) {
+                       close(mdfd);
+                       return 1;
+               }
+       if (sra)
+               sysfs_free(sra);
+
+       for (dev = content->devs; dev; dev = dev->next)
+               if (sysfs_add_disk(content, dev, 1) == 0)
+                       working++;
+               else if (errno == EEXIST)
+                       preexist++;
+       if (working == 0) {
+               close(mdfd);
+               return 1;/* Nothing new, don't try to start */
+       }
+       
+       map_update(&map, fd2devnum(mdfd),
+                  content->text_version,
+                  content->uuid, chosen_name);
+
+       if (runstop > 0 ||
+                (working + preexist) >= content->array.working_disks) {
+               int err;
+
+               switch(content->array.level) {
+               case LEVEL_LINEAR:
+               case LEVEL_MULTIPATH:
+               case 0:
+                       err = sysfs_set_str(content, NULL, "array_state",
+                                           "active");
+                       break;
+               default:
+                       err = sysfs_set_str(content, NULL, "array_state",
+                                     "readonly");
+                       /* start mdmon if needed. */
+                       if (!err) {
+                               if (!mdmon_running(st->container_dev))
+                                       start_mdmon(st->container_dev);
+                               ping_monitor(devnum2devname(st->container_dev));
+                       }
+                       break;
+               }
+               if (!err)
+                       sysfs_set_safemode(content, content->safe_mode_delay);
+               if (verbose >= 0) {
+                       if (err)
+                               fprintf(stderr, Name
+                                       ": array %s now has %d devices",
+                                       chosen_name, working + preexist);
+                       else
+                               fprintf(stderr, Name
+                                       ": Started %s with %d devices",
+                                       chosen_name, working + preexist);
+                       if (preexist)
+                               fprintf(stderr, " (%d new)", working);
+                       fprintf(stderr, "\n");
+               }
+               if (!err)
+                       wait_for(chosen_name, mdfd);
+               close(mdfd);
+               return 0;
+               /* FIXME should have an O_EXCL and wait for read-auto */
+       } else {
+               if (verbose >= 0)
+                       fprintf(stderr, Name
+                               ": %s assembled with %d devices but "
+                               "not started\n",
+                               chosen_name, working);
+               close(mdfd);
+               return 1;
+       }
+}
+#endif
+