X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=blobdiff_plain;f=mdopen.c;h=98c54e433a5df1986af0e12dadb996b0f4f0836b;hp=32ccdbbe404f2cb977e73c38a578b1cd8e3d5c86;hb=7039d1f8200b9599b23db5953934fdb43b0442e0;hpb=f2e55eccfb92969c3e11bc5d4883315f2e866a14 diff --git a/mdopen.c b/mdopen.c index 32ccdbbe..98c54e43 100644 --- a/mdopen.c +++ b/mdopen.c @@ -1,7 +1,7 @@ /* * mdadm - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001-2006 Neil Brown + * Copyright (C) 2001-2013 Neil Brown * * * This program is free software; you can redistribute it and/or modify @@ -19,19 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Neil Brown - * Email: - * Paper: Neil Brown - * School of Computer Science and Engineering - * The University of New South Wales - * Sydney, 2052 - * Australia + * Email: */ #include "mdadm.h" #include "md_p.h" #include - void make_parts(char *dev, int cnt) { /* make 'cnt' partition devices for 'dev' @@ -43,35 +37,44 @@ void make_parts(char *dev, int cnt) * else that of dev */ struct stat stb; - int major_num = major_num; /* quiet gcc -Os unitialized warning */ - int minor_num = minor_num; /* quiet gcc -Os unitialized warning */ - int odig = odig; /* quiet gcc -Os unitialized warning */ + int major_num; + int minor_num; + int odig; int i; int nlen = strlen(dev) + 20; - char *name = malloc(nlen); + char *name; int dig = isdigit(dev[strlen(dev)-1]); - char orig[1024]; + char orig[1001]; char sym[1024]; + int err; - if (cnt==0) cnt=4; + if (cnt == 0) + cnt = 4; if (lstat(dev, &stb)!= 0) return; - if (S_ISLNK(stb.st_mode)) { - int len = readlink(dev, orig, sizeof(orig)); - if (len < 0 || len > 1000) + + if (S_ISBLK(stb.st_mode)) { + major_num = major(stb.st_rdev); + minor_num = minor(stb.st_rdev); + odig = -1; + } else if (S_ISLNK(stb.st_mode)) { + int len; + + len = readlink(dev, orig, sizeof(orig)); + if (len < 0 || len >= (int)sizeof(orig)) return; orig[len] = 0; odig = isdigit(orig[len-1]); - } else if (S_ISBLK(stb.st_mode)) { - major_num = major(stb.st_rdev); - minor_num = minor(stb.st_rdev); + major_num = -1; + minor_num = -1; } else - return; - for (i=1; i <= cnt ; i++) { + return; + name = xmalloc(nlen); + for (i = 1; i <= cnt ; i++) { struct stat stb2; snprintf(name, nlen, "%s%s%d", dev, dig?"p":"", i); - if (stat(name, &stb2)==0) { - if (!S_ISBLK(stb2.st_mode)) + if (stat(name, &stb2) == 0) { + if (!S_ISBLK(stb2.st_mode) || !S_ISBLK(stb.st_mode)) continue; if (stb2.st_rdev == makedev(major_num, minor_num+i)) continue; @@ -87,15 +90,42 @@ void make_parts(char *dev, int cnt) perror("chown"); if (chmod(name, stb2.st_mode & 07777)) perror("chmod"); + err = 0; } else { - snprintf(sym, 10000, "%s%s%d", orig, odig?"p":"", i); - symlink(sym, name); + snprintf(sym, sizeof(sym), "%s%s%d", orig, odig?"p":"", i); + err = symlink(sym, name); } - stat(name, &stb2); - add_dev(name, &stb2, 0, NULL); + + if (err == 0 && stat(name, &stb2) == 0) + add_dev(name, &stb2, 0, NULL); } + free(name); } +int create_named_array(char *devnm) +{ + int fd; + int n = -1; + static const char new_array_file[] = { + "/sys/module/md_mod/parameters/new_array" + }; + + fd = open(new_array_file, O_WRONLY); + if (fd < 0 && errno == ENOENT) { + if (system("modprobe md_mod") == 0) + fd = open(new_array_file, O_WRONLY); + } + if (fd >= 0) { + n = write(fd, devnm, strlen(devnm)); + close(fd); + } + if (fd < 0 || n != (int)strlen(devnm)) { + pr_err("Fail create %s when using %s\n", devnm, new_array_file); + return 0; + } + + return 1; +} /* * We need a new md device to assemble/build/create an array. @@ -121,9 +151,9 @@ void make_parts(char *dev, int cnt) * 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 + * 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, + * If not, we create the /dev/mdXX device, and if name is usable, * /dev/md/name * In any case we return /dev/md/name or (if that isn't available) * /dev/mdXX in 'chosen'. @@ -132,7 +162,7 @@ void make_parts(char *dev, int cnt) */ int create_mddev(char *dev, char *name, int autof, int trustworthy, - char *chosen) + char *chosen, int block_udev) { int mdfd; struct stat stb; @@ -141,12 +171,16 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, struct createinfo *ci = conf_get_create_info(); int parts; char *cname; - char devname[20]; + char devname[37]; + char devnm[32]; char cbuf[400]; + + if (!use_udev()) + block_udev = 0; + if (chosen == NULL) chosen = cbuf; - if (autof == 0) autof = ci->autof; @@ -156,9 +190,7 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, 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) { @@ -172,8 +204,7 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, /* 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", + pr_err("%s is an invalid name for an md device. Try /dev/md/%s\n", dev, dev+5); return -1; } @@ -190,13 +221,11 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, * empty. */ if (strchr(cname, '/') != NULL) { - fprintf(stderr, Name ": %s is an invalid name " - "for an md device.\n", dev); + pr_err("%s is an invalid name for an md device.\n", dev); return -1; } if (cname[0] == 0) { - fprintf(stderr, Name ": %s is an invalid name " - "for an md device (empty!).", dev); + pr_err("%s is an invalid name for an md device (empty!).\n", dev); return -1; } if (num < 0) { @@ -207,7 +236,10 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, char *ep; if (cname[0] == 'd') sp++; - num = strtoul(sp, &ep, 10); + if (isdigit(sp[0])) + num = strtoul(sp, &ep, 10); + else + ep = sp; if (ep == sp || *ep || num < 0) num = -1; else if (cname[0] == 'd') @@ -222,8 +254,7 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, 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); + pr_err("%s is not allowed for a %s container. Consider /dev/md%d.\n", dev, name, num); return -1; } if (name && trustworthy == METADATA) @@ -235,34 +266,27 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, use_mdp = 0; } if (num < 0 && trustworthy == LOCAL && name) { - /* if name is numeric, us that for num */ + /* if name is numeric, possibly prefixed by + * 'md' or '/dev/md', use that for num + * if it is not already in use */ char *ep; - num = strtoul(name, &ep, 10); - if (ep == name || *ep) + char *n2 = name; + if (strncmp(n2, "/dev/", 5) == 0) + n2 += 5; + if (strncmp(n2, "md", 2) == 0) + n2 += 2; + if (*n2 == '/') + n2++; + num = strtoul(n2, &ep, 10); + if (ep == n2 || *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; + else { + sprintf(devnm, "md%s%d", use_mdp ? "_d":"", num); + if (mddev_busy(devnm)) + num = -1; } } - 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 @@ -275,8 +299,17 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, int cnlen; strncpy(cname, name, 200); cname[200] = 0; - while ((cp = strchr(cname, '/')) != NULL) - *cp = '-'; + for (cp = cname; *cp ; cp++) + switch (*cp) { + case '/': + *cp = '-'; + break; + case ' ': + case '\t': + *cp = '_'; + break; + } + if (trustworthy == LOCAL || (trustworthy == FOREIGN && strchr(cname, ':') != NULL)) { /* Only need suffix if there is a conflict */ @@ -285,10 +318,10 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, } cnlen = strlen(cname); while (conflict) { - if (trustworthy == METADATA) + if (trustworthy == METADATA && !isdigit(cname[cnlen-1])) sprintf(cname+cnlen, "%d", unum); else - /* add _%d to FOREIGN array that don't + /* add _%d to FOREIGN array that don't * a 'host:' prefix */ sprintf(cname+cnlen, "_%d", unum); @@ -297,28 +330,72 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, conflict = 0; } } - if (cname[0] == 0) + + devnm[0] = 0; + if (num < 0 && cname && ci->names) { + sprintf(devnm, "md_%s", cname); + if (block_udev) + udev_block(devnm); + if (!create_named_array(devnm)) { + devnm[0] = 0; + udev_unblock(); + } + } + if (num >= 0) { + sprintf(devnm, "md%d", num); + if (block_udev) + udev_block(devnm); + if (!create_named_array(devnm)) { + devnm[0] = 0; + udev_unblock(); + } + } + if (devnm[0] == 0) { + if (num < 0) { + /* need to choose a free number. */ + char *_devnm = find_free_devnm(use_mdp); + if (_devnm == NULL) { + pr_err("No avail md devices - aborting\n"); + return -1; + } + strcpy(devnm, _devnm); + } else { + sprintf(devnm, "%s%d", use_mdp?"md_d":"md", num); + if (mddev_busy(devnm)) { + pr_err("%s is already in use.\n", + dev); + return -1; + } + } + if (block_udev) + udev_block(devnm); + } + + sprintf(devname, "/dev/%s", devnm); + + if (dev && dev[0] == '/') + strcpy(chosen, dev); + else if (cname[0] == 0) strcpy(chosen, devname); /* We have a device number and name. * If we cannot detect udev, we need to make * devices and links ourselves. */ - if (stat("/dev/.udev", &stb) != 0 || - check_env("MDADM_NO_UDEV")) { + if (!use_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", + stb.st_rdev != devnm2devid(devnm)) { + pr_err("%s exists but looks wrong, please fix\n", devname); return -1; } } else { if (mknod(devname, S_IFBLK|0600, - makedev(dev2major(num),dev2minor(num))) != 0) { - fprintf(stderr, Name ": failed to create %s\n", + devnm2devid(devnm)) != 0) { + pr_err("failed to create %s\n", devname); return -1; } @@ -331,9 +408,9 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, } if (use_mdp == 1) make_parts(devname, parts); - if (strcmp(chosen, devname) != 0) { - if (mkdir("/dev/md",0700)==0) { + 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))) @@ -346,27 +423,31 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, if (lstat(chosen, &stb) == 0) { char buf[300]; + ssize_t link_len = readlink(chosen, buf, sizeof(buf)-1); + if (link_len >= 0) + buf[link_len] = '\0'; + if ((stb.st_mode & S_IFMT) != S_IFLNK || - readlink(chosen, buf, 300) <0 || + link_len < 0 || strcmp(buf, devname) != 0) { - fprintf(stderr, Name ": %s exists - ignoring\n", + pr_err("%s exists - ignoring\n", chosen); strcpy(chosen, devname); } - } else - symlink(devname, chosen); + } else if (symlink(devname, chosen) != 0) + pr_err("failed to create %s: %s\n", + chosen, strerror(errno)); if (use_mdp && strcmp(chosen, devname) != 0) make_parts(chosen, parts); } } - mdfd = open_dev_excl(num); + mdfd = open_dev_excl(devnm); if (mdfd < 0) - fprintf(stderr, Name ": unexpected failure opening %s\n", + pr_err("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, @@ -374,19 +455,54 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, */ int open_mddev(char *dev, int report_errors) { - int mdfd = open(dev, O_RDWR); + int mdfd = open(dev, O_RDONLY); + if (mdfd < 0) { if (report_errors) - fprintf(stderr, Name ": error opening %s: %s\n", + pr_err("error opening %s: %s\n", dev, strerror(errno)); return -1; } - if (md_get_version(mdfd) <= 0) { + + if (md_array_valid(mdfd) == 0) { close(mdfd); if (report_errors) - fprintf(stderr, Name ": %s does not appear to be " - "an md device\n", dev); + pr_err("%s does not appear to be an md device\n", dev); return -2; } + return mdfd; } + +char *find_free_devnm(int use_partitions) +{ + static char devnm[32]; + int devnum; + for (devnum = 127; devnum != 128; + devnum = devnum ? devnum-1 : (1<<9)-1) { + + if (use_partitions) + sprintf(devnm, "md_d%d", devnum); + else + sprintf(devnm, "md%d", devnum); + if (mddev_busy(devnm)) + continue; + if (!conf_name_is_free(devnm)) + continue; + if (!use_udev()) { + /* make sure it is new to /dev too, at least as a + * non-standard */ + dev_t devid = devnm2devid(devnm); + if (devid) { + char *dn = map_dev(major(devid), + minor(devid), 0); + if (dn && ! is_standard(dn, NULL)) + continue; + } + } + break; + } + if (devnum == 128) + return NULL; + return devnm; +}