From 69207ff6ac9d905c096597de4f37ff9524265d9b Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 4 Nov 2008 20:50:21 +1100 Subject: [PATCH] mdopen: Introduce new rules for creating device name. MORE CONTENT HERE --- Assemble.c | 76 ++++-------- Build.c | 4 +- Create.c | 3 +- mdadm.h | 7 +- mdassemble.c | 3 +- mdopen.c | 334 +++++++++++++++++++++++++++++++-------------------- 6 files changed, 242 insertions(+), 185 deletions(-) diff --git a/Assemble.c b/Assemble.c index c23da0d3..742bc084 100644 --- a/Assemble.c +++ b/Assemble.c @@ -142,7 +142,8 @@ int Assemble(struct supertype *st, char *mddev, mdu_array_info_t tmp_inf; char *avail; int nextspare = 0; - int uuid_for_name = 0; + char *name; + int trustworthy; memset(&info, 0, sizeof(info)); @@ -331,29 +332,6 @@ int Assemble(struct supertype *st, char *mddev, if (auto_assem) { if (tst == NULL || tst->sb == NULL) continue; - switch(tst->ss->match_home(tst, homehost)) - { - case 1: /* happy with match. */ - break; - case -1: /* cannot match */ - uuid_for_name = 1; - break; - case 0: /* Doesn't match */ - if (update) - /* We are changing the name*/ - break; - if ((inargv && verbose >= 0) || verbose > 0) - fprintf(stderr, Name ": %s is not built for " - "host %s - using UUID for " - "device name.\n", - devname, homehost); - - /* Auto-assemble, and this is not a usable host */ - /* if update != NULL, we are updating the host - * name... */ - uuid_for_name = 1; - break; - } } /* If we are this far, then we are nearly commited to this device. * If the super_block doesn't exist, or doesn't match others, @@ -425,36 +403,32 @@ int Assemble(struct supertype *st, char *mddev, if (!st || !st->sb) return 2; - /* Now need to open array the device. - * We create a name '/dev/md/XXX' based on the info in the - * superblock, and call create_mddev on that - */ - - if (mddev == NULL) { - char nbuf[64]; - char *c; - int rc; + /* Now need to open array the device. Use create_mddev */ + st->ss->getinfo_super(st, &info); - st->ss->getinfo_super(st, &info); - if (uuid_for_name) - c = fname_from_uuid(st, &info, nbuf, '-'); - else { - c = strchr(info.name, ':'); - if (c) c++; else c= info.name; - } - if (isdigit(*c) && ((ident->autof & 7)==4 || (ident->autof&7)==6)) - /* /dev/md/d0 style for partitionable */ - rc = asprintf(&mddev, "/dev/md/d%s", c); + trustworthy = FOREIGN; + switch (st->ss->match_home(st, homehost)) { + case 0: + trustworthy = FOREIGN; + name = info.name; + break; + case 1: + trustworthy = LOCAL; + name = strchr(info.name, ':'); + if (name) + name++; else - rc = asprintf(&mddev, "/dev/md/%s", c); - if (rc < 0) { - st->ss->free_super(st); - free(devices); - mddev = NULL; - goto try_again; - } + name = info.name; + break; + case -1: + if (info.name[0] == 0 && info.array.level == LEVEL_CONTAINER) { + name = info.text_version; + trustworthy = METADATA; + } else + trustworthy = FOREIGN; + break; } - mdfd = create_mddev(mddev, ident->autof); + mdfd = create_mddev(mddev, name, ident->autof, trustworthy, NULL); if (mdfd < 0) { st->ss->free_super(st); free(devices); diff --git a/Build.c b/Build.c index 1e751d13..789aca45 100644 --- a/Build.c +++ b/Build.c @@ -113,8 +113,8 @@ int Build(char *mddev, int chunk, int level, int layout, break; } - /* We need to create the device */ - mdfd = create_mddev(mddev, autof); + /* We need to create the device. It can have no name. */ + mdfd = create_mddev(mddev, NULL, autof, LOCAL, NULL); if (mdfd < 0) return 1; diff --git a/Create.c b/Create.c index 87cfebf9..ab4406e1 100644 --- a/Create.c +++ b/Create.c @@ -421,7 +421,7 @@ int Create(struct supertype *st, char *mddev, } /* We need to create the device */ - mdfd = create_mddev(mddev, autof); + mdfd = create_mddev(mddev, name, autof, LOCAL, NULL); if (mdfd < 0) return 1; @@ -514,6 +514,7 @@ int Create(struct supertype *st, char *mddev, * /dev/md/home -> home * /dev/mdhome -> home */ + /* FIXME compare this with rules in create_mddev */ name = strrchr(mddev, '/'); if (name) { name++; diff --git a/mdadm.h b/mdadm.h index 69afe7cc..5e76be7e 100644 --- a/mdadm.h +++ b/mdadm.h @@ -801,7 +801,12 @@ extern char *get_md_name(int dev); extern char DefaultConfFile[]; -extern int create_mddev(char *dev, int autof); +extern int create_mddev(char *dev, char *name, int autof, int trustworthy, + char *chosen); +/* values for 'trustworthy' */ +#define LOCAL 1 +#define FOREIGN 2 +#define METADATA 3 extern int open_mddev(char *dev, int report_errors); extern int create_mddev_devnum(char *devname, int devnum, char *name, char *chosen_name, int parts); diff --git a/mdassemble.c b/mdassemble.c index 1e3f1041..270ced26 100644 --- a/mdassemble.c +++ b/mdassemble.c @@ -69,7 +69,8 @@ int open_mddev(char *dev, int report_errors/*unused*/) } return mdfd; } -int create_mddev(char *dev, int autof/*unused*/) +int create_mddev(char *dev, char *name, int autof/*unused*/, int trustworthy, + char *chosen) { return open_mddev(dev, 0); } diff --git a/mdopen.c b/mdopen.c index 285d811b..7dfa0ba5 100644 --- a/mdopen.c +++ b/mdopen.c @@ -98,24 +98,54 @@ void make_parts(char *dev, int cnt, int symlinks) /* - * Open a given md device, and check that it really is one. - * If 'autof' is given, then we need to create, or recreate, the md device. - * If the name already exists, and is not a block device, we fail. - * If it exists and is not an md device, is not the right type (partitioned or not), - * or is currently in-use, we remove the device, but remember the owner and mode. - * If it now doesn't exist, we find a new md array and create the device. - * Default ownership/mode comes from config file. + * We need a new md device to assemble/build/create an array. + * 'dev' is a name given us by the user (command line or mdadm.conf) + * It might start with /dev or /dev/md any might end with a digit + * string. + * If it starts with just /dev, it must be /dev/mdX or /dev/md_dX + * If it ends with a digit string, then it must be as above, or + * 'trustworthy' must be 'METADATA' and the 'dev' must be + * /dev/md/'name'NN or 'name'NN + * If it doesn't end with a digit string, it must be /dev/md/'name' + * or 'name' or must be NULL. + * If the digit string is present, it gives the minor number to use + * If not, we choose a high, unused minor number. + * If the 'dev' is a standard name, it devices whether 'md' or 'mdp'. + * else if the name is 'd[0-9]+' then we use mdp + * else if trustworthy is 'METADATA' we use md + * else the choice depends on 'autof'. + * If name is NULL it is assumed to match whatever dev provides. + * If both name and dev are NULL, we choose a name 'mdXX' or 'mdpXX' + * + * If 'name' is given, and 'trustworthy' is 'foreign' and name is not + * supported by 'dev', we add a "_%d" suffix based on the minor number + * use that. + * + * If udev is configured, we create a temporary device, open it, and + * unlink it. + * If not, we create the /dev/mdXX device, and is name is usable, + * /dev/md/name + * In any case we return /dev/md/name or (if that isn't available) + * /dev/mdXX in 'chosen'. + * + * When we create devices, we use uid/gid/umask from config file. */ -int create_mddev(char *dev, int autof) + +int create_mddev(char *dev, char *name, int autof, int trustworthy, + char *chosen) { int mdfd; struct stat stb; - int major_num = MD_MAJOR; - int minor_num = 0; - int must_remove = 0; - int num; + int num = -1; + int use_mdp = -1; struct createinfo *ci = conf_get_create_info(); int parts; + char *cname; + char devname[20]; + char cbuf[400]; + if (chosen == NULL) + chosen = cbuf; + if (autof == 0) autof = ci->autof; @@ -123,141 +153,187 @@ int create_mddev(char *dev, int autof) parts = autof >> 3; autof &= 7; - if (autof && autof != 1) { - /* autof is set, so we need to check that the name is ok, - * and possibly create one if not - */ - int std; - stb.st_mode = 0; - if (stat(dev, &stb)==0 && ! S_ISBLK(stb.st_mode)) { - fprintf(stderr, Name ": %s is not a block device.\n", - dev); - return -1; - } - /* check major number is correct */ - num = -1; - std = is_standard(dev, &num); - if (std>0) major_num = get_mdp_major(); - switch(autof) { - case 2: /* only create is_standard names */ - if (!std && !stb.st_mode) { - fprintf(stderr, Name - ": %s does not exist and is not a 'standard' name " - "so it cannot be created\n", dev); - return -1; - } - break; - case 3: /* create md, reject std>0 */ - if (std > 0) { - fprintf(stderr, Name ": that --auto option " - "not compatable with device named %s\n", dev); - return -1; - } - break; - case 4: /* create mdp, reject std<0 */ - if (std < 0) { - fprintf(stderr, Name ": that --auto option " - "not compatable with device named %s\n", dev); + strcpy(chosen, "/dev/md/"); + cname = chosen + strlen(chosen); + + + if (dev) { + + if (strncmp(dev, "/dev/md/", 8) == 0) { + strcpy(cname, dev+8); + } else if (strncmp(dev, "/dev/", 5) == 0) { + char *e = dev + strlen(dev); + while (e > dev && isdigit(e[-1])) + e--; + if (e[0]) + num = strtoul(e, NULL, 10); + strcpy(cname, dev+5); + cname[e-(dev+5)] = 0; + /* name *must* be mdXX or md_dXX in this context */ + if (num < 0 || + (strcmp(cname, "md") != 0 && strcmp(cname, "md_d") != 0)) { + fprintf(stderr, Name ": %s is an invalid name " + "for an md device. Try /dev/md/%s\n", + dev, dev+5); return -1; } - major_num = get_mdp_major(); - break; - case 5: /* default to md if not standard */ - break; - case 6: /* default to mdp if not standard */ - if (std == 0) major_num = get_mdp_major(); - break; + if (strcmp(cname, "md") == 0) + use_mdp = 0; + else + use_mdp = 1; + } else + strcpy(cname, dev); + + /* 'cname' must not contain a slash, may not start or end + * with a digit, and may only be empty if num is present. + */ + if (strchr(cname, '/') != NULL || + isdigit(cname[0]) || + (cname[0] && isdigit(cname[strlen(cname)])) + ) { + fprintf(stderr, Name ": %s is an invalid name " + "for an md device.\n", dev); + return -1; } - /* major is final. num is -1 if not standard */ - if (stb.st_mode && major(stb.st_rdev) != major_num) - must_remove = 1; - if (stb.st_mode && !must_remove) { - /* looks ok, see if it is available */ - mdfd = open(dev, O_RDWR); - if (mdfd < 0) { - fprintf(stderr, Name ": error opening %s: %s\n", - dev, strerror(errno)); - return -1; - } else if (md_get_version(mdfd) <= 0) { - fprintf(stderr, Name ": %s does not appear to be an md device\n", - dev); - close(mdfd); - return -1; - } - if (major_num != MD_MAJOR && parts > 0) - make_parts(dev, parts, ci->symlinks); - return mdfd; + if (cname[0] == 0 && num < 0) { + fprintf(stderr, Name ": %s is an invalid name " + "for an md device (empty!).", dev); + return -1; } - /* Ok, need to find a minor that is not in use. - * If the device name is in a 'standard' format, - * intuit the minor from that, else - * easiest to read /proc/mdstat, and hunt through for - * an unused number - */ - if (num < 0) { - /* need to pick an unused number */ - int num = find_free_devnum(major_num != MD_MAJOR); + } - if (major_num == MD_MAJOR) - minor_num = num; - else - minor_num = (-1-num) << MdpMinorShift; - } else if (major_num == MD_MAJOR) - minor_num = num; + /* Now determine device number */ + /* named 'METADATA' cannot use 'mdp'. */ + if (name && name[0] == 0) + name = NULL; + if (name && trustworthy == METADATA && use_mdp == 1) { + fprintf(stderr, Name ": %s is not allowed for a %s container. " + "Consider /dev/md%d.\n", dev, name, num); + return -1; + } + if (name && trustworthy == METADATA) + use_mdp = 0; + if (use_mdp == -1) { + if (autof == 4 || autof == 6) + use_mdp = 1; else - minor_num = num << MdpMinorShift; - /* major and minor have been chosen */ + use_mdp = 0; + } + if (num < 0 && trustworthy == LOCAL && name) { + /* if name is numeric, us that for num */ + char *ep; + num = strtoul(name, &ep, 10); + if (ep == name || *ep) + num = -1; + } + + if (num < 0) { + /* need to choose a free number. */ + num = find_free_devnum(use_mdp); + if (num == NoMdDev) { + fprintf(stderr, Name ": No avail md devices - aborting\n"); + return -1; + } + } else { + num = use_mdp ? (-1-num) : num; + if (mddev_busy(num)) { + fprintf(stderr, Name ": %s is already in use.\n", + dev); + return -1; + } + } - /* If it was a 'standard' name and it is in-use, then - * the device could already be correct + if (num < 0) + sprintf(devname, "/dev/md_d%d", -1-num); + else + sprintf(devname, "/dev/md%d", num); + + if (cname[0] == 0 && name) { + /* Need to find a name if we can + * We don't completely trust 'name'. Truncate to + * reasonable length and remove '/' */ - if (stb.st_mode && major(stb.st_rdev) == major_num && - minor(stb.st_rdev) == minor_num) - ; - else { - if (major(makedev(major_num,minor_num)) != major_num || - minor(makedev(major_num,minor_num)) != minor_num) { - fprintf(stderr, Name ": Need newer C library to use more than 4 partitionable md devices, sorry\n"); + char *cp; + strncpy(cname, name, 200); + cname[200] = 0; + while ((cp = strchr(cname, '/')) != NULL) + *cp = '-'; + if (trustworthy == METADATA) + /* always add device number to metadata */ + sprintf(cname+strlen(cname), "%d", num); + else if (trustworthy == FOREIGN && + strchr(cname, ':') == NULL) + /* add _%d to FOREIGN array that don't have + * a 'host:' prefix + */ + sprintf(cname+strlen(cname), "_%d", num<0?(-1-num):num); + } + if (cname[0] == 0) + strcpy(chosen, devname); + + /* We have a device number and name. + * If we can detect udev, just open the device and we + * are done. + */ + if (stat("/dev/.udev", &stb) != 0 || + check_env("MDADM_NO_UDEV")) { + /* Make sure 'devname' exists and 'chosen' is a symlink to it */ + if (lstat(devname, &stb) == 0) { + /* Must be the correct device, else error */ + if ((stb.st_mode&S_IFMT) != S_IFBLK || + stb.st_rdev != makedev(dev2major(num),dev2minor(num))) { + fprintf(stderr, Name ": %s exists but looks wrong, please fix\n", + devname); return -1; } - if (must_remove) - unlink(dev); - - if (strncmp(dev, "/dev/md/", 8) == 0) { - if (mkdir("/dev/md",0700)==0) { - if (chown("/dev/md", ci->uid, ci->gid)) - perror("chown /dev/md"); - if (chmod("/dev/md", ci->mode| ((ci->mode>>2) & 0111))) - perror("chmod /dev/md"); - } - } - if (mknod(dev, S_IFBLK|0600, makedev(major_num, minor_num))!= 0) { - fprintf(stderr, Name ": failed to create %s\n", dev); + } else { + if (mknod(devname, S_IFBLK|0600, + makedev(dev2major(num),dev2minor(num))) != 0) { + fprintf(stderr, Name ": failed to create %s\n", + devname); return -1; } - if (must_remove) { - if (chown(dev, stb.st_uid, stb.st_gid)) - perror("chown"); - if (chmod(dev, stb.st_mode & 07777)) - perror("chmod"); - } else { - if (chown(dev, ci->uid, ci->gid)) - perror("chown"); - if (chmod(dev, ci->mode)) - perror("chmod"); + if (chown(devname, ci->uid, ci->gid)) + perror("chown"); + if (chmod(devname, ci->mode)) + perror("chmod"); + stat(devname, &stb); + add_dev(devname, &stb, 0, NULL); + } + if (strcmp(chosen, devname) != 0) { + + if (mkdir("/dev/md",0700)==0) { + if (chown("/dev/md", ci->uid, ci->gid)) + perror("chown /dev/md"); + if (chmod("/dev/md", ci->mode| ((ci->mode>>2) & 0111))) + perror("chmod /dev/md"); } - stat(dev, &stb); - add_dev(dev, &stb, 0, NULL); - if (ci->symlinks && strncmp(dev, "/dev/md/", 8) == 0) - make_dev_symlink(dev); - if (major_num != MD_MAJOR) - make_parts(dev,parts, ci->symlinks); + + if (dev && strcmp(chosen, dev) == 0) + /* We know we are allowed to use this name */ + unlink(chosen); + + if (lstat(chosen, &stb) == 0) { + char buf[300]; + if ((stb.st_mode & S_IFMT) != S_IFLNK || + readlink(chosen, buf, 300) <0 || + strcmp(buf, devname) != 0) { + fprintf(stderr, Name ": %s exists - ignoring\n", + chosen); + strcpy(chosen, devname); + } + } else + symlink(devname, chosen); } } - mdfd = open_mddev(dev, 1); + mdfd = open_dev_excl(num); + if (mdfd < 0) + fprintf(stderr, Name ": unexpected failure opening %s\n", + devname); return mdfd; } + /* Open this and check that it is an md device. * On success, return filedescriptor. * On failure, return -1 if it doesn't exist, -- 2.39.2