]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - Assemble.c
Assemble: allow members of containers to be assembled and auto-assembled.
[thirdparty/mdadm.git] / Assemble.c
index b09021fca5e881752699e7a089cdfecf6f2630c0..e421f22d2eddd0ddfd0e307b95fdb27a08e98e58 100644 (file)
@@ -50,6 +50,31 @@ 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,
@@ -133,6 +158,7 @@ int Assemble(struct supertype *st, char *mddev,
        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);
@@ -167,57 +193,12 @@ int Assemble(struct supertype *st, char *mddev,
                return 1;
        }
 
-       /* if the configuration specifies a container then we use that to
-        * determine the devices and retrieve the array configuration
-        */
-#ifndef MDASSEMBLE
-       if (ident->container && ident->member) {
-               int cfd = open(ident->container, O_RDWR);
-               struct mdinfo *mdi;
-               struct supertype container;
-
-               if (verbose>0)
-                       fprintf(stderr, Name ": looking to assemble member array %s"
-                               " inside container %s\n", ident->member, ident->container);
-               if (cfd < 0) {
-                       if (verbose>0)
-                               fprintf(stderr, Name ": unable to open container %s: %s\n",
-                                       ident->container, strerror(errno));
-                       return 1;
-               }
-
-               mdi = sysfs_read(cfd, fd2devnum(cfd), GET_VERSION);
-               if (!mdi) {
-                       close(cfd);
-                       if (verbose>0)
-                               fprintf(stderr, Name ": unable to read container %s\n",
-                                       ident->container);
-                       return 1;
-               }
-               container.ss = find_metadata_methods(mdi->text_version);
-               sysfs_free(mdi);
-               if (!container.ss) {
-                       close(cfd);
-                       fprintf(stderr, Name ": %s uses unknown metadata: %s\n",
-                               ident->container, mdi->text_version);
-                       return 1;
-               }
-               if (container.ss->load_super(&container, cfd, ident->container)) {
-                       fprintf(stderr, Name ": Cannot load metadata for container %s\n",
-                               ident->container);
-                       return 1;
-               }
-
-               return Incremental_container(&container, ident->container,
-                                            verbose, runstop, ident->autof,
-                                            LOCAL);
-       }
-#endif
        if (devlist == NULL)
                devlist = conf_get_devs();
        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
@@ -256,14 +237,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;
@@ -277,12 +258,12 @@ 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)
+                       if (report_missmatch)
                                fprintf( stderr, Name ": no RAID superblock on %s\n",
                                         devname);
                } else {
@@ -292,10 +273,66 @@ 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.
+                        */
+               next_member:
+                       if (tmpdev->content)
+                               content = tmpdev->content;
+                       else
+                               content = tst->ss->container_content(tst);
+
+                       tmpdev->content = content->next;
+                       if (tmpdev->content == NULL)
+                               tmpdev->used = 1;
+
+               } 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;
@@ -303,7 +340,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;
@@ -311,7 +348,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;
@@ -319,7 +356,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;
@@ -327,7 +364,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;
@@ -351,6 +388,30 @@ 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 (auto_assem)
+                                       goto loop;
+                               fprintf(stderr, Name ": member %s in %s is already assembled\n",
+                                       content->text_version,
+                                       devname);
+                               tst->ss->free_super(tst);
+                               return 1;
+                       }
+                       st = tst; tst = NULL;
+                       if (!auto_assem && 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;
+                       }
+                       break;
+               }
                if (st == NULL)
                        st = dup_super(tst);
                if (st->minor_version == -1)
@@ -372,13 +433,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)
@@ -399,6 +460,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);
        }
@@ -477,6 +540,13 @@ 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;
        for (tmpdev = devlist; tmpdev; tmpdev=tmpdev->next) if (tmpdev->used == 1) {
@@ -1115,3 +1185,71 @@ 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)
+                       return 1;
+       if (sra)
+               sysfs_free(sra);
+
+       for (dev = content->devs; dev; dev = dev->next)
+               if (sysfs_add_disk(content, dev) == 0)
+                       working++;
+               else if (errno == EEXIST)
+                       preexist++;
+       if (working == 0)
+               /* Nothing new, don't try to start */ ;
+       else if (runstop > 0 ||
+                (working + preexist) >= content->array.working_disks) {
+               switch(content->array.level) {
+               case LEVEL_LINEAR:
+               case LEVEL_MULTIPATH:
+               case 0:
+                       sysfs_set_str(content, NULL, "array_state",
+                                     "active");
+                       break;
+               default:
+                       sysfs_set_str(content, NULL, "array_state",
+                                     "readonly");
+                       /* start mdmon if needed. */
+                       if (!mdmon_running(st->container_dev))
+                               start_mdmon(st->container_dev);
+                       ping_monitor(devnum2devname(st->container_dev));
+                       break;
+               }
+               sysfs_set_safemode(content, content->safe_mode_delay);
+               if (verbose >= 0) {
+                       fprintf(stderr, Name
+                               ": Started %s with %d devices",
+                               chosen_name, working + preexist);
+                       if (preexist)
+                               fprintf(stderr, " (%d new)", working);
+                       fprintf(stderr, "\n");
+               }
+               /* 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);
+       map_update(&map, fd2devnum(mdfd),
+                  content->text_version,
+                  content->uuid, chosen_name);
+
+       return 0;
+}
+#endif
+