X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=mdopen.c;h=ebaba950705bb2dc2ca7dab30b582b2dc453b0e5;hb=25037aa872006fd4613d97e3d3da3c26c61e708f;hp=721d74ae042053f905ebcc2a9443dfea4449b34f;hpb=0bbc98b563b39b5159d247a0a4d6e8e1e6e38c30;p=thirdparty%2Fmdadm.git diff --git a/mdopen.c b/mdopen.c index 721d74ae..ebaba950 100644 --- a/mdopen.c +++ b/mdopen.c @@ -1,7 +1,7 @@ /* * mdadm - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001-2002 Neil Brown + * Copyright (C) 2001-2006 Neil Brown * * * This program is free software; you can redistribute it and/or modify @@ -31,7 +31,25 @@ #include "md_p.h" #include -void make_parts(char *dev, int cnt) + +void make_dev_symlink(char *dev) +{ + char *new = strdup(dev); + + if (!new) return; + /* /dev/md/0 -> /dev/md0 + * /dev/md/d0 -> /dev/md_d0 + */ + if (isdigit(new[8])) + strcpy(new+7, new+8); + else + new[7] = '_'; + if (symlink(dev+5, new)) + perror(new); +} + + +void make_parts(char *dev, int cnt, int symlinks) { /* make 'cnt' partition devices for 'dev' * We use the major/minor from dev and add 1..cnt @@ -40,35 +58,45 @@ void make_parts(char *dev, int cnt) * else that of dev */ struct stat stb; - int major, minor; + int major_num, minor_num; int i; - char *name = malloc(strlen(dev) + 20); + int nlen = strlen(dev) + 20; + char *name = malloc(nlen); int dig = isdigit(dev[strlen(dev)-1]); + if (cnt==0) cnt=4; if (stat(dev, &stb)!= 0) return; if (!S_ISBLK(stb.st_mode)) return; - major = major(stb.st_rdev); - minor = minor(stb.st_rdev); + major_num = major(stb.st_rdev); + minor_num = minor(stb.st_rdev); for (i=1; i <= cnt ; i++) { struct stat stb2; - sprintf(name, "%s%s%d", dev, dig?"p":"", i); + snprintf(name, nlen, "%s%s%d", dev, dig?"p":"", i); if (stat(name, &stb2)==0) { if (!S_ISBLK(stb2.st_mode)) continue; - if (stb2.st_rdev == makedev(major, minor+i)) + if (stb2.st_rdev == makedev(major_num, minor_num+i)) continue; unlink(name); } else { stb2 = stb; } - mknod(name, S_IFBLK | 0600, makedev(major, minor+i)); - chown(name, stb2.st_uid, stb2.st_gid); - chmod(name, stb2.st_mode & 07777); + if (mknod(name, S_IFBLK | 0600, makedev(major_num, minor_num+i))) + perror("mknod"); + if (chown(name, stb2.st_uid, stb2.st_gid)) + perror("chown"); + if (chmod(name, stb2.st_mode & 07777)) + perror("chmod"); + if (symlinks && strncmp(name, "/dev/md/", 8) == 0) + make_dev_symlink(name); + stat(name, &stb2); + add_dev(name, &stb2, 0, NULL); } } + /* * 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. @@ -76,26 +104,31 @@ void make_parts(char *dev, int cnt) * 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 is user=0, group=0 perm=0600 + * Default ownership/mode comes from config file. */ int open_mddev(char *dev, int autof) { int mdfd; struct stat stb; - int major = MD_MAJOR; - int minor = 0; + int major_num = MD_MAJOR; + int minor_num = 0; int must_remove = 0; struct mdstat_ent *mdlist; int num; + struct createinfo *ci = conf_get_create_info(); + int parts; + + if (autof == 0) + autof = ci->autof; + + parts = autof >> 3; + autof &= 7; - if (autof) { + if (autof && autof != 1) { /* autof is set, so we need to check that the name is ok, * and possibly create one if not */ - if (autof == -2 && !is_standard(dev, NULL)) { - fprintf(stderr, Name ": --auto=yes requires a 'standard' md device name, not %s\n", dev); - return -1; - } + 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", @@ -103,12 +136,43 @@ int open_mddev(char *dev, int autof) return -1; } /* check major number is correct */ - if (autof>0) - major = get_mdp_major(); - if (stb.st_mode && major(stb.st_rdev) != major) + 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); + 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; + } + /* 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) { - mdu_array_info_t array; /* looks ok, see if it is available */ mdfd = open(dev, O_RDWR, 0); if (mdfd < 0) { @@ -121,15 +185,9 @@ int open_mddev(char *dev, int autof) close(mdfd); return -1; } - if (ioctl(mdfd, GET_ARRAY_INFO, &array)==0) { - /* already active */ - must_remove = 1; - close(mdfd); - } else { - if (autof > 0) - make_parts(dev, autof); - return mdfd; - } + if (major_num != MD_MAJOR && parts > 0) + make_parts(dev, parts, ci->symlinks); + return mdfd; } /* Ok, need to find a minor that is not in use. * If the device name is in a 'standard' format, @@ -137,40 +195,31 @@ int open_mddev(char *dev, int autof) * easiest to read /proc/mdstat, and hunt through for * an unused number */ - switch(is_standard(dev, &num)) { - case -1: /* non partitioned */ - if (autof > 0) { - fprintf(stderr, Name ": that --auto option not compatable with device named %s\n", dev); - return -1; - } - minor = num; - num = -1-num; - break; - case 1: /* partitioned */ - if (autof == -1) { - fprintf(stderr, Name ": that --auto option not compatable with device named %s\n", dev); - return -1; - } - minor = num << MdpMinorShift; - major = get_mdp_major(); - break; - case 0: /* not standard, pick an unused number */ - mdlist = mdstat_read(0); - for (num= (autof>0)?-1:0 ; ; num+= (autof>2)?-1:1) { + if (num < 0) { + /* need to pick an unused number */ + mdlist = mdstat_read(0, 0); + /* Choose a large number. Start from 127 and search down, + * but if nothing is found, start really big + */ + for (num = 127 ; num != 128 ; num = num ? num-1 : (1<<22)-1) { struct mdstat_ent *me; + int devnum = num; + if (major_num != MD_MAJOR) + devnum = -1-num; + for (me=mdlist; me; me=me->next) - if (me->devnum == num) + if (me->devnum == devnum) break; if (!me) { - /* doesn't exist if mdstat. + /* doesn't exist in mdstat. * make sure it is new to /dev too */ char *dn; - if (autof > 0) - minor = (-1-num) << MdpMinorShift; + if (major_num != MD_MAJOR) + minor_num = num << MdpMinorShift; else - minor = num; - dn = map_dev(major,minor); + minor_num = num; + dn = map_dev(major_num,minor_num, 0); if (dn==NULL || is_standard(dn, NULL)) { /* this number only used by a 'standard' name, * so it is safe to use @@ -179,33 +228,56 @@ int open_mddev(char *dev, int autof) } } } - } + } else if (major_num == MD_MAJOR) + minor_num = num; + else + minor_num = num << MdpMinorShift; /* major and minor have been chosen */ - + /* If it was a 'standard' name and it is in-use, then * the device could already be correct */ - if (stb.st_mode && major(stb.st_rdev) == major && - minor(stb.st_rdev) == minor) + if (stb.st_mode && major(stb.st_rdev) == major_num && + minor(stb.st_rdev) == minor_num) ; else { - if (major(makedev(major,minor)) != major || - minor(makedev(major,minor)) != minor) { + 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"); return -1; } if (must_remove) unlink(dev); - if (mknod(dev, S_IFBLK|0600, makedev(major, minor))!= 0) { + 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); return -1; } if (must_remove) { - chown(dev, stb.st_uid, stb.st_gid); - chmod(dev, stb.st_mode & 07777); + 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"); } - make_parts(dev,autof); + 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); } } mdfd = open(dev, O_RDWR, 0); @@ -221,3 +293,51 @@ int open_mddev(char *dev, int autof) return mdfd; } + +int open_mddev_devnum(char *devname, int devnum, char *name, char *chosen_name) +{ + /* Open the md device with number 'devnum', possibly using 'devname', + * possibly constructing a name with 'name', but in any case, copying + * the name into 'chosen_name' + */ + int major_num, minor_num; + struct stat stb; + + if (devname) + strcpy(chosen_name, devname); + else if (name && strchr(name,'/') == NULL) { + char *n = strchr(name, ':'); + if (n) n++; else n = name; + if (isdigit(*n) && devnum < 0) + sprintf(chosen_name, "/dev/md/d%s", n); + else + sprintf(chosen_name, "/dev/md/%s", n); + } else { + if (devnum >= 0) + sprintf(chosen_name, "/dev/md%d", devnum); + else + sprintf(chosen_name, "/dev/md/d%d", -1-devnum); + } + if (devnum >= 0) { + major_num = MD_MAJOR; + minor_num = devnum; + } else { + major_num = get_mdp_major(); + minor_num = (-1-devnum) << 6; + } + if (stat(chosen_name, &stb) == 0) { + /* It already exists. Check it is right. */ + if ( ! S_ISBLK(stb.st_mode) || + stb.st_rdev != makedev(major_num, minor_num)) { + errno = EEXIST; + return -1; + } + } else { + if (mknod(chosen_name, S_IFBLK | 0600, + makedev(major_num, minor_num)) != 0) { + return -1; + } + /* FIXME chown/chmod ?? */ + } + return open(chosen_name, O_RDWR); +}