diff --git a/Grow.c b/Grow.c index 053a372..0916c5d 100644 --- 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, °raded, 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, °raded, 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, °raded, 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 e738978..b3344bd 100644 --- 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; +} diff --git a/Manage.c b/Manage.c index f6fb3ef..cca1503 100644 --- 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 diff --git a/ReadMe.c b/ReadMe.c index 9d5a211..fa33310 100644 --- 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 aa2b12f..7e252fa 100644 --- 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 d5e34c0..e7435fd 100644 --- 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 142868a..798713c 100644 --- 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 69c320e..beb39cf 100644 --- 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); } diff --git a/super-ddf.c b/super-ddf.c index 3feea57..736e07f 100644 --- a/super-ddf.c +++ b/super-ddf.c @@ -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; diff --git a/super-intel.c b/super-intel.c index 3bd041a..159ae4a 100644 --- a/super-intel.c +++ b/super-intel.c @@ -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 79d2b0f..66bf2f9 100644 --- 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) {