]> git.ipfire.org Git - thirdparty/mdadm.git/commitdiff
Merge branch 'subarray' into for-neil
authorDan Williams <dan.j.williams@intel.com>
Fri, 2 Jul 2010 00:36:05 +0000 (17:36 -0700)
committerDan Williams <dan.j.williams@intel.com>
Fri, 2 Jul 2010 00:36:05 +0000 (17:36 -0700)
Conflicts:
mdadm.h
super-intel.c

Grow.c
Kill.c
Manage.c
ReadMe.c
mdadm.8
mdadm.c
mdadm.h
mdmon.c
super-ddf.c
super-intel.c
util.c

diff --git a/Grow.c b/Grow.c
index 053a372ac3b526f7a5b5cccce6955f02735914ea..0916c5de236e5260827b812509a64181df7c8942 100644 (file)
--- a/Grow.c
+++ b/Grow.c
@@ -931,8 +931,8 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                 * old stripes and a whole number of new stripes.
                 * So LCM for (chunksize*datadisks).
                 */
-               a = ochunk/512 * odata;
-               b = nchunk/512 * ndata;
+               a = (ochunk/512) * odata;
+               b = (nchunk/512) * ndata;
                /* Find GCD */
                while (a != b) {
                        if (a < b)
@@ -941,7 +941,7 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                                a -= b;
                }
                /* LCM == product / GCD */
-               blocks = ochunk/512 * nchunk/512 * odata * ndata / a;
+               blocks = (ochunk/512) * (nchunk/512) * odata * ndata / a;
 
                sysfs_free(sra);
                sra = sysfs_read(fd, 0,
@@ -1284,7 +1284,7 @@ int grow_backup(struct mdinfo *sra,
                odata--;
        if (level == 6)
                odata--;
-       sysfs_set_num(sra, NULL, "suspend_hi", (offset + stripes * chunk/512) * odata);
+       sysfs_set_num(sra, NULL, "suspend_hi", (offset + stripes * (chunk/512)) * odata);
        /* Check that array hasn't become degraded, else we might backup the wrong data */
        sysfs_get_ll(sra, NULL, "degraded", &new_degraded);
        if (new_degraded != *degraded) {
@@ -1312,10 +1312,10 @@ int grow_backup(struct mdinfo *sra,
        }
        if (part) {
                bsb.arraystart2 = __cpu_to_le64(offset * odata);
-               bsb.length2 = __cpu_to_le64(stripes * chunk/512 * odata);
+               bsb.length2 = __cpu_to_le64(stripes * (chunk/512) * odata);
        } else {
                bsb.arraystart = __cpu_to_le64(offset * odata);
-               bsb.length = __cpu_to_le64(stripes * chunk/512 * odata);
+               bsb.length = __cpu_to_le64(stripes * (chunk/512) * odata);
        }
        if (part)
                bsb.magic[15] = '2';
@@ -1540,10 +1540,10 @@ static int child_grow(int afd, struct mdinfo *sra, unsigned long stripes,
                    dests, destfd, destoffsets,
                    0, &degraded, buf);
        validate(afd, destfd[0], destoffsets[0]);
-       wait_backup(sra, 0, stripes * chunk / 512, stripes * chunk / 512,
+       wait_backup(sra, 0, stripes * (chunk / 512), stripes * (chunk / 512),
                    dests, destfd, destoffsets,
                    0);
-       sysfs_set_num(sra, NULL, "suspend_lo", (stripes * chunk/512) * data);
+       sysfs_set_num(sra, NULL, "suspend_lo", (stripes * (chunk/512)) * data);
        free(buf);
        /* FIXME this should probably be numeric */
        sysfs_set_str(sra, NULL, "sync_max", "max");
@@ -1562,12 +1562,12 @@ static int child_shrink(int afd, struct mdinfo *sra, unsigned long stripes,
 
        if (posix_memalign((void**)&buf, 4096, disks * chunk))
                return 0;
-       start = sra->component_size - stripes * chunk/512;
+       start = sra->component_size - stripes * (chunk/512);
        sysfs_set_num(sra, NULL, "sync_max", start);
        sysfs_set_str(sra, NULL, "sync_action", "reshape");
        sysfs_set_num(sra, NULL, "suspend_lo", 0);
        sysfs_set_num(sra, NULL, "suspend_hi", 0);
-       rv = wait_backup(sra, 0, start - stripes * chunk/512, stripes * chunk/512,
+       rv = wait_backup(sra, 0, start - stripes * (chunk/512), stripes * (chunk/512),
                         dests, destfd, destoffsets, 0);
        if (rv < 0)
                return 0;
@@ -1577,9 +1577,9 @@ static int child_shrink(int afd, struct mdinfo *sra, unsigned long stripes,
                    dests, destfd, destoffsets,
                    0, &degraded, buf);
        validate(afd, destfd[0], destoffsets[0]);
-       wait_backup(sra, start, stripes*chunk/512, 0,
+       wait_backup(sra, start, stripes*(chunk/512), 0,
                    dests, destfd, destoffsets, 0);
-       sysfs_set_num(sra, NULL, "suspend_lo", (stripes * chunk/512) * data);
+       sysfs_set_num(sra, NULL, "suspend_lo", (stripes * (chunk/512)) * data);
        free(buf);
        /* FIXME this should probably be numeric */
        sysfs_set_str(sra, NULL, "sync_max", "max");
@@ -1614,7 +1614,7 @@ static int child_same_size(int afd, struct mdinfo *sra, unsigned long stripes,
                    disks, chunk, level, layout,
                    dests, destfd, destoffsets,
                    0, &degraded, buf);
-       grow_backup(sra, (start + stripes) * chunk/512, stripes,
+       grow_backup(sra, (start + stripes) * (chunk/512), stripes,
                    fds, offsets,
                    disks, chunk, level, layout,
                    dests, destfd, destoffsets,
@@ -1624,16 +1624,16 @@ static int child_same_size(int afd, struct mdinfo *sra, unsigned long stripes,
        start += stripes * 2; /* where to read next */
        size = sra->component_size / (chunk/512);
        while (start < size) {
-               if (wait_backup(sra, (start-stripes*2)*chunk/512,
-                               stripes*chunk/512, 0,
+               if (wait_backup(sra, (start-stripes*2)*(chunk/512),
+                               stripes*(chunk/512), 0,
                                dests, destfd, destoffsets,
                                part) < 0)
                        return 0;
-               sysfs_set_num(sra, NULL, "suspend_lo", start*chunk/512 * data);
+               sysfs_set_num(sra, NULL, "suspend_lo", start*(chunk/512) * data);
                if (start + stripes > size)
                        tailstripes = (size - start);
 
-               grow_backup(sra, start*chunk/512, tailstripes,
+               grow_backup(sra, start*(chunk/512), tailstripes,
                            fds, offsets,
                            disks, chunk, level, layout,
                            dests, destfd, destoffsets,
@@ -1642,15 +1642,15 @@ static int child_same_size(int afd, struct mdinfo *sra, unsigned long stripes,
                part = 1 - part;
                validate(afd, destfd[0], destoffsets[0]);
        }
-       if (wait_backup(sra, (start-stripes*2) * chunk/512, stripes * chunk/512, 0,
+       if (wait_backup(sra, (start-stripes*2) * (chunk/512), stripes * (chunk/512), 0,
                        dests, destfd, destoffsets,
                        part) < 0)
                return 0;
-       sysfs_set_num(sra, NULL, "suspend_lo", ((start-stripes)*chunk/512) * data);
-       wait_backup(sra, (start-stripes) * chunk/512, tailstripes * chunk/512, 0,
+       sysfs_set_num(sra, NULL, "suspend_lo", ((start-stripes)*(chunk/512)) * data);
+       wait_backup(sra, (start-stripes) * (chunk/512), tailstripes * (chunk/512), 0,
                    dests, destfd, destoffsets,
                    1-part);
-       sysfs_set_num(sra, NULL, "suspend_lo", (size*chunk/512) * data);
+       sysfs_set_num(sra, NULL, "suspend_lo", (size*(chunk/512)) * data);
        sysfs_set_num(sra, NULL, "sync_speed_min", speed);
        free(buf);
        return 1;
@@ -1991,8 +1991,8 @@ int Grow_continue(int mdfd, struct supertype *st, struct mdinfo *info,
        nchunk = info->new_chunk;
 
 
-       a = ochunk/512 * odata;
-       b = nchunk/512 * ndata;
+       a = (ochunk/512) * odata;
+       b = (nchunk/512) * ndata;
        /* Find GCD */
        while (a != b) {
                if (a < b)
@@ -2001,7 +2001,7 @@ int Grow_continue(int mdfd, struct supertype *st, struct mdinfo *info,
                        a -= b;
        }
        /* LCM == product / GCD */
-       blocks = ochunk/512 * nchunk/512 * odata * ndata / a;
+       blocks = (ochunk/512) * (nchunk/512) * odata * ndata / a;
 
        sra = sysfs_read(-1, devname2devnum(info->sys_name),
                         GET_COMPONENT|GET_DEVS|GET_OFFSET|GET_STATE|
diff --git a/Kill.c b/Kill.c
index e73897857dd15df767f8975fb841664ad74d964d..b3344bd287802b95bda2ca381f0f7d7f00efd916 100644 (file)
--- a/Kill.c
+++ b/Kill.c
@@ -79,3 +79,81 @@ int Kill(char *dev, struct supertype *st, int force, int quiet, int noexcl)
        close(fd);
        return rv;
 }
+
+int Kill_subarray(char *dev, char *subarray, int quiet)
+{
+       /* Delete a subarray out of a container, the subarry must be
+        * inactive.  The subarray string must be a subarray index
+        * number.
+        *
+        * 0 = successfully deleted subarray from all container members
+        * 1 = failed to sync metadata to one or more devices
+        * 2 = failed to find the container, subarray, or other resource
+        *     issue
+        */
+       struct supertype supertype, *st = &supertype;
+       int fd, rv = 2;
+
+       memset(st, 0, sizeof(*st));
+
+       if (snprintf(st->subarray, sizeof(st->subarray), "%s", subarray) >=
+           sizeof(st->subarray)) {
+               if (!quiet)
+                       fprintf(stderr,
+                               Name ": Input overflow for subarray '%s' > %zu bytes\n",
+                               subarray, sizeof(st->subarray) - 1);
+               return 2;
+       }
+
+       fd = open_subarray(dev, st, quiet);
+       if (fd < 0)
+               return 2;
+
+       if (!st->ss->kill_subarray) {
+               if (!quiet)
+                       fprintf(stderr,
+                               Name ": Operation not supported for %s metadata\n",
+                               st->ss->name);
+               goto free_super;
+       }
+
+       if (is_subarray_active(subarray, st->devname)) {
+               if (!quiet)
+                       fprintf(stderr,
+                               Name ": Subarray-%s still active, aborting\n",
+                               subarray);
+               goto free_super;
+       }
+
+       if (mdmon_running(st->devnum))
+               st->update_tail = &st->updates;
+
+       /* ok we've found our victim, drop the axe */
+       rv = st->ss->kill_subarray(st);
+       if (rv) {
+               if (!quiet)
+                       fprintf(stderr,
+                               Name ": Failed to delete subarray-%s from %s\n",
+                               subarray, dev);
+               goto free_super;
+       }
+
+       /* FIXME these routines do not report success/failure */
+       if (st->update_tail)
+               flush_metadata_updates(st);
+       else
+               st->ss->sync_metadata(st);
+
+       if (!quiet)
+               fprintf(stderr,
+                       Name ": Deleted subarray-%s from %s, UUIDs may have changed\n",
+                       subarray, dev);
+
+       rv = 0;
+
+ free_super:
+       st->ss->free_super(st);
+       close(fd);
+
+       return rv;
+}
index f6fb3ef1875e7d3e29942ad2d1da08b3982f7a3e..cca15031d8eb35d163e5176f6b8122a5e164f15b 100644 (file)
--- a/Manage.c
+++ b/Manage.c
@@ -869,4 +869,57 @@ int autodetect(void)
        }
        return rv;
 }
+
+int Update_subarray(char *dev, char *subarray, char *update, mddev_ident_t ident, int quiet)
+{
+       struct supertype supertype, *st = &supertype;
+       int fd, rv = 2;
+
+       memset(st, 0, sizeof(*st));
+       if (snprintf(st->subarray, sizeof(st->subarray), "%s", subarray) >=
+           sizeof(st->subarray)) {
+               if (!quiet)
+                       fprintf(stderr,
+                               Name ": Input overflow for subarray '%s' > %zu bytes\n",
+                               subarray, sizeof(st->subarray) - 1);
+               return 2;
+       }
+
+       fd = open_subarray(dev, st, quiet);
+       if (fd < 0)
+               return 2;
+
+       if (!st->ss->update_subarray) {
+               if (!quiet)
+                       fprintf(stderr,
+                               Name ": Operation not supported for %s metadata\n",
+                               st->ss->name);
+               goto free_super;
+       }
+
+       if (mdmon_running(st->devnum))
+               st->update_tail = &st->updates;
+
+       rv = st->ss->update_subarray(st, update, ident);
+
+       if (rv) {
+               if (!quiet)
+                       fprintf(stderr, Name ": Failed to update %s of subarray-%s in %s\n",
+                               update, subarray, dev);
+       } else if (st->update_tail)
+               flush_metadata_updates(st);
+       else
+               st->ss->sync_metadata(st);
+
+       if (rv == 0 && strcmp(update, "name") == 0 && !quiet)
+               fprintf(stderr,
+                       Name ": Updated subarray-%s name from %s, UUIDs may have changed\n",
+                       subarray, dev);
+
+ free_super:
+       st->ss->free_super(st);
+       close(fd);
+
+       return rv;
+}
 #endif
index 9d5a21129aef477a7b927f8b85631eaa2fb052f6..fa333109055372174d67227c3baa70cb88751f74 100644 (file)
--- a/ReadMe.c
+++ b/ReadMe.c
@@ -108,6 +108,8 @@ struct option long_options[] = {
     {"examine-bitmap", 0, 0, 'X'},
     {"auto-detect", 0, 0, AutoDetect},
     {"detail-platform", 0, 0, DetailPlatform},
+    {"kill-subarray", 1, 0, KillSubarray},
+    {"update-subarray", 1, 0, UpdateSubarray},
 
     /* synonyms */
     {"monitor",   0, 0, 'F'},
diff --git a/mdadm.8 b/mdadm.8
index aa2b12f06c1a333d913ee676c160a7e38c55092e..7e252fa3f22e0e6505d70b8a0d20c0c0b84e2005 100644 (file)
--- a/mdadm.8
+++ b/mdadm.8
@@ -1172,6 +1172,24 @@ overwritten with zeros.  With
 the block where the superblock would be is overwritten even if it
 doesn't appear to be valid.
 
+.TP
+.B \-\-kill\-subarray=
+If the device is a container and the argument to \-\-kill\-subarray
+specifies an inactive subarray in the container, then the subarray is
+deleted.  Deleting all subarrays will leave an 'empty-container' or
+spare superblock on the drives.  See \-\-zero\-superblock for completely
+removing a superblock.  Note that some formats depend on the subarray
+index for generating a UUID, this command will fail if it would change
+the UUID of an active subarray.
+
+.TP
+.B \-\-update\-subarray=
+If the device is a container and the argument to \-\-update\-subarray
+specifies a subarray in the container, then attempt to update the given
+superblock field in the subarray. See below in
+.B MISC MODE
+for details.
+
 .TP
 .BR \-t ", " \-\-test
 When used with
@@ -1747,6 +1765,28 @@ metadata is platform independent
 metadata failed to find its platform components on this system
 .RE
 
+.TP
+.B \-\-update\-subarray=
+If the device is a container and the argument to \-\-update\-subarray
+specifies a subarray in the container, then attempt to update the given
+superblock field in the subarray.  Similar to updating an array in
+"assemble" mode, the field to update is selected by
+.B \-U
+or
+.B \-\-update=
+option.  Currently only
+.B name
+is supported.
+
+The 
+.B name
+option updates the subarray name in the metadata, it may not affect the
+device node name or the device node symlink until the subarray is
+re\-assembled.  If updating 
+.B name
+would change the UUID of an active subarray this operation is blocked,
+and the command will end in an error.
+
 .TP
 .B \-\-examine
 The device should be a component of an md array.
diff --git a/mdadm.c b/mdadm.c
index d5e34c074c034f99d289124422ab38d944e8feee..e7435fd9e6e98b8cdbbec5cec0867f5573eb0363 100644 (file)
--- a/mdadm.c
+++ b/mdadm.c
@@ -103,6 +103,7 @@ int main(int argc, char *argv[])
        int dosyslog = 0;
        int rebuild_map = 0;
        int auto_update_home = 0;
+       char *subarray = NULL;
 
        int print_help = 0;
        FILE *outf;
@@ -216,6 +217,15 @@ int main(int argc, char *argv[])
                case 'W':
                case Waitclean:
                case DetailPlatform:
+               case KillSubarray:
+               case UpdateSubarray:
+                       if (opt == KillSubarray || opt == UpdateSubarray) {
+                               if (subarray) {
+                                       fprintf(stderr, Name ": subarray can only be specified once\n");
+                                       exit(2);
+                               }
+                               subarray = optarg;
+                       }
                case 'K': if (!mode) newmode = MISC; break;
                }
                if (mode && newmode == mode) {
@@ -589,11 +599,16 @@ int main(int argc, char *argv[])
 
                case O(CREATE,'N'):
                case O(ASSEMBLE,'N'):
+               case O(MISC,'N'):
                        if (ident.name[0]) {
                                fprintf(stderr, Name ": name cannot be set twice.   "
                                        "Second value %s.\n", optarg);
                                exit(2);
                        }
+                       if (mode == MISC && !subarray) {
+                               fprintf(stderr, Name ": -N/--name only valid with --update-subarray in misc mode\n");
+                               exit(2);
+                       }
                        if (strlen(optarg) > 32) {
                                fprintf(stderr, Name ": name '%s' is too long, 32 chars max.\n",
                                        optarg);
@@ -620,11 +635,16 @@ int main(int argc, char *argv[])
                        continue;
 
                case O(ASSEMBLE,'U'): /* update the superblock */
+               case O(MISC,'U'):
                        if (update) {
                                fprintf(stderr, Name ": Can only update one aspect of superblock, both %s and %s given.\n",
                                        update, optarg);
                                exit(2);
                        }
+                       if (mode == MISC && !subarray) {
+                               fprintf(stderr, Name ": Only subarrays can be updated in misc mode\n");
+                               exit(2);
+                       }
                        update = optarg;
                        if (strcmp(update, "sparc2.2")==0)
                                continue;
@@ -807,10 +827,21 @@ int main(int argc, char *argv[])
                case O(MISC,'W'):
                case O(MISC, Waitclean):
                case O(MISC, DetailPlatform):
+               case O(MISC, KillSubarray):
+               case O(MISC, UpdateSubarray):
                        if (devmode && devmode != opt &&
                            (devmode == 'E' || (opt == 'E' && devmode != 'Q'))) {
-                               fprintf(stderr, Name ": --examine/-E cannot be given with -%c\n",
-                                       devmode =='E'?opt:devmode);
+                               fprintf(stderr, Name ": --examine/-E cannot be given with ");
+                               if (devmode == 'E') {
+                                       if (option_index >= 0)
+                                               fprintf(stderr, "--%s\n",
+                                                       long_options[option_index].name);
+                                       else
+                                               fprintf(stderr, "-%c\n", opt);
+                               } else if (isalpha(devmode))
+                                       fprintf(stderr, "-%c\n", devmode);
+                               else
+                                       fprintf(stderr, "previous option\n");
                                exit(2);
                        }
                        devmode = opt;
@@ -1403,6 +1434,18 @@ int main(int argc, char *argv[])
                                        rv |= Wait(dv->devname); continue;
                                case Waitclean:
                                        rv |= WaitClean(dv->devname, -1, verbose-quiet); continue;
+                               case KillSubarray:
+                                       rv |= Kill_subarray(dv->devname, subarray, quiet);
+                                       continue;
+                               case UpdateSubarray:
+                                       if (update == NULL) {
+                                               fprintf(stderr,
+                                                       Name ": -U/--update must be specified with --update-subarray\n");
+                                               rv |= 1;
+                                               continue;
+                                       }
+                                       rv |= Update_subarray(dv->devname, subarray, update, &ident, quiet);
+                                       continue;
                                }
                                mdfd = open_mddev(dv->devname, 1);
                                if (mdfd>=0) {
diff --git a/mdadm.h b/mdadm.h
index 142868ad34ed8cd73aa70573b632c78d21ca46df..798713cba0aacc3bed1f4bacdd4c3254f67ac18e 100644 (file)
--- a/mdadm.h
+++ b/mdadm.h
@@ -258,6 +258,7 @@ extern char Version[], Usage[], Help[], OptionHelp[],
 
 /* for option that don't have short equivilents, we assign arbitrary
  * small numbers.  '1' means an undecorated option, so we start at '2'.
+ * (note we must stop before we get to 65 i.e. 'A')
  */
 enum special_options {
        AssumeClean = 2,
@@ -266,13 +267,15 @@ enum special_options {
        ReAdd,
        NoDegraded,
        Sparc22,
-       BackupFile,
+       BackupFile, /* 8 */
        HomeHost,
        AutoHomeHost,
        Symlinks,
        AutoDetect,
        Waitclean,
        DetailPlatform,
+       KillSubarray,
+       UpdateSubarray, /* 16 */
 };
 
 /* structures read from config file */
@@ -611,6 +614,10 @@ extern struct superswitch {
        int (*default_layout)(int level); /* optional */
        /* query the supertype for default chunk size */
        int (*default_chunk)(struct supertype *st); /* optional */
+       /* Permit subarray's to be deleted from inactive containers */
+       int (*kill_subarray)(struct supertype *st); /* optional */
+       /* Permit subarray's to be modified */
+       int (*update_subarray)(struct supertype *st, char *update, mddev_ident_t ident); /* optional */
 
 /* for mdmon */
        int (*open_new)(struct supertype *c, struct active_array *a,
@@ -807,6 +814,8 @@ extern int Monitor(mddev_dev_t devlist,
                   int dosyslog, int test, char *pidfile, int increments);
 
 extern int Kill(char *dev, struct supertype *st, int force, int quiet, int noexcl);
+extern int Kill_subarray(char *dev, char *subarray, int quiet);
+extern int Update_subarray(char *dev, char *subarray, char *update, mddev_ident_t ident, int quiet);
 extern int Wait(char *dev);
 extern int WaitClean(char *dev, int sock, int verbose);
 
@@ -878,6 +887,7 @@ extern int enough(int level, int raid_disks, int layout, int clean,
 extern int ask(char *mesg);
 extern unsigned long long get_component_size(int fd);
 extern void remove_partitions(int fd);
+extern int test_partition(int fd);
 extern unsigned long long calc_array_size(int level, int raid_disks, int layout,
                                   int chunksize, unsigned long long devsize);
 extern int flush_metadata_updates(struct supertype *st);
@@ -912,6 +922,10 @@ extern int create_mddev(char *dev, char *name, int autof, int trustworthy,
 #define        METADATA 3
 extern int open_mddev(char *dev, int report_errors);
 extern int open_container(int fd);
+extern int is_container_member(struct mdstat_ent *ent, char *devname);
+extern int is_subarray_active(char *subarray, char *devname);
+extern int open_subarray(char *dev, struct supertype *st, int quiet);
+extern struct superswitch *version_to_superswitch(char *vers);
 
 extern char *pid_dir;
 extern int mdmon_running(int devnum);
diff --git a/mdmon.c b/mdmon.c
index 69c320e66da27e683759307bb8a0047afce2da38..beb39cfcf0be284383b83fda036aaceffe74af37 100644 (file)
--- a/mdmon.c
+++ b/mdmon.c
@@ -104,15 +104,6 @@ int __clone2(int (*fn)(void *),
        return mon_tid;
 }
 
-static struct superswitch *find_metadata_methods(char *vers)
-{
-       if (strcmp(vers, "ddf") == 0)
-               return &super_ddf;
-       if (strcmp(vers, "imsm") == 0)
-               return &super_imsm;
-       return NULL;
-}
-
 static int make_pidfile(char *devname)
 {
        char path[100];
@@ -136,18 +127,6 @@ static int make_pidfile(char *devname)
        return 0;
 }
 
-int is_container_member(struct mdstat_ent *mdstat, char *container)
-{
-       if (mdstat->metadata_version == NULL ||
-           strncmp(mdstat->metadata_version, "external:", 9) != 0 ||
-           !is_subarray(mdstat->metadata_version+9) ||
-           strncmp(mdstat->metadata_version+10, container, strlen(container)) != 0 ||
-           mdstat->metadata_version[10+strlen(container)] != '/')
-               return 0;
-
-       return 1;
-}
-
 static void try_kill_monitor(pid_t pid, char *devname, int sock)
 {
        char buf[100];
@@ -414,9 +393,9 @@ static int mdmon(char *devname, int devnum, int must_fork, int takeover)
                exit(3);
        }
 
-       container->ss = find_metadata_methods(mdi->text_version);
+       container->ss = version_to_superswitch(mdi->text_version);
        if (container->ss == NULL) {
-               fprintf(stderr, "mdmon: %s uses unknown metadata: %s\n",
+               fprintf(stderr, "mdmon: %s uses unsupported metadata: %s\n",
                        devname, mdi->text_version);
                exit(3);
        }
index 3feea57796e1242201095ca85659b9279902a1ff..736e07f374a44e09ae266681676bd3add839a522 100644 (file)
@@ -783,6 +783,10 @@ static int load_super_ddf(struct supertype *st, int fd,
        if (get_dev_size(fd, devname, &dsize) == 0)
                return 1;
 
+       if (test_partition(fd))
+               /* DDF is not allowed on partitions */
+               return 1;
+
        /* 32M is a lower bound */
        if (dsize <= 32*1024*1024) {
                if (devname)
@@ -841,10 +845,18 @@ static int load_super_ddf(struct supertype *st, int fd,
        }
 
        if (st->subarray[0]) {
+               unsigned long val;
                struct vcl *v;
+               char *ep;
+
+               val = strtoul(st->subarray, &ep, 10);
+               if (*ep != '\0') {
+                       free(super);
+                       return 1;
+               }
 
                for (v = super->conflist; v; v = v->next)
-                       if (v->vcnum == atoi(st->subarray))
+                       if (v->vcnum == val)
                                super->currentconf = v;
                if (!super->currentconf) {
                        free(super);
@@ -2866,14 +2878,25 @@ static int load_super_ddf_all(struct supertype *st, int fd,
                        return 1;
        }
        if (st->subarray[0]) {
+               unsigned long val;
                struct vcl *v;
+               char *ep;
+
+               val = strtoul(st->subarray, &ep, 10);
+               if (*ep != '\0') {
+                       free(super);
+                       return 1;
+               }
 
                for (v = super->conflist; v; v = v->next)
-                       if (v->vcnum == atoi(st->subarray))
+                       if (v->vcnum == val)
                                super->currentconf = v;
-               if (!super->currentconf)
+               if (!super->currentconf) {
+                       free(super);
                        return 1;
+               }
        }
+
        *sbp = super;
        if (st->ss == NULL) {
                st->ss = &super_ddf;
index 3bd041ad37c8a28014fdd847e9ff7785de30d321..159ae4a83ec5f5b099733ea4b1dcc76e05e89a39 100644 (file)
@@ -282,6 +282,8 @@ struct extent {
 enum imsm_update_type {
        update_activate_spare,
        update_create_array,
+       update_kill_array,
+       update_rename_array,
        update_add_disk,
 };
 
@@ -303,6 +305,17 @@ struct imsm_update_create_array {
        struct imsm_dev dev;
 };
 
+struct imsm_update_kill_array {
+       enum imsm_update_type type;
+       int dev_idx;
+};
+
+struct imsm_update_rename_array {
+       enum imsm_update_type type;
+       __u8 name[MAX_RAID_SERIAL_LEN];
+       int dev_idx;
+};
+
 struct imsm_update_add_disk {
        enum imsm_update_type type;
 };
@@ -2759,11 +2772,20 @@ static int load_super_imsm_all(struct supertype *st, int fd, void **sbp,
        }
 
        if (st->subarray[0]) {
-               if (atoi(st->subarray) <= super->anchor->num_raid_devs)
-                       super->current_vol = atoi(st->subarray);
+               unsigned long val;
+               char *ep;
+
+               err = 1;
+               val = strtoul(st->subarray, &ep, 10);
+               if (*ep != '\0') {
+                       free_imsm(super);
+                       goto error;
+               }
+
+               if (val < super->anchor->num_raid_devs)
+                       super->current_vol = val;
                else {
                        free_imsm(super);
-                       err = 1;
                        goto error;
                }
        }
@@ -2804,6 +2826,10 @@ static int load_super_imsm(struct supertype *st, int fd, char *devname)
                return 0;
 #endif
 
+       if (test_partition(fd))
+               /* IMSM not allowed on partitions */
+               return 1;
+
        free_super_imsm(st);
 
        super = alloc_super();
@@ -2826,8 +2852,17 @@ static int load_super_imsm(struct supertype *st, int fd, char *devname)
        }
 
        if (st->subarray[0]) {
-               if (atoi(st->subarray) <= super->anchor->num_raid_devs)
-                       super->current_vol = atoi(st->subarray);
+               unsigned long val;
+               char *ep;
+
+               val = strtoul(st->subarray, &ep, 10);
+               if (*ep != '\0') {
+                       free_imsm(super);
+                       return 1;
+               }
+
+               if (val < super->anchor->num_raid_devs)
+                       super->current_vol = val;
                else {
                        free_imsm(super);
                        return 1;
@@ -2917,6 +2952,30 @@ static void imsm_update_version_info(struct intel_super *super)
        }
 }
 
+static int check_name(struct intel_super *super, char *name, int quiet)
+{
+       struct imsm_super *mpb = super->anchor;
+       char *reason = NULL;
+       int i;
+
+       if (strlen(name) > MAX_RAID_SERIAL_LEN)
+               reason = "must be 16 characters or less";
+
+       for (i = 0; i < mpb->num_raid_devs; i++) {
+               struct imsm_dev *dev = get_imsm_dev(super, i);
+
+               if (strncmp((char *) dev->volume, name, MAX_RAID_SERIAL_LEN) == 0) {
+                       reason = "already exists";
+                       break;
+               }
+       }
+
+       if (reason && !quiet)
+               fprintf(stderr, Name ": imsm volume name %s\n", reason);
+
+       return !reason;
+}
+
 static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                                  unsigned long long size, char *name,
                                  char *homehost, int *uuid)
@@ -2968,16 +3027,8 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        if (super->current_vol == 0)
                mpb->num_disks = 0;
 
-       for (i = 0; i < super->current_vol; i++) {
-               dev = get_imsm_dev(super, i);
-               if (strncmp((char *) dev->volume, name,
-                            MAX_RAID_SERIAL_LEN) == 0) {
-                       fprintf(stderr, Name": '%s' is already defined for this container\n",
-                               name);
-                       return 0;
-               }
-       }
-
+       if (!check_name(super, name, 0))
+               return 0;
        sprintf(st->subarray, "%d", idx);
        dv = malloc(sizeof(*dv));
        if (!dv) {
@@ -4020,6 +4071,129 @@ static int default_chunk_imsm(struct supertype *st)
        return imsm_orom_default_chunk(super->orom);
 }
 
+static void handle_missing(struct intel_super *super, struct imsm_dev *dev);
+
+static int kill_subarray_imsm(struct supertype *st)
+{
+       /* remove the subarray currently referenced by ->current_vol */
+       __u8 i;
+       struct intel_dev **dp;
+       struct intel_super *super = st->sb;
+       __u8 current_vol = super->current_vol;
+       struct imsm_super *mpb = super->anchor;
+
+       if (super->current_vol < 0)
+               return 2;
+       super->current_vol = -1; /* invalidate subarray cursor */
+
+       /* block deletions that would change the uuid of active subarrays
+        *
+        * FIXME when immutable ids are available, but note that we'll
+        * also need to fixup the invalidated/active subarray indexes in
+        * mdstat
+        */
+       for (i = 0; i < mpb->num_raid_devs; i++) {
+               char subarray[4];
+
+               if (i < current_vol)
+                       continue;
+               sprintf(subarray, "%u", i);
+               if (is_subarray_active(subarray, st->devname)) {
+                       fprintf(stderr,
+                               Name ": deleting subarray-%d would change the UUID of active subarray-%d, aborting\n",
+                               current_vol, i);
+
+                       return 2;
+               }
+       }
+
+       if (st->update_tail) {
+               struct imsm_update_kill_array *u = malloc(sizeof(*u));
+
+               if (!u)
+                       return 2;
+               u->type = update_kill_array;
+               u->dev_idx = current_vol;
+               append_metadata_update(st, u, sizeof(*u));
+
+               return 0;
+       }
+
+       for (dp = &super->devlist; *dp;)
+               if ((*dp)->index == current_vol) {
+                       *dp = (*dp)->next;
+               } else {
+                       handle_missing(super, (*dp)->dev);
+                       if ((*dp)->index > current_vol)
+                               (*dp)->index--;
+                       dp = &(*dp)->next;
+               }
+
+       /* no more raid devices, all active components are now spares,
+        * but of course failed are still failed
+        */
+       if (--mpb->num_raid_devs == 0) {
+               struct dl *d;
+
+               for (d = super->disks; d; d = d->next)
+                       if (d->index > -2) {
+                               d->index = -1;
+                               d->disk.status = SPARE_DISK;
+                       }
+       }
+
+       super->updates_pending++;
+
+       return 0;
+}
+
+static int update_subarray_imsm(struct supertype *st, char *update, mddev_ident_t ident)
+{
+       /* update the subarray currently referenced by ->current_vol */
+       struct intel_super *super = st->sb;
+       struct imsm_super *mpb = super->anchor;
+
+       if (super->current_vol < 0)
+               return 2;
+
+       if (strcmp(update, "name") == 0) {
+               char *name = ident->name;
+
+               if (is_subarray_active(st->subarray, st->devname)) {
+                       fprintf(stderr,
+                               Name ": Unable to update name of active subarray\n");
+                       return 2;
+               }
+
+               if (!check_name(super, name, 0))
+                       return 2;
+
+               if (st->update_tail) {
+                       struct imsm_update_rename_array *u = malloc(sizeof(*u));
+
+                       if (!u)
+                               return 2;
+                       u->type = update_rename_array;
+                       u->dev_idx = super->current_vol;
+                       snprintf((char *) u->name, MAX_RAID_SERIAL_LEN, "%s", name);
+                       append_metadata_update(st, u, sizeof(*u));
+               } else {
+                       struct imsm_dev *dev;
+                       int i;
+
+                       dev = get_imsm_dev(super, super->current_vol);
+                       snprintf((char *) dev->volume, MAX_RAID_SERIAL_LEN, "%s", name);
+                       for (i = 0; i < mpb->num_raid_devs; i++) {
+                               dev = get_imsm_dev(super, i);
+                               handle_missing(super, dev);
+                       }
+                       super->updates_pending++;
+               }
+       } else
+               return 2;
+
+       return 0;
+}
 #endif /* MDASSEMBLE */
 
 static int is_rebuilding(struct imsm_dev *dev)
@@ -4360,6 +4534,24 @@ static void mark_missing(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
        memmove(&disk->serial[0], &disk->serial[1], MAX_RAID_SERIAL_LEN - 1);
 }
 
+static void handle_missing(struct intel_super *super, struct imsm_dev *dev)
+{
+       __u8 map_state;
+       struct dl *dl;
+       int failed;
+
+       if (!super->missing)
+               return;
+       failed = imsm_count_failed(super, dev);
+       map_state = imsm_check_degraded(super, dev, failed);
+
+       dprintf("imsm: mark missing\n");
+       end_migration(dev, map_state);
+       for (dl = super->missing; dl; dl = dl->next)
+               mark_missing(dev, &dl->disk, dl->index);
+       super->updates_pending++;
+}
+
 /* Handle dirty -> clean transititions and resync.  Degraded and rebuild
  * states are handled in imsm_set_disk() with one exception, when a
  * resync is stopped due to a new failure this routine will set the
@@ -4376,15 +4568,8 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
        __u32 blocks_per_unit;
 
        /* before we activate this array handle any missing disks */
-       if (consistent == 2 && super->missing) {
-               struct dl *dl;
-
-               dprintf("imsm: mark missing\n");
-               end_migration(dev, map_state);
-               for (dl = super->missing; dl; dl = dl->next)
-                       mark_missing(dev, &dl->disk, dl->index);
-               super->updates_pending++;
-       }
+       if (consistent == 2)
+               handle_missing(super, dev);
 
        if (consistent == 2 &&
            (!is_resync_complete(&a->info) ||
@@ -5081,6 +5266,70 @@ static void imsm_process_update(struct supertype *st,
                }
                break;
        }
+       case update_kill_array: {
+               struct imsm_update_kill_array *u = (void *) update->buf;
+               int victim = u->dev_idx;
+               struct active_array *a;
+               struct intel_dev **dp;
+               struct imsm_dev *dev;
+
+               /* sanity check that we are not affecting the uuid of
+                * active arrays, or deleting an active array
+                *
+                * FIXME when immutable ids are available, but note that
+                * we'll also need to fixup the invalidated/active
+                * subarray indexes in mdstat
+                */
+               for (a = st->arrays; a; a = a->next)
+                       if (a->info.container_member >= victim)
+                               break;
+               /* by definition if mdmon is running at least one array
+                * is active in the container, so checking
+                * mpb->num_raid_devs is just extra paranoia
+                */
+               dev = get_imsm_dev(super, victim);
+               if (a || !dev || mpb->num_raid_devs == 1) {
+                       dprintf("failed to delete subarray-%d\n", victim);
+                       break;
+               }
+
+               for (dp = &super->devlist; *dp;)
+                       if ((*dp)->index == super->current_vol) {
+                               *dp = (*dp)->next;
+                       } else {
+                               if ((*dp)->index > victim)
+                                       (*dp)->index--;
+                               dp = &(*dp)->next;
+                       }
+               mpb->num_raid_devs--;
+               super->updates_pending++;
+               break;
+       }
+       case update_rename_array: {
+               struct imsm_update_rename_array *u = (void *) update->buf;
+               char name[MAX_RAID_SERIAL_LEN+1];
+               int target = u->dev_idx;
+               struct active_array *a;
+               struct imsm_dev *dev;
+
+               /* sanity check that we are not affecting the uuid of
+                * an active array
+                */
+               snprintf(name, MAX_RAID_SERIAL_LEN, "%s", (char *) u->name);
+               name[MAX_RAID_SERIAL_LEN] = '\0';
+               for (a = st->arrays; a; a = a->next)
+                       if (a->info.container_member == target)
+                               break;
+               dev = get_imsm_dev(super, u->dev_idx);
+               if (a || !dev || !check_name(super, name, 1)) {
+                       dprintf("failed to rename subarray-%d\n", target);
+                       break;
+               }
+
+               snprintf((char *) dev->volume, MAX_RAID_SERIAL_LEN, name);
+               super->updates_pending++;
+               break;
+       }
        case update_add_disk:
 
                /* we may be able to repair some arrays if disks are
@@ -5257,6 +5506,8 @@ struct superswitch super_imsm = {
        .default_chunk  = default_chunk_imsm,
        .add_to_super   = add_to_super_imsm,
        .detail_platform = detail_platform_imsm,
+       .kill_subarray = kill_subarray_imsm,
+       .update_subarray = update_subarray_imsm,
 #endif
        .match_home     = match_home_imsm,
        .uuid_from_super= uuid_from_super_imsm,
diff --git a/util.c b/util.c
index 79d2b0fecdb58077143bca5f8d2f6b2604110b7d..66bf2f9605e6c5b6a87b8bf6642b05f0b48dea5f 100644 (file)
--- a/util.c
+++ b/util.c
@@ -302,6 +302,31 @@ void remove_partitions(int fd)
 #endif
 }
 
+int test_partition(int fd)
+{
+       /* Check if fd is a whole-disk or a partition.
+        * BLKPG will return EINVAL on a partition, and BLKPG_DEL_PARTITION
+        * will return ENXIO on an invalid partition number.
+        */
+       struct blkpg_ioctl_arg a;
+       struct blkpg_partition p;
+       a.op = BLKPG_DEL_PARTITION;
+       a.data = (void*)&p;
+       a.datalen = sizeof(p);
+       a.flags = 0;
+       memset(a.data, 0, a.datalen);
+       p.pno = 1<<30;
+       if (ioctl(fd, BLKPG, &a) == 0)
+               /* Very unlikely, but not a partition */
+               return 0;
+       if (errno == ENXIO)
+               /* not a partition */
+               return 0;
+
+       return 1;
+}
+
+
 int enough(int level, int raid_disks, int layout, int clean,
           char *avail, int avail_disks)
 {
@@ -1367,6 +1392,143 @@ int open_container(int fd)
        return -1;
 }
 
+struct superswitch *version_to_superswitch(char *vers)
+{
+       int i;
+
+       for (i = 0; superlist[i]; i++) {
+               struct superswitch *ss = superlist[i];
+
+               if (strcmp(vers, ss->name) == 0)
+                       return ss;
+       }
+
+       return NULL;
+}
+
+int is_container_member(struct mdstat_ent *mdstat, char *container)
+{
+       if (mdstat->metadata_version == NULL ||
+           strncmp(mdstat->metadata_version, "external:", 9) != 0 ||
+           !is_subarray(mdstat->metadata_version+9) ||
+           strncmp(mdstat->metadata_version+10, container, strlen(container)) != 0 ||
+           mdstat->metadata_version[10+strlen(container)] != '/')
+               return 0;
+
+       return 1;
+}
+
+int is_subarray_active(char *subarray, char *container)
+{
+       struct mdstat_ent *mdstat = mdstat_read(0, 0);
+       struct mdstat_ent *ent;
+
+       for (ent = mdstat; ent; ent = ent->next) {
+               if (is_container_member(ent, container)) {
+                       char *inst = &ent->metadata_version[10+strlen(container)+1];
+
+                       if (strcmp(inst, subarray) == 0)
+                               break;
+               }
+       }
+
+       free_mdstat(mdstat);
+
+       return ent != NULL;
+}
+
+/* open_subarray - opens a subarray in a container
+ * @dev: container device name
+ * @st: supertype with only ->subarray set
+ * @quiet: block reporting errors flag
+ *
+ * On success returns an fd to a container and fills in *st
+ */
+int open_subarray(char *dev, struct supertype *st, int quiet)
+{
+       struct mdinfo *mdi;
+       int fd, err = 1;
+
+       fd = open(dev, O_RDWR|O_EXCL);
+       if (fd < 0) {
+               if (!quiet)
+                       fprintf(stderr, Name ": Couldn't open %s, aborting\n",
+                               dev);
+               return 2;
+       }
+
+       st->devnum = fd2devnum(fd);
+       if (st->devnum == NoMdDev) {
+               if (!quiet)
+                       fprintf(stderr,
+                               Name ": Failed to determine device number for %s\n",
+                               dev);
+               goto close_fd;
+       }
+
+       mdi = sysfs_read(fd, st->devnum, GET_VERSION|GET_LEVEL);
+       if (!mdi) {
+               if (!quiet)
+                       fprintf(stderr, Name ": Failed to read sysfs for %s\n",
+                               dev);
+               goto close_fd;
+       }
+
+       if (mdi->array.level != UnSet) {
+               if (!quiet)
+                       fprintf(stderr, Name ": %s is not a container\n", dev);
+               goto free_sysfs;
+       }
+
+       st->ss = version_to_superswitch(mdi->text_version);
+       if (!st->ss) {
+               if (!quiet)
+                       fprintf(stderr,
+                               Name ": Operation not supported for %s metadata\n",
+                               mdi->text_version);
+               goto free_sysfs;
+       }
+
+       st->devname = devnum2devname(st->devnum);
+       if (!st->devname) {
+               if (!quiet)
+                       fprintf(stderr, Name ": Failed to allocate device name\n");
+               goto free_sysfs;
+       }
+
+       if (st->ss->load_super(st, fd, NULL)) {
+               if (!quiet)
+                       fprintf(stderr, Name ": Failed to find subarray-%s in %s\n",
+                               st->subarray, dev);
+               goto free_name;
+       }
+
+       if (!st->loaded_container) {
+               if (!quiet)
+                       fprintf(stderr, Name ": %s is not a container\n", dev);
+               goto free_super;
+       }
+
+       err = 0;
+
+ free_super:
+       if (err)
+               st->ss->free_super(st);
+ free_name:
+       if (err)
+               free(st->devname);
+ free_sysfs:
+       sysfs_free(mdi);
+ close_fd:
+       if (err)
+               close(fd);
+
+       if (err)
+               return -1;
+       else
+               return fd;
+}
+
 int add_disk(int mdfd, struct supertype *st,
             struct mdinfo *sra, struct mdinfo *info)
 {