]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - Assemble.c
Central calls to ioctl BLKGETSIZE
[thirdparty/mdadm.git] / Assemble.c
index 46ec9bc0bb96e6d24e4d3811d16c3f8db34d95a9..5e4e3941ac735592abcdd1f573140a0867b9bdbe 100644 (file)
@@ -28,6 +28,7 @@
  */
 
 #include       "mdadm.h"
+#include       <ctype.h>
 
 static int name_matches(char *found, char *required, char *homehost)
 {
@@ -50,7 +51,7 @@ static int name_matches(char *found, char *required, char *homehost)
 }
 
 int Assemble(struct supertype *st, char *mddev, int mdfd,
-            mddev_ident_t ident, char *conffile,
+            mddev_ident_t ident,
             mddev_dev_t devlist, char *backup_file,
             int readonly, int runstop,
             char *update, char *homehost,
@@ -110,8 +111,10 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
         *    START_ARRAY
         *
         */
+       int clean = 0;
+       int must_close = 0;
        int old_linux = 0;
-       int vers;
+       int vers = 0; /* Keep gcc quite - it really is initialised */
        void *first_super = NULL, *super = NULL;
        struct {
                char *devname;
@@ -133,7 +136,8 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
        int chosen_drive;
        int change = 0;
        int inargv = 0;
-       int start_partial_ok = (runstop >= 0) && (force || devlist==NULL);
+       int bitmap_done;
+       int start_partial_ok = (runstop >= 0) && (force || devlist==NULL || mdfd < 0);
        unsigned int num_devs;
        mddev_dev_t tmpdev;
        struct mdinfo info;
@@ -179,12 +183,18 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                return 1;
        }
        if (devlist == NULL)
-               devlist = conf_get_devs(conffile);
-       else inargv = 1;
+               devlist = conf_get_devs();
+       else if (mdfd >= 0)
+               inargv = 1;
+
+ try_again:
 
        tmpdev = devlist; num_devs = 0;
        while (tmpdev) {
-               num_devs++;
+               if (tmpdev->used)
+                       tmpdev->used = 2;
+               else
+                       num_devs++;
                tmpdev = tmpdev->next;
        }
        devices = malloc(num_devs * sizeof(*devices));
@@ -207,6 +217,8 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                struct stat stb;
                struct supertype *tst = st;
 
+               if (tmpdev->used > 1) continue;
+
                if (ident->devices &&
                    !match_oneof(ident->devices, devname)) {
                        if ((inargv && verbose>=0) || verbose > 0)
@@ -224,16 +236,21 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                        if ((inargv && verbose >= 0) || verbose > 0)
                                fprintf(stderr, Name ": cannot open device %s: %s\n",
                                        devname, strerror(errno));
+                       tmpdev->used = 2;
                } else if (fstat(dfd, &stb)< 0) {
                        /* Impossible! */
                        fprintf(stderr, Name ": fstat failed for %s: %s\n",
                                devname, strerror(errno));
+                       tmpdev->used = 2;
                } else if ((stb.st_mode & S_IFMT) != S_IFBLK) {
                        fprintf(stderr, Name ": %s is not a block device.\n",
                                devname);
+                       tmpdev->used = 2;
                } else if (!tst && (tst = guess_super(dfd)) == NULL) {
                        if ((inargv && verbose >= 0) || verbose > 0)
-                               fprintf(stderr, Name ": no recogniseable superblock\n");
+                               fprintf(stderr, Name ": no recogniseable superblock on %s\n",
+                                       devname);
+                       tmpdev->used = 2;
                } else if (tst->ss->load_super(tst,dfd, &super, NULL)) {
                        if ((inargv && verbose >= 0) || verbose > 0)
                                fprintf( stderr, Name ": no RAID superblock on %s\n",
@@ -278,7 +295,20 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                                        devname);
                        continue;
                }
-
+               if (mdfd < 0) {
+                       if (tst == NULL || super == NULL)
+                               continue;
+                       if (update == NULL &&
+                           tst->ss->match_home(super, homehost)==0) {
+                               if ((inargv && verbose >= 0) || verbose > 0)
+                                       fprintf(stderr, Name ": %s is not built for host %s.\n",
+                                               devname, homehost);
+                               /* Auto-assemble, and this is not a usable host */
+                               /* if update != NULL, we are updating the host
+                                * name... */
+                               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
@@ -299,8 +329,12 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                    st->minor_version != tst->minor_version ||
                    st->ss->compare_super(&first_super, super) != 0) {
                        /* Some mismatch. If exactly one array matches this host,
-                        * we can resolve on that one
+                        * we can resolve on that one.
+                        * Or, if we are auto assembling, we just ignore the second
+                        * for now.
                         */
+                       if (mdfd < 0)
+                               continue;
                        if (homehost) {
                                int first = st->ss->match_home(first_super, homehost);
                                int last = tst->ss->match_home(super, homehost);
@@ -339,17 +373,53 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                 * We create a name '/dev/md/XXX' based on the info in the
                 * superblock, and call open_mddev on that
                 */
-               asprintf(&mddev, "/dev/md/%s", info.name);
-               mdfd = open_mddev(mddev, 0);
-               if (mdfd < 0)
-                       return mdfd;
+               mdu_array_info_t inf;
+               char *c;
+               if (!first_super) {
+                       return 2;
+               }
+               st->ss->getinfo_super(&info, first_super);
+               c = strchr(info.name, ':');
+               if (c) c++; else c= info.name;
+               if (isdigit(*c) && ((ident->autof & 7)==4 || (ident->autof&7)==6))
+                       /* /dev/md/d0 style for partitionable */
+                       asprintf(&mddev, "/dev/md/d%s", c);
+               else
+                       asprintf(&mddev, "/dev/md/%s", c);
+               mdfd = open_mddev(mddev, ident->autof);
+               if (mdfd < 0) {
+                       free(first_super);
+                       free(devices);
+                       first_super = NULL;
+                       goto try_again;
+               }
+               vers = md_get_version(mdfd);
+               if (ioctl(mdfd, GET_ARRAY_INFO, &inf)==0) {
+                       for (tmpdev = devlist ;
+                            tmpdev && tmpdev->used != 1;
+                            tmpdev = tmpdev->next)
+                               ;
+                       fprintf(stderr, Name ": %s already active, cannot restart it!\n", mddev);
+                       if (tmpdev)
+                               fprintf(stderr, Name ":   %s needed for %s...\n",
+                                       mddev, tmpdev->devname);
+                       close(mdfd);
+                       mdfd = -1;
+                       free(first_super);
+                       free(devices);
+                       first_super = NULL;
+                       goto try_again;
+               }
+               must_close = 1;
        }
 
        /* Ok, no bad inconsistancy, we can try updating etc */
-       for (tmpdev = devlist; tmpdev; tmpdev=tmpdev->next) if (tmpdev->used) {
+       bitmap_done = 0;
+       for (tmpdev = devlist; tmpdev; tmpdev=tmpdev->next) if (tmpdev->used == 1) {
                char *devname = tmpdev->devname;
                struct stat stb;
                /* looks like a good enough match to update the super block if needed */
+#ifndef MDASSEMBLE
                if (update) {
                        int dfd;
                        /* prepare useful information in info structures */
@@ -370,6 +440,8 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                        }
                        dfd = dev_open(devname, O_RDWR|O_EXCL);
 
+                       remove_partitions(dfd);
+
                        if (super) {
                                free(super);
                                super = NULL;
@@ -399,8 +471,28 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                                close(dfd);
 
                        if (strcmp(update, "uuid")==0 &&
-                           ident->bitmap_fd)
-                               bitmap_update_uuid(ident->bitmap_fd, info.uuid);
+                           ident->bitmap_fd >= 0 && !bitmap_done) {
+                               if (bitmap_update_uuid(ident->bitmap_fd, info.uuid, st->ss->swapuuid) != 0)
+                                       fprintf(stderr, Name ": Could not update uuid on external bitmap.\n");
+                               else
+                                       bitmap_done = 1;
+                       }
+               } else
+#endif
+               {
+                       int dfd;
+                       dfd = dev_open(devname, O_RDWR|O_EXCL);
+
+                       remove_partitions(dfd);
+
+                       if (super) {
+                               free(super);
+                               super = NULL;
+                       }
+
+                       st->ss->load_super(st, dfd, &super, NULL);
+                       st->ss->getinfo_super(&info, super);
+                       close(dfd);
                }
 
                stat(devname, &stb);
@@ -432,6 +524,10 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                        if (nextspare < info.array.raid_disks)
                                nextspare = info.array.raid_disks;
                        i = nextspare++;
+               } else {
+                       if (i >= info.array.raid_disks &&
+                           i >= nextspare)
+                               nextspare = i+1;
                }
                if (i < 10000) {
                        if (i >= bestcnt) {
@@ -447,6 +543,28 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                                best = newbest;
                                bestcnt = newbestcnt;
                        }
+                       if (best[i] >=0 &&
+                           devices[best[i]].events == devices[devcnt].events &&
+                           devices[best[i]].minor != devices[devcnt].minor &&
+                           st->ss->major == 0 &&
+                           info.array.level != -4) {
+                               /* two different devices with identical superblock.
+                                * Could be a mis-detection caused by overlapping
+                                * partitions.  fail-safe.
+                                */
+                               fprintf(stderr, Name ": WARNING %s and %s appear"
+                                       " to have very similar superblocks.\n"
+                                       "      If they are really different, "
+                                       "please --zero the superblock on one\n"
+                                       "      If they are the same or overlap,"
+                                       " please remove one from %s.\n",
+                                       devices[best[i]].devname, devname,
+                                       inargv ? "the list" :
+                                          "the\n      DEVICE list in mdadm.conf"
+                                       );
+                               if (must_close) close(mdfd);
+                               return 1;
+                       }
                        if (best[i] == -1
                            || devices[best[i]].events < devices[devcnt].events)
                                best[i] = devcnt;
@@ -465,10 +583,12 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                fprintf(stderr, Name ": no devices found for %s\n",
                        mddev);
                free(first_super);
+               if (must_close) close(mdfd);
                return 1;
        }
 
        st->ss->getinfo_super(&info, first_super);
+       clean = info.array.state & 1;
 
        /* now we have some devices that might be suitable.
         * I wonder how many
@@ -503,13 +623,14 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                }
        }
        while (force && !enough(info.array.level, info.array.raid_disks,
-                               info.array.layout,
+                               info.array.layout, 1,
                                avail, okcnt)) {
                /* Choose the newest best drive which is
                 * not up-to-date, update the superblock
                 * and add it.
                 */
                int fd;
+               long long current_events;
                chosen_drive = -1;
                for (i=0; i<info.array.raid_disks && i < bestcnt; i++) {
                        int j = best[i];
@@ -522,6 +643,8 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                }
                if (chosen_drive < 0)
                        break;
+               current_events = devices[chosen_drive].events;
+       add_another:
                if (verbose >= 0)
                        fprintf(stderr, Name ": forcing event count in %s(%d) from %d upto %d\n",
                                devices[chosen_drive].devname, devices[chosen_drive].raid_disk,
@@ -542,7 +665,9 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                        continue;
                }
                info.events = devices[most_recent].events;
-               st->ss->update_super(&info, super, "force", devices[chosen_drive].devname, verbose, 0, NULL);
+               st->ss->update_super(&info, super, "force-one",
+                                    devices[chosen_drive].devname, verbose,
+                                    0, NULL);
 
                if (st->ss->store_super(st, fd, super)) {
                        close(fd);
@@ -558,6 +683,20 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                avail[chosen_drive] = 1;
                okcnt++;
                free(super);
+
+               /* 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<info.array.raid_disks && i < bestcnt ; i++) {
+                       int j = best[i];
+                       if (j >= 0 &&
+                           !devices[j].uptodate &&
+                           devices[j].events > 0 &&
+                           devices[j].events == current_events) {
+                               chosen_drive = j;
+                               goto add_another;
+                       }
+               }
        }
 
        /* Now we want to look at the superblock which the kernel will base things on
@@ -580,18 +719,21 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                if ((fd=dev_open(devices[j].devname, O_RDONLY|O_EXCL))< 0) {
                        fprintf(stderr, Name ": Cannot open %s: %s\n",
                                devices[j].devname, strerror(errno));
+                       if (must_close) close(mdfd);
                        return 1;
                }
                if (st->ss->load_super(st,fd, &super, NULL)) {
                        close(fd);
                        fprintf(stderr, Name ": RAID superblock has disappeared from %s\n",
                                devices[j].devname);
+                       if (must_close) close(mdfd);
                        return 1;
                }
                close(fd);
        }
        if (super == NULL) {
                fprintf(stderr, Name ": No suitable drives found for %s\n", mddev);
+               if (must_close) close(mdfd);
                return 1;
        }
        st->ss->getinfo_super(&info, super);
@@ -635,10 +777,14 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                }
 #endif
        }
-       if (force && okcnt == info.array.raid_disks-1) {
-               /* FIXME check event count */
-               change += st->ss->update_super(&info, super, "force",
-                                       devices[chosen_drive].devname, verbose, 0, NULL);
+       if (force && !clean &&
+           !enough(info.array.level, info.array.raid_disks,
+                   info.array.layout, clean,
+                   avail, okcnt)) {
+               change += st->ss->update_super(&info, super, "force-array",
+                                       devices[chosen_drive].devname, verbose,
+                                              0, NULL);
+               clean = 1;
        }
 
        if (change) {
@@ -647,12 +793,14 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                if (fd < 0) {
                        fprintf(stderr, Name ": Could not open %s for write - cannot Assemble array.\n",
                                devices[chosen_drive].devname);
+                       if (must_close) close(mdfd);
                        return 1;
                }
                if (st->ss->store_super(st, fd, super)) {
                        close(fd);
                        fprintf(stderr, Name ": Could not re-write superblock on %s\n",
                                devices[chosen_drive].devname);
+                       if (must_close) close(mdfd);
                        return 1;
                }
                close(fd);
@@ -687,6 +835,7 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                }
                if (err) {
                        fprintf(stderr, Name ": Failed to restore critical section for reshape, sorry.\n");
+                       if (must_close) close(mdfd);
                        return err;
                }
        }
@@ -711,11 +860,13 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                if (rv) {
                        fprintf(stderr, Name ": SET_ARRAY_INFO failed for %s: %s\n",
                                mddev, strerror(errno));
+                       if (must_close) 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");
+                               if (must_close) close(mdfd);
                                return 1;
                        }
                } else if (ident->bitmap_file) {
@@ -724,11 +875,13 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                        if (bmfd < 0) {
                                fprintf(stderr, Name ": Could not open bitmap file %s\n",
                                        ident->bitmap_file);
+                               if (must_close) 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);
+                               if (must_close) close(mdfd);
                                return 1;
                        }
                        close(bmfd);
@@ -768,7 +921,8 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                
                if (runstop == 1 ||
                    (runstop <= 0 &&
-                    ( enough(info.array.level, info.array.raid_disks, info.array.layout, avail, okcnt) &&
+                    ( enough(info.array.level, info.array.raid_disks,
+                             info.array.layout, clean, avail, okcnt) &&
                       (okcnt >= req_cnt || start_partial_ok)
                             ))) {
                        if (ioctl(mdfd, RUN_ARRAY, NULL)==0) {
@@ -781,10 +935,50 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                                                fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s");
                                        fprintf(stderr, ".\n");
                                }
+                               if (must_close) {
+                                       int usecs = 1;
+                                       close(mdfd);
+                                       /* There is a nasty race with 'mdadm --monitor'.
+                                        * If it opens this device before we close it,
+                                        * it gets an incomplete open on which IO
+                                        * doesn't work and the capacity if wrong.
+                                        * If we reopen (to check for layered devices)
+                                        * before --monitor closes, we loose.
+                                        *
+                                        * So: wait upto 1 second for there to be
+                                        * a non-zero capacity.
+                                        */
+                                       while (usecs < 1000) {
+                                               mdfd = open(mddev, O_RDONLY);
+                                               if (mdfd >= 0) {
+                                                       unsigned long long size;
+                                                       if (get_dev_size(mdfd, NULL, &size) &&
+                                                           size > 0)
+                                                               break;
+                                                       close(mdfd);
+                                               }
+                                               usleep(usecs);
+                                               usecs <<= 1;
+                                       }
+                               }
                                return 0;
                        }
                        fprintf(stderr, Name ": failed to RUN_ARRAY %s: %s\n",
                                mddev, strerror(errno));
+
+                       if (!enough(info.array.level, info.array.raid_disks,
+                                   info.array.layout, 1, avail, okcnt))
+                               fprintf(stderr, Name ": Not enough devices to "
+                                       "start the array.\n");
+                       else if (!enough(info.array.level,
+                                        info.array.raid_disks,
+                                        info.array.layout, clean,
+                                        avail, okcnt))
+                               fprintf(stderr, Name ": Not enough devices to "
+                                       "start the array while not clean "
+                                       "- consider --force.\n");
+
+                       if (must_close) close(mdfd);
                        return 1;
                }
                if (runstop == -1) {
@@ -793,14 +987,23 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                        if (okcnt != info.array.raid_disks)
                                fprintf(stderr, " (out of %d)", info.array.raid_disks);
                        fprintf(stderr, ", but not started.\n");
+                       if (must_close) close(mdfd);
                        return 0;
                }
                if (verbose >= 0) {
                        fprintf(stderr, Name ": %s assembled from %d drive%s", mddev, okcnt, okcnt==1?"":"s");
                        if (sparecnt)
                                fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s");
-                       if (!enough(info.array.level, info.array.raid_disks, info.array.layout, avail, okcnt))
+                       if (!enough(info.array.level, info.array.raid_disks,
+                                   info.array.layout, 1, avail, okcnt))
                                fprintf(stderr, " - not enough to start the array.\n");
+                       else if (!enough(info.array.level,
+                                        info.array.raid_disks,
+                                        info.array.layout, clean,
+                                        avail, okcnt))
+                               fprintf(stderr, " - not enough to start the "
+                                       "array while not clean - consider "
+                                       "--force.\n");
                        else {
                                if (req_cnt == info.array.raid_disks)
                                        fprintf(stderr, " - need all %d to start it", req_cnt);
@@ -809,6 +1012,7 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                                fprintf(stderr, " (use --run to insist).\n");
                        }
                }
+               if (must_close) close(mdfd);
                return 1;
        } else {
                /* The "chosen_drive" is a good choice, and if necessary, the superblock has
@@ -824,5 +1028,6 @@ int Assemble(struct supertype *st, char *mddev, int mdfd,
                }
                
        }
+       if (must_close) close(mdfd);
        return 0;
 }