diff --git a/Assemble.c b/Assemble.c index 61e8cd1..0557a00 100644 --- a/Assemble.c +++ b/Assemble.c @@ -428,8 +428,6 @@ static int select_devices(struct mddev_dev *devlist, /* make sure we finished the loop */ tmpdev = NULL; - free(st); - st = NULL; goto loop; } else { content = *contentp; @@ -1992,12 +1990,10 @@ int assemble_container_content(struct supertype *st, int mdfd, return 1; } - if (strcmp(sra->text_version, content->text_version) != 0) { - if (content->array.major_version == -1 && - content->array.minor_version == -2 && - c->readonly && - content->text_version[0] == '/') - content->text_version[0] = '-'; + /* Fill sysfs properties only if they are not set. Determine it by checking text_version + * and ignoring special character on the first place. + */ + if (strcmp(sra->text_version + 1, content->text_version + 1) != 0) { if (sysfs_set_array(content, 9003) != 0) { sysfs_free(sra); return 1; diff --git a/Build.c b/Build.c index 8d6f6f5..657ab31 100644 --- a/Build.c +++ b/Build.c @@ -24,8 +24,8 @@ #include "mdadm.h" -int Build(char *mddev, struct mddev_dev *devlist, - struct shape *s, struct context *c) +int Build(struct mddev_ident *ident, struct mddev_dev *devlist, struct shape *s, + struct context *c) { /* Build a linear or raid0 arrays without superblocks * We cannot really do any checks, we just do it. @@ -75,13 +75,12 @@ int Build(char *mddev, struct mddev_dev *devlist, /* We need to create the device. It can have no name. */ map_lock(&map); - mdfd = create_mddev(mddev, NULL, c->autof, LOCAL, + mdfd = create_mddev(ident->devname, NULL, c->autof, LOCAL, chosen_name, 0); if (mdfd < 0) { map_unlock(&map); return 1; } - mddev = chosen_name; map_update(&map, fd2devnm(mdfd), "none", uuid, chosen_name); map_unlock(&map); @@ -93,7 +92,7 @@ int Build(char *mddev, struct mddev_dev *devlist, array.nr_disks = s->raiddisks; array.raid_disks = s->raiddisks; array.md_minor = 0; - if (fstat_is_blkdev(mdfd, mddev, &rdev)) + if (fstat_is_blkdev(mdfd, chosen_name, &rdev)) array.md_minor = minor(rdev); array.not_persistent = 1; array.state = 0; /* not clean, but no errors */ @@ -108,8 +107,7 @@ int Build(char *mddev, struct mddev_dev *devlist, array.chunk_size = s->chunk*1024; array.layout = s->layout; if (md_set_array_info(mdfd, &array)) { - pr_err("md_set_array_info() failed for %s: %s\n", - mddev, strerror(errno)); + pr_err("md_set_array_info() failed for %s: %s\n", chosen_name, strerror(errno)); goto abort; } @@ -178,8 +176,8 @@ int Build(char *mddev, struct mddev_dev *devlist, } if (bitmap_fd >= 0) { if (ioctl(mdfd, SET_BITMAP_FILE, bitmap_fd) < 0) { - pr_err("Cannot set bitmap file for %s: %s\n", - mddev, strerror(errno)); + pr_err("Cannot set bitmap file for %s: %s\n", chosen_name, + strerror(errno)); goto abort; } } @@ -193,9 +191,8 @@ int Build(char *mddev, struct mddev_dev *devlist, } if (c->verbose >= 0) - pr_err("array %s built and started.\n", - mddev); - wait_for(mddev, mdfd); + pr_err("array %s built and started.\n", chosen_name); + wait_for(chosen_name, mdfd); close(mdfd); return 0; diff --git a/Create.c b/Create.c index ea6a474..a280c7b 100644 --- a/Create.c +++ b/Create.c @@ -471,11 +471,8 @@ out: return ret; } -int Create(struct supertype *st, char *mddev, - char *name, int *uuid, - int subdevs, struct mddev_dev *devlist, - struct shape *s, - struct context *c) +int Create(struct supertype *st, struct mddev_ident *ident, int subdevs, + struct mddev_dev *devlist, struct shape *s, struct context *c) { /* * Create a new raid array. @@ -497,6 +494,8 @@ int Create(struct supertype *st, char *mddev, unsigned long long minsize = 0, maxsize = 0; char *mindisc = NULL; char *maxdisc = NULL; + char *name = ident->name; + int *uuid = ident->uuid_set == 1 ? ident->uuid : NULL; int dnum; struct mddev_dev *dv; dev_t rdev; @@ -1015,7 +1014,7 @@ int Create(struct supertype *st, char *mddev, /* We need to create the device */ map_lock(&map); - mdfd = create_mddev(mddev, name, c->autof, LOCAL, chosen_name, 1); + mdfd = create_mddev(ident->devname, ident->name, c->autof, LOCAL, chosen_name, 1); if (mdfd < 0) { map_unlock(&map); return 1; @@ -1032,7 +1031,6 @@ int Create(struct supertype *st, char *mddev, udev_unblock(); return 1; } - mddev = chosen_name; memset(&inf, 0, sizeof(inf)); md_get_array_info(mdfd, &inf); @@ -1050,7 +1048,7 @@ int Create(struct supertype *st, char *mddev, * with, but it chooses to trust me instead. Sigh */ info.array.md_minor = 0; - if (fstat_is_blkdev(mdfd, mddev, &rdev)) + if (fstat_is_blkdev(mdfd, chosen_name, &rdev)) info.array.md_minor = minor(rdev); info.array.not_persistent = 0; @@ -1102,8 +1100,8 @@ int Create(struct supertype *st, char *mddev, info.array.layout = s->layout; info.array.chunk_size = s->chunk*1024; - if (name == NULL || *name == 0) { - /* base name on mddev */ + if (*name == 0) { + /* base name on devname */ /* /dev/md0 -> 0 * /dev/md_d0 -> d0 * /dev/md_foo -> foo @@ -1113,15 +1111,16 @@ int Create(struct supertype *st, char *mddev, * /dev/mdhome -> home */ /* FIXME compare this with rules in create_mddev */ - name = strrchr(mddev, '/'); + name = strrchr(chosen_name, '/'); + if (name) { name++; if (strncmp(name, "md_", 3) == 0 && - strlen(name) > 3 && (name-mddev) == 5 /* /dev/ */) + strlen(name) > 3 && (name - chosen_name) == 5 /* /dev/ */) name += 3; else if (strncmp(name, "md", 2) == 0 && strlen(name) > 2 && isdigit(name[2]) && - (name-mddev) == 5 /* /dev/ */) + (name - chosen_name) == 5 /* /dev/ */) name += 2; } } @@ -1215,8 +1214,7 @@ int Create(struct supertype *st, char *mddev, } rv = set_array_info(mdfd, st, &info); if (rv) { - pr_err("failed to set array info for %s: %s\n", - mddev, strerror(errno)); + pr_err("failed to set array info for %s: %s\n", chosen_name, strerror(errno)); goto abort_locked; } @@ -1237,8 +1235,7 @@ int Create(struct supertype *st, char *mddev, goto abort_locked; } if (ioctl(mdfd, SET_BITMAP_FILE, bitmap_fd) < 0) { - pr_err("Cannot set bitmap file for %s: %s\n", - mddev, strerror(errno)); + pr_err("Cannot set bitmap file for %s: %s\n", chosen_name, strerror(errno)); goto abort_locked; } } @@ -1254,7 +1251,7 @@ int Create(struct supertype *st, char *mddev, * create links */ sysfs_uevent(&info, "change"); if (c->verbose >= 0) - pr_err("container %s prepared.\n", mddev); + pr_err("container %s prepared.\n", chosen_name); wait_for(chosen_name, mdfd); } else if (c->runstop == 1 || subdevs >= s->raiddisks) { if (st->ss->external) { @@ -1312,7 +1309,7 @@ int Create(struct supertype *st, char *mddev, ioctl(mdfd, RESTART_ARRAY_RW, NULL); } if (c->verbose >= 0) - pr_info("array %s started.\n", mddev); + pr_info("array %s started.\n", chosen_name); if (st->ss->external && st->container_devnm[0]) { if (need_mdmon) start_mdmon(st->container_devnm); diff --git a/Detail.c b/Detail.c index 206d88e..57ac336 100644 --- a/Detail.c +++ b/Detail.c @@ -254,11 +254,9 @@ int Detail(char *dev, struct context *c) fname_from_uuid(st, info, nbuf, ':'); printf("MD_UUID=%s\n", nbuf + 5); mp = map_by_uuid(&map, info->uuid); - if (mp && mp->path && strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) { - printf("MD_DEVNAME="); - print_escape(mp->path + DEV_MD_DIR_LEN); - putchar('\n'); - } + + if (mp && mp->path && strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) + printf("MD_DEVNAME=%s\n", mp->path + DEV_MD_DIR_LEN); if (st->ss->export_detail_super) st->ss->export_detail_super(st); @@ -271,12 +269,9 @@ int Detail(char *dev, struct context *c) __fname_from_uuid(mp->uuid, 0, nbuf, ':'); printf("MD_UUID=%s\n", nbuf+5); } - if (mp && mp->path && - strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) { - printf("MD_DEVNAME="); - print_escape(mp->path + DEV_MD_DIR_LEN); - putchar('\n'); - } + if (mp && mp->path && strncmp(mp->path, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) + printf("MD_DEVNAME=%s\n", mp->path + DEV_MD_DIR_LEN); + map_free(map); } if (!c->no_devices && sra) { diff --git a/Incremental.c b/Incremental.c index f13ce02..3551c65 100644 --- a/Incremental.c +++ b/Incremental.c @@ -1467,17 +1467,6 @@ static int Incremental_container(struct supertype *st, char *devname, st->ss->getinfo_super(st, &info, NULL); - if ((c->runstop > 0 && info.container_enough >= 0) || - info.container_enough > 0) - /* pass */; - else { - if (c->export) { - printf("MD_STARTED=no\n"); - } else if (c->verbose) - pr_err("not enough devices to start the container\n"); - return 0; - } - match = conf_match(st, &info, devname, c->verbose, &rv); if (match == NULL && rv == 2) return rv; @@ -1628,54 +1617,18 @@ release: return rv; } -static void run_udisks(char *arg1, char *arg2) -{ - int pid = fork(); - int status; - if (pid == 0) { - manage_fork_fds(1); - execl("/usr/bin/udisks", "udisks", arg1, arg2, NULL); - execl("/bin/udisks", "udisks", arg1, arg2, NULL); - exit(1); - } - while (pid > 0 && wait(&status) != pid) - ; -} - -static int force_remove(char *devnm, int fd, struct mdinfo *mdi, int verbose) -{ - int rv; - int devid = devnm2devid(devnm); - - run_udisks("--unmount", map_dev(major(devid), minor(devid), 0)); - rv = Manage_stop(devnm, fd, verbose, 1); - if (rv) { - /* At least we can try to trigger a 'remove' */ - sysfs_uevent(mdi, "remove"); - if (verbose) - pr_err("Fail to stop %s too.\n", devnm); - } - return rv; -} - static void remove_from_member_array(struct mdstat_ent *memb, struct mddev_dev *devlist, int verbose) { - int rv; - struct mdinfo mmdi; int subfd = open_dev(memb->devnm); if (subfd >= 0) { - rv = Manage_subdevs(memb->devnm, subfd, devlist, verbose, - 0, UOPT_UNDEFINED, 0); - if (rv & 2) { - if (sysfs_init(&mmdi, -1, memb->devnm)) - pr_err("unable to initialize sysfs for: %s\n", - memb->devnm); - else - force_remove(memb->devnm, subfd, &mmdi, - verbose); - } + /* + * Ignore the return value because it's necessary + * to handle failure condition here. + */ + Manage_subdevs(memb->devnm, subfd, devlist, verbose, + 0, UOPT_UNDEFINED, 0); close(subfd); } } @@ -1758,21 +1711,19 @@ int IncrementalRemove(char *devname, char *id_path, int verbose) } free_mdstat(mdstat); } else { - rv |= Manage_subdevs(ent->devnm, mdfd, &devlist, - verbose, 0, UOPT_UNDEFINED, 0); - if (rv & 2) { - /* Failed due to EBUSY, try to stop the array. - * Give udisks a chance to unmount it first. + /* + * This 'I' incremental remove is a try-best effort, + * the failure condition can be safely ignored + * because of the following up 'r' remove. */ - rv = force_remove(ent->devnm, mdfd, &mdi, verbose); - goto end; - } + Manage_subdevs(ent->devnm, mdfd, &devlist, + verbose, 0, UOPT_UNDEFINED, 0); } devlist.disposition = 'r'; rv = Manage_subdevs(ent->devnm, mdfd, &devlist, verbose, 0, UOPT_UNDEFINED, 0); -end: + close(mdfd); free_mdstat(ent); return rv; diff --git a/Makefile b/Makefile index 5eac1a4..b3aa36f 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ # define "CXFLAGS" to give extra flags to CC. # e.g. make CXFLAGS=-O to optimise -CXFLAGS ?=-O2 +CXFLAGS ?=-O2 -D_FORTIFY_SOURCE=2 TCC = tcc UCLIBC_GCC = $(shell for nm in i386-uclibc-linux-gcc i386-uclibc-gcc; do which $$nm > /dev/null && { echo $$nm ; exit; } ; done; echo false No uclibc found ) #DIET_GCC = diet gcc @@ -50,14 +50,30 @@ ifeq ($(origin CC),default) CC := $(CROSS_COMPILE)gcc endif CXFLAGS ?= -ggdb -CWFLAGS = -Wall -Werror -Wstrict-prototypes -Wextra -Wno-unused-parameter +CWFLAGS ?= -Wall -Werror -Wstrict-prototypes -Wextra -Wno-unused-parameter -Wformat -Wformat-security -Werror=format-security -fstack-protector-strong -fPIE -Warray-bounds ifdef WARN_UNUSED -CWFLAGS += -Wp,-D_FORTIFY_SOURCE=2 -O3 +CWFLAGS += -Wp -O3 endif -FALLTHROUGH := $(shell gcc -v --help 2>&1 | grep "implicit-fallthrough" | wc -l) -ifneq "$(FALLTHROUGH)" "0" -CWFLAGS += -Wimplicit-fallthrough=0 +ifeq ($(origin FALLTHROUGH), undefined) + FALLTHROUGH := $(shell gcc -Q --help=warnings 2>&1 | grep "implicit-fallthrough" | wc -l) + ifneq "$(FALLTHROUGH)" "0" + CWFLAGS += -Wimplicit-fallthrough=0 + endif +endif + +ifeq ($(origin FORMATOVERFLOW), undefined) + FORMATOVERFLOW := $(shell gcc -Q --help=warnings 2>&1 | grep "format-overflow" | wc -l) + ifneq "$(FORMATOVERFLOW)" "0" + CWFLAGS += -Wformat-overflow + endif +endif + +ifeq ($(origin STRINGOPOVERFLOW), undefined) + STRINGOPOVERFLOW := $(shell gcc -Q --help=warnings 2>&1 | grep "stringop-overflow" | wc -l) + ifneq "$(STRINGOPOVERFLOW)" "0" + CWFLAGS += -Wstringop-overflow + endif endif ifdef DEBIAN @@ -116,10 +132,12 @@ CFLAGS += -DUSE_PTHREADS MON_LDFLAGS += -pthread endif +LDFLAGS = -Wl,-z,now,-z,noexecstack + # If you want a static binary, you might uncomment these -# LDFLAGS = -static +# LDFLAGS += -static # STRIP = -s -LDLIBS = -ldl +LDLIBS = -ldl -pie # To explicitly disable libudev, set -DNO_LIBUDEV in CXFLAGS ifeq (, $(findstring -DNO_LIBUDEV, $(CXFLAGS))) @@ -209,14 +227,13 @@ mdadm.Os : $(SRCS) $(INCL) $(CC) -o mdadm.Os $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -DHAVE_STDINT_H -Os $(SRCS) $(LDLIBS) mdadm.O2 : $(SRCS) $(INCL) mdmon.O2 - $(CC) -o mdadm.O2 $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -DHAVE_STDINT_H -O2 -D_FORTIFY_SOURCE=2 $(SRCS) $(LDLIBS) + $(CC) -o mdadm.O2 $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -DHAVE_STDINT_H -O2 $(SRCS) $(LDLIBS) mdmon.O2 : $(MON_SRCS) $(INCL) mdmon.h - $(CC) -o mdmon.O2 $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(MON_LDFLAGS) -DHAVE_STDINT_H -O2 -D_FORTIFY_SOURCE=2 $(MON_SRCS) $(LDLIBS) + $(CC) -o mdmon.O2 $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(MON_LDFLAGS) -DHAVE_STDINT_H -O2 $(MON_SRCS) $(LDLIBS) -# use '-z now' to guarantee no dynamic linker interactions with the monitor thread mdmon : $(MON_OBJS) | check_rundir - $(CC) $(CFLAGS) $(LDFLAGS) $(MON_LDFLAGS) -Wl,-z,now -o mdmon $(MON_OBJS) $(LDLIBS) + $(CC) $(CFLAGS) $(LDFLAGS) $(MON_LDFLAGS) -o mdmon $(MON_OBJS) $(LDLIBS) msg.o: msg.c msg.h test_stripe : restripe.c xmalloc.o mdadm.h diff --git a/Manage.c b/Manage.c index f997b16..075dd72 100644 --- a/Manage.c +++ b/Manage.c @@ -704,6 +704,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, struct supertype *dev_st; int j; mdu_disk_info_t disc; + struct map_ent *map = NULL; if (!get_dev_size(tfd, dv->devname, &ldsize)) { if (dv->disposition == 'M') @@ -907,6 +908,9 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, disc.raid_disk = 0; } + if (map_lock(&map)) + pr_err("failed to get exclusive lock on mapfile when add disk\n"); + if (array->not_persistent==0) { int dfd; if (dv->disposition == 'j') @@ -918,9 +922,9 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, dfd = dev_open(dv->devname, O_RDWR | O_EXCL|O_DIRECT); if (tst->ss->add_to_super(tst, &disc, dfd, dv->devname, INVALID_SECTORS)) - return -1; + goto unlock; if (tst->ss->write_init_super(tst)) - return -1; + goto unlock; } else if (dv->disposition == 'A') { /* this had better be raid1. * As we are "--re-add"ing we must find a spare slot @@ -978,14 +982,14 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, pr_err("add failed for %s: could not get exclusive access to container\n", dv->devname); tst->ss->free_super(tst); - return -1; + goto unlock; } /* Check if metadata handler is able to accept the drive */ if (!tst->ss->validate_geometry(tst, LEVEL_CONTAINER, 0, 1, NULL, 0, 0, dv->devname, NULL, 0, 1)) { close(container_fd); - return -1; + goto unlock; } Kill(dv->devname, NULL, 0, -1, 0); @@ -994,7 +998,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, dv->devname, INVALID_SECTORS)) { close(dfd); close(container_fd); - return -1; + goto unlock; } if (!mdmon_running(tst->container_devnm)) tst->ss->sync_metadata(tst); @@ -1005,7 +1009,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, dv->devname); close(container_fd); tst->ss->free_super(tst); - return -1; + goto unlock; } sra->array.level = LEVEL_CONTAINER; /* Need to set data_offset and component_size */ @@ -1020,7 +1024,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, pr_err("add new device to external metadata failed for %s\n", dv->devname); close(container_fd); sysfs_free(sra); - return -1; + goto unlock; } ping_monitor(devnm); sysfs_free(sra); @@ -1034,7 +1038,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, else pr_err("add new device failed for %s as %d: %s\n", dv->devname, j, strerror(errno)); - return -1; + goto unlock; } if (dv->disposition == 'j') { pr_err("Journal added successfully, making %s read-write\n", devname); @@ -1045,7 +1049,11 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, } if (verbose >= 0) pr_err("added %s\n", dv->devname); + map_unlock(&map); return 1; +unlock: + map_unlock(&map); + return -1; } int Manage_remove(struct supertype *tst, int fd, struct mddev_dev *dv, diff --git a/config.c b/config.c index 450880e..5f12a1f 100644 --- a/config.c +++ b/config.c @@ -122,7 +122,7 @@ int match_keyword(char *word) /** * is_devname_ignore() - check if &devname is a special "" keyword. */ -bool is_devname_ignore(char *devname) +bool is_devname_ignore(const char *devname) { static const char keyword[] = ""; @@ -131,6 +131,34 @@ bool is_devname_ignore(char *devname) return false; } +/** + * ident_log() - generate and write message to the user. + * @param_name: name of the property. + * @value: value of the property. + * @reason: meaningful description. + * @cmdline: context dependent actions, see below. + * + * The function is made to provide similar error handling for both config and cmdline. The behavior + * is configurable via @cmdline. Message has following format: + * "Value "@value" cannot be set for @param_name. Reason: @reason." + * + * If cmdline is on: + * - message is written to stderr. + * otherwise: + * - message is written to stdout. + * - "Value ignored" is added at the end of the message. + */ +static void ident_log(const char *param_name, const char *value, const char *reason, + const bool cmdline) +{ + if (cmdline == true) + pr_err("Value \"%s\" cannot be set as %s. Reason: %s.\n", value, param_name, + reason); + else + pr_info("Value \"%s\" cannot be set as %s. Reason: %s. Value ignored.\n", value, + param_name, reason); +} + /** * ident_init() - Set defaults. * @ident: ident pointer, not NULL. @@ -159,6 +187,127 @@ inline void ident_init(struct mddev_ident *ident) ident->uuid_set = 0; } +/** + * _ident_set_devname()- verify devname and set it in &mddev_ident. + * @ident: pointer to &mddev_ident. + * @devname: devname to be set. + * @cmdline: context dependent actions. If set, ignore keyword is not allowed. + * + * @devname can have following forms: + * '' keyword (if allowed) + * /dev/md{number} + * /dev/md_d{number} (legacy) + * /dev/md_{name} + * /dev/md/{name} + * {name} + * + * {name} must follow name's criteria and be POSIX compatible. + * If criteria passed, duplicate memory and set devname in @ident. + * + * Return: %MDADM_STATUS_SUCCESS or %MDADM_STATUS_ERROR. + */ +mdadm_status_t _ident_set_devname(struct mddev_ident *ident, const char *devname, + const bool cmdline) +{ + assert(ident); + assert(devname); + + static const char named_dev_pref[] = DEV_NUM_PREF "_"; + static const int named_dev_pref_size = sizeof(named_dev_pref) - 1; + const char *prop_name = "devname"; + const char *name; + + if (ident->devname) { + ident_log(prop_name, devname, "Already defined", cmdline); + return MDADM_STATUS_ERROR; + } + + if (is_devname_ignore(devname) == true) { + if (!cmdline) + goto pass; + + ident_log(prop_name, devname, "Special keyword is invalid in this context", + cmdline); + return MDADM_STATUS_ERROR; + } + + if (is_devname_md_numbered(devname) == true || is_devname_md_d_numbered(devname) == true) + goto pass; + + if (strncmp(devname, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) + name = devname + DEV_MD_DIR_LEN; + else if (strncmp(devname, named_dev_pref, named_dev_pref_size) == 0) + name = devname + named_dev_pref_size; + else + name = devname; + + if (is_name_posix_compatible(name) == false) { + ident_log(prop_name, name, "Not POSIX compatible", cmdline); + return MDADM_STATUS_ERROR; + } + + if (is_string_lq(name, MD_NAME_MAX + 1) == false) { + ident_log(prop_name, devname, "Invalid length", cmdline); + return MDADM_STATUS_ERROR; + } +pass: + ident->devname = xstrdup(devname); + return MDADM_STATUS_SUCCESS; +} + +/** + * _ident_set_name()- set name in &mddev_ident. + * @ident: pointer to &mddev_ident. + * @name: name to be set. + * @cmdline: context dependent actions. + * + * If criteria passed, set name in @ident. + * + * Return: %MDADM_STATUS_SUCCESS or %MDADM_STATUS_ERROR. + */ +static mdadm_status_t _ident_set_name(struct mddev_ident *ident, const char *name, + const bool cmdline) +{ + assert(name); + assert(ident); + + const char *prop_name = "name"; + + if (ident->name[0]) { + ident_log(prop_name, name, "Already defined", cmdline); + return MDADM_STATUS_ERROR; + } + + if (is_string_lq(name, MD_NAME_MAX + 1) == false) { + ident_log(prop_name, name, "Too long or empty", cmdline); + return MDADM_STATUS_ERROR; + } + + if (is_name_posix_compatible(name) == false) { + ident_log(prop_name, name, "Not POSIX compatible", cmdline); + return MDADM_STATUS_ERROR; + } + + snprintf(ident->name, MD_NAME_MAX + 1, "%s", name); + return MDADM_STATUS_SUCCESS; +} + +/** + * ident_set_devname()- exported, for cmdline. + */ +mdadm_status_t ident_set_devname(struct mddev_ident *ident, const char *name) +{ + return _ident_set_devname(ident, name, true); +} + +/** + * ident_set_name()- exported, for cmdline. + */ +mdadm_status_t ident_set_name(struct mddev_ident *ident, const char *name) +{ + return _ident_set_name(ident, name, true); +} + struct conf_dev { struct conf_dev *next; char *name; @@ -396,29 +545,7 @@ void arrayline(char *line) for (w = dl_next(line); w != line; w = dl_next(w)) { if (w[0] == '/' || strchr(w, '=') == NULL) { - /* This names the device, or is ''. - * The rules match those in create_mddev. - * 'w' must be: - * /dev/md/{anything} - * /dev/mdNN - * /dev/md_dNN - * - * or anything that doesn't start '/' or '<' - */ - if (is_devname_ignore(w) == true || - strncmp(w, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0 || - (w[0] != '/' && w[0] != '<') || - is_devname_md_numbered(w) == true || - is_devname_md_d_numbered(w) == true) { - /* This is acceptable */; - if (mis.devname) - pr_err("only give one device per ARRAY line: %s and %s\n", - mis.devname, w); - else - mis.devname = w; - }else { - pr_err("%s is an invalid name for an md device - ignored.\n", w); - } + _ident_set_devname(&mis, w, false); } else if (strncasecmp(w, "uuid=", 5) == 0) { if (mis.uuid_set) pr_err("only specify uuid once, %s ignored.\n", @@ -444,14 +571,7 @@ void arrayline(char *line) mis.super_minor = minor; } } else if (strncasecmp(w, "name=", 5) == 0) { - if (mis.name[0]) - pr_err("only specify name once, %s ignored.\n", - w); - else if (strlen(w + 5) > 32) - pr_err("name too long, ignoring %s\n", w); - else - strcpy(mis.name, w + 5); - + _ident_set_name(&mis, w + 5, false); } else if (strncasecmp(w, "bitmap=", 7) == 0) { if (mis.bitmap_file) pr_err("only specify bitmap file once. %s ignored\n", diff --git a/lib.c b/lib.c index 8a4b48e..7ab5998 100644 --- a/lib.c +++ b/lib.c @@ -27,6 +27,24 @@ #include #include +/** + * is_string_lq() - Check if string length with NULL byte is lower or equal to requested. + * @str: string to check. + * @max_len: max length. + * + * @str length must be bigger than 0 and be lower or equal @max_len, including termination byte. + */ +bool is_string_lq(const char * const str, size_t max_len) +{ + assert(str); + + size_t _len = strnlen(str, max_len); + + if (_len > 0 && _len < max_len) + return true; + return false; +} + bool is_dev_alive(char *path) { if (!path) @@ -465,24 +483,50 @@ void print_quoted(char *str) putchar(q); } -void print_escape(char *str) +/** + * is_alphanum() - Check if sign is letter or digit. + * @c: char to analyze. + * + * Similar to isalnum() but additional locales are excluded. + * + * Return: %true on success, %false otherwise. + */ +bool is_alphanum(const char c) { - /* print str, but change space and tab to '_' - * as is suitable for device names - */ - for (; *str; str++) { - switch (*str) { - case ' ': - case '\t': - putchar('_'); - break; - case '/': - putchar('-'); - break; - default: - putchar(*str); - } + if (isupper(c) || islower(c) || isdigit(c) != 0) + return true; + return false; +} + +/** + * is_name_posix_compatible() - Check if name is POSIX compatible. + * @name: name to check. + * + * POSIX portable file name character set contains ASCII letters, + * digits, '_', '.', and '-'. Also forbid leading '-'. + * The length of the name cannot exceed NAME_MAX - 1 (ensure NULL ending). + * + * Return: %true on success, %false otherwise. + */ +bool is_name_posix_compatible(const char * const name) +{ + assert(name); + + char allowed_symbols[] = "-_."; + const char *n = name; + + if (!is_string_lq(name, NAME_MAX)) + return false; + + if (*n == '-') + return false; + + while (*n != '\0') { + if (!is_alphanum(*n) && !strchr(allowed_symbols, *n)) + return false; + n++; } + return true; } int check_env(char *name) diff --git a/mdadm.8.in b/mdadm.8.in index b715950..3142436 100644 --- a/mdadm.8.in +++ b/mdadm.8.in @@ -364,7 +364,7 @@ Use the Intel(R) Matrix Storage Manager metadata format. This creates a which is managed in a similar manner to DDF, and is supported by an option-rom on some platforms: .IP -.B https://www.intel.com/content/www/us/en/support/products/122484/memory-and-storage/ssd-software/intel-virtual-raid-on-cpu-intel-vroc.html +.B https://www.intel.com/content/www/us/en/support/products/122484 .PP .RE @@ -932,17 +932,14 @@ option will be ignored. .BR \-N ", " \-\-name= Set a .B name -for the array. This is currently only effective when creating an -array with a version-1 superblock, or an array in a DDF container. -The name is a simple textual string that can be used to identify array -components when assembling. If name is needed but not specified, it -is taken from the basename of the device that is being created. -e.g. when creating -.I /dev/md/home -the -.B name -will default to -.IR home . +for the array. It must be +.BR "POSIX PORTABLE NAME" +compatible and cannot be longer than 32 chars. This is effective when creating an array +with a v1 metadata, or an external array. + +If name is needed but not specified, it is taken from the basename of the device +that is being created. See +.BR "DEVICE NAMES" .TP .BR \-R ", " \-\-run @@ -1132,8 +1129,10 @@ is much safer. .TP .BR \-N ", " \-\-name= -Specify the name of the array to assemble. This must be the name -that was specified when creating the array. It must either match +Specify the name of the array to assemble. It must be +.BR "POSIX PORTABLE NAME" +compatible and cannot be longer than 32 chars. This must be the name +that was specified when creating the array. It must either match the name stored in the superblock exactly, or it must match with the current .I homehost @@ -2179,14 +2178,17 @@ Usage: .I md-device .BI \-\-chunk= X .BI \-\-level= Y -.br .BI \-\-raid\-devices= Z .I devices .PP -This usage will initialise a new md array, associate some devices with +This usage will initialize a new md array, associate some devices with it, and activate the array. +.I md-device +is a new device. This could be standard name or chosen name. For details see: +.BR "DEVICE NAMES" + The named device will normally not exist when .I "mdadm \-\-create" is run, but will be created by @@ -2227,24 +2229,6 @@ array. This feature can be overridden with the .B \-\-force option. -When creating an array with version-1 metadata a name for the array is -required. -If this is not given with the -.B \-\-name -option, -.I mdadm -will choose a name based on the last component of the name of the -device being created. So if -.B /dev/md3 -is being created, then the name -.B 3 -will be chosen. -If -.B /dev/md/home -is being created, then the name -.B home -will be used. - When creating a partition based array, using .I mdadm with version-1.x metadata, the partition type should be set to @@ -2429,12 +2413,10 @@ and 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. +option updates the subarray name in the metadata. It must be +.BR "POSIX PORTABLE NAME" +compatible and cannot be longer than 32 chars. If successes, new value will be respected after +next assembly. The .B ppl @@ -3395,6 +3377,10 @@ When .B \-\-incremental mode is used, this file gets a list of arrays currently being created. +.SH POSIX PORTABLE NAME +A valid name can only consist of characters "A-Za-z0-9.-_". +The name cannot start with a leading "-" and cannot exceed 255 chars. + .SH DEVICE NAMES .I mdadm @@ -3416,6 +3402,10 @@ can be given, or just the suffix of the second sort of name, such as .I home can be given. +In every style, raw name must be compatible with +.BR "POSIX PORTABLE NAME" +and has to be no longer than 32 chars. + When .I mdadm chooses device names during auto-assembly or incremental assembly, it diff --git a/mdadm.c b/mdadm.c index 22d1c53..62f981d 100644 --- a/mdadm.c +++ b/mdadm.c @@ -690,20 +690,14 @@ int main(int argc, char *argv[]) case O(CREATE,'N'): case O(ASSEMBLE,'N'): case O(MISC,'N'): - if (ident.name[0]) { - pr_err("name cannot be set twice. Second value %s.\n", optarg); - exit(2); - } if (mode == MISC && !c.subarray) { pr_err("-N/--name only valid with --update-subarray in misc mode\n"); exit(2); } - if (strlen(optarg) > 32) { - pr_err("name '%s' is too long, 32 chars max.\n", - optarg); + + if (ident_set_name(&ident, optarg) != MDADM_STATUS_SUCCESS) exit(2); - } - strcpy(ident.name, optarg); + continue; case O(ASSEMBLE,'m'): /* super-minor for array */ @@ -1290,37 +1284,33 @@ int main(int argc, char *argv[]) pr_err("an md device must be given in this mode\n"); exit(2); } + if (ident_set_devname(&ident, devlist->devname) != MDADM_STATUS_SUCCESS) + exit(1); + if ((int)ident.super_minor == -2 && c.autof) { pr_err("--super-minor=dev is incompatible with --auto\n"); exit(2); } if (mode == MANAGE || mode == GROW) { - mdfd = open_mddev(devlist->devname, 1); + mdfd = open_mddev(ident.devname, 1); if (mdfd < 0) exit(1); ret = fstat(mdfd, &stb); if (ret) { - pr_err("fstat failed on %s.\n", devlist->devname); + pr_err("fstat failed on %s.\n", ident.devname); exit(1); } } else { - char *bname = basename(devlist->devname); - - if (strlen(bname) > MD_NAME_MAX) { - pr_err("Name %s is too long.\n", devlist->devname); - exit(1); - } - - ret = stat(devlist->devname, &stb); + ret = stat(ident.devname, &stb); if (ident.super_minor == -2 && ret != 0) { pr_err("--super-minor=dev given, and listed device %s doesn't exist.\n", - devlist->devname); + ident.devname); exit(1); } if (!ret && !stat_is_md_dev(&stb)) { - pr_err("device %s exists but is not an md array.\n", devlist->devname); + pr_err("device %s exists but is not an md array.\n", ident.devname); exit(1); } } @@ -1408,17 +1398,17 @@ int main(int argc, char *argv[]) case MANAGE: /* readonly, add/remove, readwrite, runstop */ if (c.readonly > 0) - rv = Manage_ro(devlist->devname, mdfd, c.readonly); + rv = Manage_ro(ident.devname, mdfd, c.readonly); if (!rv && devs_found > 1) - rv = Manage_subdevs(devlist->devname, mdfd, + rv = Manage_subdevs(ident.devname, mdfd, devlist->next, c.verbose, c.test, c.update, c.force); if (!rv && c.readonly < 0) - rv = Manage_ro(devlist->devname, mdfd, c.readonly); + rv = Manage_ro(ident.devname, mdfd, c.readonly); if (!rv && c.runstop > 0) - rv = Manage_run(devlist->devname, mdfd, &c); + rv = Manage_run(ident.devname, mdfd, &c); if (!rv && c.runstop < 0) - rv = Manage_stop(devlist->devname, mdfd, c.verbose, 0); + rv = Manage_stop(ident.devname, mdfd, c.verbose, 0); break; case ASSEMBLE: if (!c.scan && c.runstop == -1) { @@ -1428,22 +1418,19 @@ int main(int argc, char *argv[]) ident.super_minor == UnSet && ident.name[0] == 0 && !c.scan) { /* Only a device has been given, so get details from config file */ - struct mddev_ident *array_ident = conf_get_ident(devlist->devname); + struct mddev_ident *array_ident = conf_get_ident(ident.devname); if (array_ident == NULL) { - pr_err("%s not identified in config file.\n", - devlist->devname); + pr_err("%s not identified in config file.\n", ident.devname); rv |= 1; if (mdfd >= 0) close(mdfd); } else { if (array_ident->autof == 0) array_ident->autof = c.autof; - rv |= Assemble(ss, devlist->devname, array_ident, - NULL, &c); + rv |= Assemble(ss, ident.devname, array_ident, NULL, &c); } } else if (!c.scan) - rv = Assemble(ss, devlist->devname, &ident, - devlist->next, &c); + rv = Assemble(ss, ident.devname, &ident, devlist->next, &c); else if (devs_found > 0) { if (c.update && devs_found > 1) { pr_err("can only update a single array at a time\n"); @@ -1501,7 +1488,7 @@ int main(int argc, char *argv[]) break; } } - rv = Build(devlist->devname, devlist->next, &s, &c); + rv = Build(&ident, devlist->next, &s, &c); break; case CREATE: if (c.delay == 0) @@ -1538,9 +1525,7 @@ int main(int argc, char *argv[]) break; } - rv = Create(ss, devlist->devname, - ident.name, ident.uuid_set ? ident.uuid : NULL, - devs_found - 1, devlist->next, &s, &c); + rv = Create(ss, &ident, devs_found - 1, devlist->next, &s, &c); break; case MISC: if (devmode == 'E') { @@ -1637,8 +1622,7 @@ int main(int argc, char *argv[]) break; } for (dv = devlist->next; dv; dv = dv->next) { - rv = Grow_Add_device(devlist->devname, mdfd, - dv->devname); + rv = Grow_Add_device(ident.devname, mdfd, dv->devname); if (rv) break; } @@ -1651,18 +1635,15 @@ int main(int argc, char *argv[]) } if (c.delay == 0) c.delay = DEFAULT_BITMAP_DELAY; - rv = Grow_addbitmap(devlist->devname, mdfd, &c, &s); + rv = Grow_addbitmap(ident.devname, mdfd, &c, &s); } else if (grow_continue) - rv = Grow_continue_command(devlist->devname, - mdfd, c.backup_file, - c.verbose); + rv = Grow_continue_command(ident.devname, mdfd, c.backup_file, c.verbose); else if (s.size > 0 || s.raiddisks || s.layout_str || s.chunk != 0 || s.level != UnSet || s.data_offset != INVALID_SECTORS) { - rv = Grow_reshape(devlist->devname, mdfd, - devlist->next, &c, &s); + rv = Grow_reshape(ident.devname, mdfd, devlist->next, &c, &s); } else if (s.consistency_policy != CONSISTENCY_POLICY_UNKNOWN) { - rv = Grow_consistency_policy(devlist->devname, mdfd, &c, &s); + rv = Grow_consistency_policy(ident.devname, mdfd, &c, &s); } else if (array_size == 0) pr_err("no changes to --grow\n"); break; diff --git a/mdadm.conf.5.in b/mdadm.conf.5.in index bc2295c..94e23dd 100644 --- a/mdadm.conf.5.in +++ b/mdadm.conf.5.in @@ -717,10 +717,6 @@ ARRAY /dev/md/home UUID=9187a482:5dde19d9:eea3cc4a:d646ab8b .br auto=part .br -# The name of this array contains a space. -.br -ARRAY /dev/md9 name='Data Storage' -.sp POLICY domain=domain1 metadata=imsm path=pci-0000:00:1f.2-scsi-* .br action=spare diff --git a/mdadm.h b/mdadm.h index f0ceeb7..b48e6f8 100644 --- a/mdadm.h +++ b/mdadm.h @@ -294,6 +294,11 @@ static inline void __put_unaligned32(__u32 val, void *p) #define KIB_TO_BYTES(x) ((x) << 10) #define SEC_TO_BYTES(x) ((x) << 9) +/** + * This is true for native and DDF, IMSM allows 16. + */ +#define MD_NAME_MAX 32 + extern const char Name[]; struct md_bb_entry { @@ -372,9 +377,6 @@ struct mdinfo { int container_member; /* for assembling external-metatdata arrays * This is to be used internally by metadata * handler only */ - int container_enough; /* flag external handlers can set to - * indicate that subarrays have not enough (-1), - * enough to start (0), or all expected disks (1) */ char sys_name[32]; struct mdinfo *devs; struct mdinfo *next; @@ -425,6 +427,12 @@ struct spare_criteria { unsigned int sector_size; }; +typedef enum mdadm_status { + MDADM_STATUS_SUCCESS = 0, + MDADM_STATUS_ERROR, + MDADM_STATUS_UNDEF, +} mdadm_status_t; + enum mode { ASSEMBLE=1, BUILD, @@ -593,7 +601,7 @@ struct mddev_ident { int uuid_set; int uuid[4]; - char name[33]; + char name[MD_NAME_MAX + 1]; int super_minor; @@ -1531,14 +1539,11 @@ extern int Assemble(struct supertype *st, char *mddev, struct mddev_dev *devlist, struct context *c); -extern int Build(char *mddev, struct mddev_dev *devlist, - struct shape *s, struct context *c); +extern int Build(struct mddev_ident *ident, struct mddev_dev *devlist, struct shape *s, + struct context *c); -extern int Create(struct supertype *st, char *mddev, - char *name, int *uuid, - int subdevs, struct mddev_dev *devlist, - struct shape *s, - struct context *c); +extern int Create(struct supertype *st, struct mddev_ident *ident, int subdevs, + struct mddev_dev *devlist, struct shape *s, struct context *c); extern int Detail(char *dev, struct context *c); extern int Detail_Platform(struct superswitch *ss, int scan, int verbose, int export, char *controller_path); @@ -1609,9 +1614,11 @@ extern int check_raid(int fd, char *name); extern int check_partitions(int fd, char *dname, unsigned long long freesize, unsigned long long size); +extern bool is_name_posix_compatible(const char *path); extern int fstat_is_blkdev(int fd, char *devname, dev_t *rdev); extern int stat_is_blkdev(char *devname, dev_t *rdev); +extern bool is_string_lq(const char * const str, size_t max_len); extern bool is_dev_alive(char *path); extern int get_mdp_major(void); extern int get_maj_min(char *dev, int *major, int *minor); @@ -1629,6 +1636,8 @@ extern void manage_fork_fds(int close_all); extern int continue_via_systemd(char *devnm, char *service_name, char *prefix); extern void ident_init(struct mddev_ident *ident); +extern mdadm_status_t ident_set_devname(struct mddev_ident *ident, const char *devname); +extern mdadm_status_t ident_set_name(struct mddev_ident *ident, const char *name); extern int parse_auto(char *str, char *msg, int config); extern struct mddev_ident *conf_get_ident(char *dev); @@ -1646,11 +1655,10 @@ extern int conf_get_monitor_delay(void); extern char *conf_line(FILE *file); extern char *conf_word(FILE *file, int allow_key); extern void print_quoted(char *str); -extern void print_escape(char *str); extern int use_udev(void); extern unsigned long GCD(unsigned long a, unsigned long b); extern int conf_name_is_free(char *name); -extern bool is_devname_ignore(char *devname); +extern bool is_devname_ignore(const char *devname); extern bool is_devname_md_numbered(const char *devname); extern bool is_devname_md_d_numbered(const char *devname); extern int conf_verify_devnames(struct mddev_ident *array_list); @@ -2005,11 +2013,6 @@ enum r0layout { /* And another special number needed for --data_offset=variable */ #define VARIABLE_OFFSET 3 -/** - * This is true for native and DDF, IMSM allows 16. - */ -#define MD_NAME_MAX 32 - /** * is_container() - check if @level is &LEVEL_CONTAINER * @level: level value diff --git a/super-ddf.c b/super-ddf.c index c524265..a87e316 100644 --- a/super-ddf.c +++ b/super-ddf.c @@ -1975,7 +1975,6 @@ static void getinfo_super_ddf(struct supertype *st, struct mdinfo *info, char *m info->array.ctime = DECADE + __be32_to_cpu(*cptr); info->array.chunk_size = 0; - info->container_enough = 1; info->disk.major = 0; info->disk.minor = 0; @@ -1984,12 +1983,14 @@ static void getinfo_super_ddf(struct supertype *st, struct mdinfo *info, char *m info->disk.number = be32_to_cpu(ddf->dlist->disk.refnum); info->disk.raid_disk = find_phys(ddf, ddf->dlist->disk.refnum); + if (info->disk.raid_disk < 0) + return; + info->data_offset = be64_to_cpu(ddf->phys-> entries[info->disk.raid_disk]. config_size); info->component_size = ddf->dlist->size - info->data_offset; - if (info->disk.raid_disk >= 0) - pde = ddf->phys->entries + info->disk.raid_disk; + pde = ddf->phys->entries + info->disk.raid_disk; if (pde && !(be16_to_cpu(pde->state) & DDF_Failed) && !(be16_to_cpu(pde->state) & DDF_Missing)) diff --git a/super-intel.c b/super-intel.c index 77b0066..6bdd5c4 100644 --- a/super-intel.c +++ b/super-intel.c @@ -3806,7 +3806,6 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char * struct intel_super *super = st->sb; struct imsm_disk *disk; int map_disks = info->array.raid_disks; - int max_enough = -1; int i; struct imsm_super *mpb; @@ -3848,12 +3847,9 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char * for (i = 0; i < mpb->num_raid_devs; i++) { struct imsm_dev *dev = get_imsm_dev(super, i); - int failed, enough, j, missing = 0; + int j = 0; struct imsm_map *map; - __u8 state; - failed = imsm_count_failed(super, dev, MAP_0); - state = imsm_check_degraded(super, dev, failed, MAP_0); map = get_imsm_map(dev, MAP_0); /* any newly missing disks? @@ -3868,36 +3864,10 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char * if (!(ord & IMSM_ORD_REBUILD) && get_imsm_missing(super, idx)) { - missing = 1; break; } } - - if (state == IMSM_T_STATE_FAILED) - enough = -1; - else if (state == IMSM_T_STATE_DEGRADED && - (state != map->map_state || missing)) - enough = 0; - else /* we're normal, or already degraded */ - enough = 1; - if (is_gen_migration(dev) && missing) { - /* during general migration we need all disks - * that process is running on. - * No new missing disk is allowed. - */ - max_enough = -1; - enough = -1; - /* no more checks necessary - */ - break; - } - /* in the missing/failed disk case check to see - * if at least one array is runnable - */ - max_enough = max(max_enough, enough); } - dprintf("enough: %d\n", max_enough); - info->container_enough = max_enough; if (super->disks) { __u32 reserved = imsm_reserved_sectors(super, super->disks); @@ -5561,40 +5531,37 @@ static void imsm_update_version_info(struct intel_super *super) } } -static int check_name(struct intel_super *super, char *name, int quiet) +/** + * imsm_check_name() - check imsm naming criteria. + * @super: &intel_super pointer, not NULL. + * @name: name to check. + * @verbose: verbose level. + * + * Name must be no longer than &MAX_RAID_SERIAL_LEN and must be unique across volumes. + * + * Returns: &true if @name matches, &false otherwise. + */ +static bool imsm_is_name_allowed(struct intel_super *super, const char * const name, + const int verbose) { struct imsm_super *mpb = super->anchor; - char *reason = NULL; - char *start = name; - size_t len = strlen(name); int i; - if (len > 0) { - while (isspace(start[len - 1])) - start[--len] = 0; - while (*start && isspace(*start)) - ++start, --len; - memmove(name, start, len + 1); + if (is_string_lq(name, MAX_RAID_SERIAL_LEN + 1) == false) { + pr_vrb("imsm: Name \"%s\" is too long\n", name); + return false; } - if (len > MAX_RAID_SERIAL_LEN) - reason = "must be 16 characters or less"; - else if (len == 0) - reason = "must be a non-empty string"; - 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; + pr_vrb("imsm: Name \"%s\" already exists\n", name); + return false; } } - if (reason && !quiet) - pr_err("imsm volume name %s\n", reason); - - return !reason; + return true; } static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info, @@ -5689,8 +5656,9 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info, } } - if (!check_name(super, name, 0)) + if (imsm_is_name_allowed(super, name, 1) == false) return 0; + dv = xmalloc(sizeof(*dv)); dev = xcalloc(1, sizeof(*dev) + sizeof(__u32) * (info->raid_disks - 1)); /* @@ -8018,7 +7986,7 @@ static int update_subarray_imsm(struct supertype *st, char *subarray, char *ep; int vol; - if (!check_name(super, name, 0)) + if (imsm_is_name_allowed(super, name, 1) == false) return 2; vol = strtoul(subarray, &ep, 10); @@ -10345,7 +10313,8 @@ static void imsm_process_update(struct supertype *st, if (a->info.container_member == target) break; dev = get_imsm_dev(super, u->dev_idx); - if (a || !check_name(super, name, 1)) { + + if (a || !dev || imsm_is_name_allowed(super, name, 0) == false) { dprintf("failed to rename subarray-%d\n", target); break; } diff --git a/super1.c b/super1.c index 856b020..1da71b9 100644 --- a/super1.c +++ b/super1.c @@ -1967,6 +1967,14 @@ fail_to_write: return 1; } +static bool has_raid0_layout(struct mdp_superblock_1 *sb) +{ + if (sb->level == 0 && sb->layout != 0) + return true; + else + return false; +} + static int write_init_super1(struct supertype *st) { struct mdp_superblock_1 *sb = st->sb; @@ -1978,12 +1986,17 @@ static int write_init_super1(struct supertype *st) unsigned long long sb_offset; unsigned long long data_offset; long bm_offset; - int raid0_need_layout = 0; + bool raid0_need_layout = false; + + /* Since linux kernel v5.4, raid0 always has a layout */ + if (has_raid0_layout(sb) && get_linux_version() >= 5004000) + raid0_need_layout = true; for (di = st->info; di; di = di->next) { if (di->disk.state & (1 << MD_DISK_JOURNAL)) sb->feature_map |= __cpu_to_le32(MD_FEATURE_JOURNAL); - if (sb->level == 0 && sb->layout != 0) { + if (has_raid0_layout(sb) && !raid0_need_layout) { + struct devinfo *di2 = st->info; unsigned long long s1, s2; s1 = di->dev_size; @@ -1995,7 +2008,7 @@ static int write_init_super1(struct supertype *st) s2 -= di2->data_offset; s2 /= __le32_to_cpu(sb->chunksize); if (s1 != s2) - raid0_need_layout = 1; + raid0_need_layout = true; } } diff --git a/test b/test index 61d9ee8..b244453 100755 --- a/test +++ b/test @@ -107,8 +107,12 @@ do_test() { echo -ne "$_script... " if ( set -ex ; . $_script ) &> $targetdir/log then - dmesg | grep -iq "error\|call trace\|segfault" && - die "dmesg prints errors when testing $_basename!" + if [ -f "${_script}.inject_error" ]; then + echo "dmesg checking is skipped because test inject error" + else + dmesg | grep -iq "error\|call trace\|segfault" && + die "dmesg prints errors when testing $_basename!" + fi echo "succeeded" _fail=0 else diff --git a/tests/00confnames b/tests/00confnames new file mode 100644 index 0000000..10823f0 --- /dev/null +++ b/tests/00confnames @@ -0,0 +1,107 @@ +set -x -e +. tests/templates/names_template + +# Test how and from config are handled during Incremental assemblation. +# 1-6 only tests (no in config). +# 6-10 and combinations are tested. +# 11-13 corner cases. + +names_create "/dev/md/name" +local _UUID="$(mdadm -D --export /dev/md127 | grep MD_UUID | cut -d'=' -f2)" +[[ "$_UUID" == "" ]] && echo "Cannot obtain UUID for $DEVNODE_NAME" && exit 1 + + +# 1. definition consistent with metadata name. +names_make_conf $_UUID "/dev/md/name" "empty" $config +mdadm -S "/dev/md127" +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "name" "name" +mdadm -S "/dev/md127" + +# 2. Same as 1, but use short name form of . +names_make_conf $_UUID "name" "empty" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "name" "name" +mdadm -S "/dev/md127" + +# 3. Same as 1, but use different than metadata provides. +names_make_conf $_UUID "/dev/md/other" "empty" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "other" "name" +mdadm -S "/dev/md127" + +# 4. Same as 3, but use short name form of . +names_make_conf $_UUID "other" "empty" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "other" "name" +mdadm -S "/dev/md127" + +# 5. Force particular node creation by setting to /dev/mdX. Link is not created in this +# case. +names_make_conf $_UUID "/dev/md4" "empty" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md4" "empty" "name" +mdadm -S "/dev/md4" + +# 6. set to /dev/mdX, same as in metadata. +# Metadata name and default node used - controversial. Current behavior documented. +names_make_conf $_UUID "/dev/md22" "name" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "name" "name" +mdadm -S "/dev/md127" + +# 7. set to /dev/mdX, different than in metadata. +# Metadata name and default node used - controversial. Current behavior documented. +names_make_conf $_UUID "/dev/md8" "other" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "name" "name" +mdadm -S "/dev/md127" + +# 8. Both and different than in metadata. +# Metadata name and default node used - controversial. Current behavior documented. +names_make_conf $_UUID "devnode" "other_name" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "name" "name" +mdadm -S "/dev/md127" + +# 9. set to metadata name, different than in metadata. +# Metadata name and default node used - controversial. Current behavior documented. +names_make_conf $_UUID "name" "other_name" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "name" "name" +mdadm -S "/dev/md127" + +# 10. Bad set, no . +# Metadata name and default node used - expected. +names_make_conf $_UUID "/im/bad/devname" "empty" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "name" "name" +mdadm -S "/dev/md127" + +# 11. with some special symbols and locales, no . +# should be ignored. +names_make_conf $_UUID "tźż-\.,<>st+-" "empty" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "name" "name" +mdadm -S "/dev/md127" + +# 12. No and set. +# Metadata name and default node used - expected. +names_make_conf $_UUID "empty" "empty" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "name" "name" +mdadm -S "/dev/md127" + +# 13. No , set to /dev/mdX. +# Entry should be ignored, it is not ignored but result is good anyway. +names_make_conf $_UUID "empty" "/dev/md12" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "name" "name" +mdadm -S "/dev/md127" + +# 13. No , with special symbols and locales. +# Entry should be ignored, it is not ignored but result is good anyway. +names_make_conf $_UUID "empty" "./\śćń#&" $config +mdadm -I $dev0 --config=$config +names_verify "/dev/md127" "name" "name" +mdadm -S "/dev/md127" diff --git a/tests/00createnames b/tests/00createnames index 64b81b9..a95e7d2 100644 --- a/tests/00createnames +++ b/tests/00createnames @@ -1,93 +1,44 @@ set -x -e +. tests/templates/names_template # Test how and --name= are handled for create mode. -# We need to check three properties, generated from those parameters: -# - devnode name -# - link in /dev/md/ (MD_DEVNAME property from --detail --export) -# - name in metadata (MD_NAME property from --examine --export) - -function _verify() { - local DEVNODE_NAME="$1" - local WANTED_LINK="$2" - local WANTED_NAME="$3" - - local RES="$(mdadm -D --export $DEVNODE_NAME | grep MD_DEVNAME)" - if [[ "$?" != "0" ]]; then - echo "Cannot get details for $DEVNODE_NAME - unexpected devnode." - exit 1 - fi - - if [[ "$WANTED_LINK" != "empty" ]]; then - local EXPECTED="MD_DEVNAME=$WANTED_LINK" - if [[ "$RES" != "$EXPECTED" ]]; then - echo "$RES doesn't match $EXPECTED." - exit 1 - fi - fi - - - local RES="$(mdadm -E --export $dev0 | grep MD_NAME)" - if [[ "$?" != "0" ]]; then - echo "Cannot get metadata from $dev0." - exit 1 - fi - - local EXPECTED="MD_NAME=$(hostname):$WANTED_NAME" - if [[ "$RES" != "$EXPECTED" ]]; then - echo "$RES doesn't match $EXPECTED." - exit 1 - fi -} - -function _create() { - local DEVNAME=$1 - local NAME=$2 - - if [[ -z "$NAME" ]]; then - mdadm -CR "$DEVNAME" -l0 -n 1 $dev0 --force - else - mdadm -CR "$DEVNAME" --name="$NAME" -l0 -n 1 $dev0 --force - fi - - if [[ "$?" != "0" ]]; then - echo "Cannot create device." - exit 1 - fi -} # The most trivial case. -_create "/dev/md/name" -_verify "/dev/md127" "name" "name" +names_create "/dev/md/name" +names_verify "/dev/md127" "name" "name" mdadm -S "/dev/md127" -_create "name" -_verify "/dev/md127" "name" "name" +names_create "name" +names_verify "/dev/md127" "name" "name" mdadm -S "/dev/md127" # Use 'mdX' as name. -_create "/dev/md/md0" -_verify "/dev/md127" "md0" "md0" +names_create "/dev/md/md0" +names_verify "/dev/md127" "md0" "md0" mdadm -S "/dev/md127" -_create "md0" -_verify "/dev/md127" "md0" "md0" +names_create "md0" +names_verify "/dev/md127" "md0" "md0" mdadm -S "/dev/md127" # is used to create MD_DEVNAME but, name is used to create MD_NAME. -_create "/dev/md/devnode" "name" -_verify "/dev/md127" "devnode" "name" +names_create "/dev/md/devnode" "name" +names_verify "/dev/md127" "devnode" "name" mdadm -S "/dev/md127" -_create "devnode" "name" -_verify "/dev/md127" "devnode" "name" +names_create "devnode" "name" +names_verify "/dev/md127" "devnode" "name" mdadm -S "/dev/md127" # Devnode points to /dev/ directory. MD_DEVNAME doesn't exist. -_create "/dev/md0" -_verify "/dev/md0" "empty" "0" +names_create "/dev/md0" +names_verify "/dev/md0" "empty" "0" mdadm -S "/dev/md0" # Devnode points to /dev/ directory and name is set. -_create "/dev/md0" "name" -_verify "/dev/md0" "empty" "name" +names_create "/dev/md0" "name" +names_verify "/dev/md0" "empty" "name" mdadm -S "/dev/md0" + +# Devnode is a special ignore keyword. Should be rejected. +names_create "" "name", "true" diff --git a/tests/06name b/tests/06name index 4d5e824..86eaab6 100644 --- a/tests/06name +++ b/tests/06name @@ -3,8 +3,8 @@ set -x # create an array with a name mdadm -CR $md0 -l0 -n2 --metadata=1 --name="Fred" $dev0 $dev1 -mdadm -E $dev0 | grep 'Name : [^:]*:Fred ' > /dev/null || exit 1 -mdadm -D $md0 | grep 'Name : [^:]*:Fred ' > /dev/null || exit 1 +mdadm -E $dev0 | grep 'Name : Fred' > /dev/null || exit 1 +mdadm -D $md0 | grep 'Name : Fred' > /dev/null || exit 1 mdadm -S $md0 mdadm -A $md0 --name="Fred" $devlist diff --git a/tests/23rdev-lifetime b/tests/23rdev-lifetime new file mode 100644 index 0000000..1750b0d --- /dev/null +++ b/tests/23rdev-lifetime @@ -0,0 +1,34 @@ +devname=${dev0##*/} +devt=`cat /sys/block/$devname/dev` +pid="" +runtime=2 + +clean_up_test() { + pill -9 $pid + echo clear > /sys/block/md0/md/array_state +} + +trap 'clean_up_test' EXIT + +add_by_sysfs() { + while true; do + echo $devt > /sys/block/md0/md/new_dev + done +} + +remove_by_sysfs(){ + while true; do + echo remove > /sys/block/md0/md/dev-${devname}/state + done +} + +echo md0 > /sys/module/md_mod/parameters/new_array || die "create md0 failed" + +add_by_sysfs & +pid="$pid $!" + +remove_by_sysfs & +pid="$pid $!" + +sleep $runtime +exit 0 diff --git a/tests/24raid10deadlock b/tests/24raid10deadlock new file mode 100644 index 0000000..ee330aa --- /dev/null +++ b/tests/24raid10deadlock @@ -0,0 +1,88 @@ +devs="$dev0 $dev1 $dev2 $dev3" +runtime=120 +pid="" +action_pid="" + +set_up_injection() +{ + echo -1 > /sys/kernel/debug/fail_make_request/times + echo 1 > /sys/kernel/debug/fail_make_request/probability + echo 0 > /sys/kernel/debug/fail_make_request/verbose + echo 1 > /sys/block/${1##*/}/make-it-fail +} + +clean_up_injection() +{ + echo 0 > /sys/block/${1##*/}/make-it-fail + echo 0 > /sys/kernel/debug/fail_make_request/times + echo 0 > /sys/kernel/debug/fail_make_request/probability + echo 2 > /sys/kernel/debug/fail_make_request/verbose +} + +test_rdev() +{ + while true; do + mdadm -f $md0 $1 &> /dev/null + mdadm -r $md0 $1 &> /dev/null + mdadm --zero-superblock $1 &> /dev/null + mdadm -a $md0 $1 &> /dev/null + sleep $2 + done +} + +test_write_action() +{ + while true; do + echo frozen > /sys/block/md0/md/sync_action + echo idle > /sys/block/md0/md/sync_action + sleep 0.1 + done +} + +set_up_test() +{ + fio -h &> /dev/null || die "fio not found" + + # create a simple raid10 + mdadm -Cv -R -n 4 -l10 $md0 $devs || die "create raid10 failed" +} + +clean_up_test() +{ + clean_up_injection $dev0 + pkill -9 fio + kill -9 $pid + kill -9 $action_pid + + sleep 1 + + if ps $action_pid | tail -1 | awk '{print $3}' | grep D; then + die "thread that is writing sysfs is stuck in D state, deadlock is triggered" + fi + mdadm -S $md0 +} + +cat /sys/kernel/debug/fail_make_request/times || die "fault injection is not enabled" + +trap 'clean_up_test' EXIT + +set_up_test || die "set up test failed" + +# backgroup io pressure +fio -filename=$md0 -rw=randwrite -direct=1 -name=test -bs=4k -numjobs=16 -iodepth=16 & + +# trigger add/remove device by io failure +set_up_injection $dev0 +test_rdev $dev0 2 & +pid="$pid $!" + +# add/remove device directly +test_rdev $dev3 10 & +pid="$pid $!" + +test_write_action & +action_pid="$!" + +sleep $runtime + +exit 0 diff --git a/tests/24raid10deadlock.inject_error b/tests/24raid10deadlock.inject_error new file mode 100644 index 0000000..e69de29 diff --git a/tests/24raid456deadlock b/tests/24raid456deadlock new file mode 100644 index 0000000..80e6e97 --- /dev/null +++ b/tests/24raid456deadlock @@ -0,0 +1,58 @@ +devs="$dev0 $dev1 $dev2 $dev3 $dev4 $dev5" +runtime=120 +pid="" +old=`cat /proc/sys/vm/dirty_background_ratio` + +test_write_action() +{ + while true; do + echo check > /sys/block/md0/md/sync_action &> /dev/null + sleep 0.1 + echo idle > /sys/block/md0/md/sync_action &> /dev/null + done +} + +test_write_back() +{ + fio -filename=$md0 -bs=4k -rw=write -numjobs=1 -name=test \ + -time_based -runtime=$runtime &> /dev/null +} + +set_up_test() +{ + fio -h &> /dev/null || die "fio not found" + + # create a simple raid6 + mdadm -Cv -R -n 6 -l6 $md0 $devs --assume-clean || die "create raid6 failed" + + # trigger dirty pages write back + echo 0 > /proc/sys/vm/dirty_background_ratio +} + +clean_up_test() +{ + echo $old > /proc/sys/vm/dirty_background_ratio + + pkill -9 fio + kill -9 $pid + + sleep 1 + + if ps $pid | tail -1 | awk '{print $3}' | grep D; then + die "thread that is writing sysfs is stuck in D state, deadlock is triggered" + fi + mdadm -S $md0 +} + +trap 'clean_up_test' EXIT + +set_up_test || die "set up test failed" + +test_write_back & + +test_write_action & +pid="$!" + +sleep $runtime + +exit 0 diff --git a/tests/25raid456-recovery-while-reshape b/tests/25raid456-recovery-while-reshape new file mode 100644 index 0000000..3f6251b --- /dev/null +++ b/tests/25raid456-recovery-while-reshape @@ -0,0 +1,33 @@ +devs="$dev0 $dev1 $dev2" + +set_up_test() +{ + mdadm -Cv -R -n 3 -l5 $md0 $devs --assume-clean --size=50M || die "create array failed" + mdadm -a $md0 $dev3 $dev4 || die "failed to bind new disk to array" + echo 1000 > /sys/block/md0/md/sync_speed_max +} + +clean_up_test() +{ + mdadm -S $md0 +} + +trap 'clean_up_test' EXIT + +set_up_test || die "set up test failed" + +# trigger reshape +mdadm --grow -l 6 $md0 +sleep 1 + +# set up replacement +echo frozen > /sys/block/md0/md/sync_action +echo want_replacement > /sys/block/md0/md/rd0/state +echo reshape > /sys/block/md0/md/sync_action +sleep 1 + +# reassemeble array +mdadm -S $md0 || die "can't stop array" +mdadm --assemble $md0 $devs $dev3 $dev4 || die "can't assemble array" + +exit 0 diff --git a/tests/25raid456-reshape-corrupt-data b/tests/25raid456-reshape-corrupt-data new file mode 100644 index 0000000..fdb875f --- /dev/null +++ b/tests/25raid456-reshape-corrupt-data @@ -0,0 +1,35 @@ +devs="$dev0 $dev1 $dev2" + +set_up_test() +{ + mdadm -Cv -R -n 3 -l5 $md0 $devs --size=50M || die "create array failed" + mdadm -a $md0 $dev3 || die "failed to bind new disk to array" + mkfs.xfs -f $md0 || die "mkfs failed" + xfs_ncheck $md0 || die "check fs failed" +} + +clean_up_test() +{ + mdadm -S $md0 +} + +trap 'clean_up_test' EXIT + +set_up_test || die "set up test failed" + +# trigger reshape +echo 1000 > /sys/block/md0/md/sync_speed_max +mdadm --grow -l 6 $md0 +sleep 1 + +# stop and start reshape +echo frozen > /sys/block/md0/md/sync_action +echo system > /sys/block/md0/md/sync_speed_max +echo reshape > /sys/block/md0/md/sync_action + +mdadm -W $md0 + +# check if data is corrupted +xfs_ncheck $md0 || die "data is corrupted after reshape" + +exit 0 diff --git a/tests/25raid456-reshape-deadlock b/tests/25raid456-reshape-deadlock new file mode 100644 index 0000000..bfa0cc5 --- /dev/null +++ b/tests/25raid456-reshape-deadlock @@ -0,0 +1,34 @@ +devs="$dev0 $dev1 $dev2" + +set_up_test() +{ + mdadm -Cv -R -n 3 -l5 $md0 $devs --size=50M || die "create array failed" + mdadm -a $md0 $dev3 || die "failed to bind new disk to array" + echo 1000 > /sys/block/md0/md/sync_speed_max +} + +clean_up_test() +{ + echo idle > /sys/block/md0/md/sync_action + mdadm -S $md0 +} + +trap 'clean_up_test' EXIT + +set_up_test || die "set up test failed" + +# trigger reshape +mdadm --grow -l 6 $md0 +sleep 1 + +# stop reshape +echo frozen > /sys/block/md0/md/sync_action + +# read accross reshape +dd if=$md0 of=/dev/NULL bs=1m count=100 iflag=direct &> /dev/null & +sleep 2 + +# suspend array +echo 1 > /sys/block/md0/md/suspend_lo + +exit 0 diff --git a/tests/25raid456-reshape-while-recovery b/tests/25raid456-reshape-while-recovery new file mode 100644 index 0000000..b9f871f --- /dev/null +++ b/tests/25raid456-reshape-while-recovery @@ -0,0 +1,32 @@ +devs="$dev0 $dev1 $dev2" + +set_up_test() +{ + mdadm -Cv -R -n 3 -l5 $md0 $devs --assume-clean --size=50M || die "create array failed" + mdadm -a $md0 $dev3 $dev4 || die "failed to bind new disk to array" + echo 1000 > /sys/block/md0/md/sync_speed_max +} + +clean_up_test() +{ + mdadm -S $md0 +} + +trap 'clean_up_test' EXIT + +set_up_test || die "set up test failed" + +# set up replacement +echo want_replacement > /sys/block/md0/md/rd0/state +sleep 1 + +# trigger reshape +echo frozen > /sys/block/md0/md/sync_action +mdadm --grow -l 6 $md0 +sleep 1 + +# reassemeble array +mdadm -S $md0 || die "can't stop array" +mdadm --assemble $md0 $devs $dev3 $dev4 || die "can't assemble array" + +exit 0 diff --git a/tests/func.sh b/tests/func.sh index 9710a53..5053b01 100644 --- a/tests/func.sh +++ b/tests/func.sh @@ -170,7 +170,6 @@ do_setup() { dd if=/dev/zero of=$targetdir/mdtest$d count=$sz bs=1K > /dev/null 2>&1 # make sure udev doesn't touch mdadm --zero $targetdir/mdtest$d 2> /dev/null - [ -b /dev/loop$d ] || mknod /dev/loop$d b 7 $d if [ $d -eq 7 ] then losetup /dev/loop$d $targetdir/mdtest6 # for multipath use diff --git a/tests/templates/names_template b/tests/templates/names_template new file mode 100644 index 0000000..6181bfa --- /dev/null +++ b/tests/templates/names_template @@ -0,0 +1,80 @@ +# NAME is optional. Testing with native 1.2 superblock. +function names_create() { + local DEVNAME=$1 + local NAME=$2 + local NEG_TEST=$3 + + if [[ -z "$NAME" ]]; then + mdadm -CR "$DEVNAME" -l0 -n 1 $dev0 --force + else + mdadm -CR "$DEVNAME" --name="$NAME" --metadata=1.2 -l0 -n 1 $dev0 --force + fi + + if [[ "$NEG_TEST" == "true" ]]; then + [[ "$?" == "0" ]] && return 0 + echo "Negative verification failed" + exit 1 + fi + + if [[ "$?" != "0" ]]; then + echo "Cannot create device." + exit 1 + fi +} + +# Three properties to check: +# - devnode name +# - link in /dev/md/ (MD_DEVNAME property from --detail --export) +# - name in metadata (MD_NAME property from --detail --export)- that works only with 1.2 sb. +function names_verify() { + local DEVNODE_NAME="$1" + local WANTED_LINK="$2" + local WANTED_NAME="$3" + + local RES="$(mdadm -D --export $DEVNODE_NAME | grep MD_DEVNAME)" + if [[ "$?" != "0" ]]; then + echo "Cannot get details for $DEVNODE_NAME - unexpected devnode." + exit 1 + fi + + if [[ "$WANTED_LINK" != "empty" ]]; then + local EXPECTED="MD_DEVNAME=$WANTED_LINK" + fi + + if [[ "$RES" != "$EXPECTED" ]]; then + echo "$RES doesn't match $EXPECTED." + exit 1 + fi + + local RES="$(mdadm -D --export $DEVNODE_NAME | grep MD_NAME)" + if [[ "$?" != "0" ]]; then + echo "Cannot get metadata from $dev0." + exit 1 + fi + + local EXPECTED="MD_NAME=$(hostname):$WANTED_NAME" + if [[ "$RES" != "$EXPECTED" ]]; then + echo "$RES doesn't match $EXPECTED." + exit 1 + fi +} + +# Generate ARRAYLINE for tested array. +names_make_conf() { + local UUID="$1" + local WANTED_DEVNAME="$2" + local WANTED_NAME="$3" + local CONF="$4" + + local LINE="ARRAY metadata=1.2 UUID=$UUID" + + if [[ "$WANTED_DEVNAME" != "empty" ]]; then + LINE="$LINE $WANTED_DEVNAME" + fi + + if [[ "$WANTED_NAME" != "empty" ]]; then + LINE="$LINE name=$WANTED_NAME" + fi + + echo $LINE > $CONF +}