From: Neil Brown Date: Fri, 4 Jun 2004 12:03:19 +0000 (+0000) Subject: mdadm-1.6.0 X-Git-Tag: mdadm-1.6.0 X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=commitdiff_plain;h=dd0781e50555c32ff2f808ec46f4b03a5693ea47;hp=98c6faba80e6db0693f99faf5c6525ef4f1fb680 mdadm-1.6.0 --- diff --git a/ANNOUNCE-1.6.0 b/ANNOUNCE-1.6.0 new file mode 100644 index 00000000..4461fe31 --- /dev/null +++ b/ANNOUNCE-1.6.0 @@ -0,0 +1,36 @@ +Subject: ANNOUNCE: mdadm 1.6.0 - A tool for managing Soft RAID under Linux + + +I am pleased to announce the availability of + mdadm version 1.6.0 +It is available at + http://www.cse.unsw.edu.au/~neilb/source/mdadm/ +and + http://www.{countrycode}.kernel.org/pub/linux/utils/raid/mdadm/ + +as a source tar-ball and (at the first site) as an SRPM, and as an RPM for i386. + +mdadm is a tool for creating, managing and monitoring +device arrays using the "md" driver in Linux, also +known as Software RAID arrays. + +Release 1.6.0 adds: + - --grow which (in 2.6.7-rc1-mm1 and hopefully 2.6.8) allows raid1/4/5/6 + arrays to change the active size of the underlying devices, and allows + raid1 arrays to change the number of active drives. + - Allows --build to buld raid1 and multipath arrays. + - adds "degraded" and "recovering" as possibilities for the status line + in --detail + - fixes a bug in 1.5.0 which stopped resync status messages from being + generated in --monitor mode + - Further support for partitionable arrays included "--auto=" option + and "auto=" config file entry which instructs mdadm to create the necessary + device files after allocating an unused array number. + - assorted minor fixes and improvements. + +Development of mdadm is sponsored by CSE@UNSW: + The School of Computer Science and Engineering +at + The University of New South Wales + +NeilBrown 4 Jun 2004 diff --git a/Build.c b/Build.c index 0179807f..3e182f8c 100644 --- a/Build.c +++ b/Build.c @@ -35,7 +35,7 @@ int Build(char *mddev, int mdfd, int chunk, int level, int raiddisks, - mddev_dev_t devlist) + mddev_dev_t devlist, int assume_clean) { /* Build a linear or raid0 arrays without superblocks * We cannot really do any checks, we just do it. @@ -91,6 +91,8 @@ int Build(char *mddev, int mdfd, int chunk, int level, array.md_minor = MINOR(stb.st_rdev); array.not_persistent = 1; array.state = 0; /* not clean, but no errors */ + if (assume_clean) + array.state |= 1; array.active_disks = raiddisks; array.working_disks = raiddisks; array.spare_disks = 0; diff --git a/ChangeLog b/ChangeLog index 7c8fe054..a505a74f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,28 @@ -Changes Prior to this release +Changes Prior to 1.6.0 release + - Device name given in -Eb is determined by examining /dev rather + than assuming /dev/md%d + - Fix bug in --monitor where an array could be held open an so + could not be stopped without killing mdadm. + - Add --grow mode. Currently only --size and --raid-disks can be + changed. Both require kernel support which, at the time of + writing, is not in a release kernel yet. + - Don't print out "errors" or "no-errors" in -D and -E, as the bit + is never set or used. + - Use md event notification in 2.6.??? to make --monitor mode + respond instantly to events. + - Add --auto= option and auto= configfile entry to tell mdadm to + create device files as needed. This is particularly useful + with partitioned arrays where the major device number can change. + - When generating --brief listing, if the standard name doesn't + exist, search /dev for one rather than using a temp name. + - Allow --build to build raid1 and multipath arrays. + - Add "--assume-clean" for Create and Build, particularly for raid1 + Note: this is dangerous. Only use it if you are certain. + - Fix bug so that Rebuild status monitoring works again. + - Add "degraded" and "recovering" options to the "Status:" + entry for --detail + +Changes Prior to 1.5.0 release - new commands "mdassemble" which is a stripped-down equivalent of "mdadm -As", that can be compiled with dietlibc. Thanks to Luca Berra . @@ -323,4 +347,3 @@ Changes Prior to 0.5 release the --help output, is not wholy correct. After I get --follow working properly, I plan to revise the various documentation and/or the code to make sure the two match. - diff --git a/Create.c b/Create.c index a536a75b..fb9857b5 100644 --- a/Create.c +++ b/Create.c @@ -68,7 +68,7 @@ int Create(char *mddev, int mdfd, if (md_get_version(mdfd) < 9000) { - fprintf(stderr, Name ": Create requires md driver verison 0.90.0 or later\n"); + fprintf(stderr, Name ": Create requires md driver version 0.90.0 or later\n"); return 1; } if (level == UnSet) { @@ -351,6 +351,7 @@ int Create(char *mddev, int mdfd, if (ioctl(mdfd, RUN_ARRAY, ¶m)) { fprintf(stderr, Name ": RUN_ARRAY failed: %s\n", strerror(errno)); + Manage_runstop(mddev, mdfd, -1); return 1; } fprintf(stderr, Name ": array %s started.\n", mddev); diff --git a/Detail.c b/Detail.c index 419c45cb..5028ae29 100644 --- a/Detail.c +++ b/Detail.c @@ -46,6 +46,7 @@ int Detail(char *dev, int brief, int test) char *c; char *devices = NULL; int spares = 0; + struct stat stb; mdp_super_t super; int have_super = 0; @@ -79,6 +80,8 @@ int Detail(char *dev, int brief, int test) close(fd); return rv; } + if (fstat(fd, &stb) != 0 && !S_ISBLK(stb.st_mode)) + stb.st_rdev = 0; rv = 0; /* Ok, we have some info to print... */ c = map_num(pers, array.level); @@ -87,6 +90,15 @@ int Detail(char *dev, int brief, int test) else { unsigned long array_size; unsigned long long larray_size; + struct mdstat_ent *ms = mdstat_read(0); + struct mdstat_ent *e; + int devnum = array.md_minor; + if (MAJOR(stb.st_rdev) != MD_MAJOR) + devnum = -1 - devnum; + + for (e=ms; e; e=e->next) + if (e->devnum == devnum) + break; #ifdef BLKGETSIZE64 if (ioctl(fd, BLKGETSIZE64, &larray_size)==0) ; @@ -106,7 +118,7 @@ int Detail(char *dev, int brief, int test) printf(" Creation Time : %.24s\n", ctime(&atime)); printf(" Raid Level : %s\n", c?c:"-unknown-"); if (larray_size) - printf(" Array Size : %llu%s\n", (larray_size>>10), human_size(larray_size)); + printf(" Array Size : %llu%s\n", (larray_size>>10), human_size(larray_size)); if (array.level >= 1) printf(" Device Size : %d%s\n", array.size, human_size((long long)array.size<<10)); printf(" Raid Devices : %d\n", array.raid_disks); @@ -117,9 +129,10 @@ int Detail(char *dev, int brief, int test) printf("\n"); atime = array.utime; printf(" Update Time : %.24s\n", ctime(&atime)); - printf(" State : %s, %serrors\n", + printf(" State : %s%s%s\n", (array.state&(1<percent >= 0) ? ", recovering": ""); printf(" Active Devices : %d\n", array.active_disks); printf("Working Devices : %d\n", array.working_disks); printf(" Failed Devices : %d\n", array.failed_disks); @@ -142,17 +155,11 @@ int Detail(char *dev, int brief, int test) } printf("\n"); - { - struct mdstat_ent *ms = mdstat_read(); - struct mdstat_ent *e; - for (e=ms; e; e=e->next) - if (e->devnum == array.md_minor) { - if (e->percent >= 0) - printf(" Rebuild Status : %d%% complete\n\n", e->percent); - break; - } - free_mdstat(ms); - } + + if (e && e->percent >= 0) + printf(" Rebuild Status : %d%% complete\n\n", e->percent); + free_mdstat(ms); + printf(" Number Major Minor RaidDevice State\n"); } for (d= 0; dsuper.level); char *d; - printf("ARRAY /dev/md%d level=%s num-devices=%d UUID=", - ap->super.md_minor, c?c:"-unknown-", ap->super.raid_disks); + printf("ARRAY %s level=%s num-devices=%d UUID=", + get_md_name(ap->super.md_minor), + c?c:"-unknown-", ap->super.raid_disks); if (spares) printf(" spares=%d", spares); if (ap->super.minor_version >= 90) printf("%08x:%08x:%08x:%08x", ap->super.set_uuid0, ap->super.set_uuid1, diff --git a/Manage.c b/Manage.c index 0d8ad8d2..624c775d 100644 --- a/Manage.c +++ b/Manage.c @@ -114,6 +114,27 @@ int Manage_runstop(char *devname, int fd, int runstop) return 0; } +int Manage_resize(char *devname, int fd, long long size, int raid_disks) +{ + mdu_array_info_t info; + if (ioctl(fd, GET_ARRAY_INFO, &info) != 0) { + fprintf(stderr, Name ": Cannot get array information for %s: %s\n", + devname, strerror(errno)); + return 1; + } + if (size >= 0) + info.size = size; + if (raid_disks > 0) + info.raid_disks = raid_disks; + if (ioctl(fd, SET_ARRAY_INFO, &info) != 0) { + fprintf(stderr, Name ": Cannot set device size/shape for %s: %s\n", + devname, strerror(errno)); + return 1; + } + return 0; +} + + int Manage_subdevs(char *devname, int fd, mddev_dev_t devlist) { diff --git a/Monitor.c b/Monitor.c index 2d3693bd..021a9677 100644 --- a/Monitor.c +++ b/Monitor.c @@ -185,11 +185,11 @@ int Monitor(mddev_dev_t devlist, if (mdstat) free_mdstat(mdstat); - mdstat = mdstat_read(); + mdstat = mdstat_read(oneshot?0:1); for (st=statelist; st; st=st->next) { mdu_array_info_t array; - struct mdstat_ent *mse; + struct mdstat_ent *mse = NULL, *mse2; char *dev = st->devname; int fd; unsigned int i; @@ -228,16 +228,18 @@ int Monitor(mddev_dev_t devlist, struct stat stb; if (fstat(fd, &stb) == 0 && (S_IFMT&stb.st_mode)==S_IFBLK) { - if (MINOR(stb.st_rdev) == 9) + if (MAJOR(stb.st_rdev) == MD_MAJOR) st->devnum = MINOR(stb.st_rdev); else st->devnum = -1- (MINOR(stb.st_rdev)>>6); } } - for (mse = mdstat ; mse ; mse=mse->next) - if (mse->devnum == st->devnum) - mse->devnum = MAXINT; /* flag it as "used" */ + for (mse2 = mdstat ; mse2 ; mse2=mse2->next) + if (mse2->devnum == st->devnum) { + mse2->devnum = MAXINT; /* flag it as "used" */ + mse = mse2; + } if (st->utime == array.utime && st->failed == array.failed_disks && @@ -349,6 +351,7 @@ int Monitor(mddev_dev_t devlist, free(st); continue; } + close(fd); st->utime = 0; st->next = statelist; st->err = 1; @@ -414,7 +417,7 @@ int Monitor(mddev_dev_t devlist, if (oneshot) break; else - sleep(period); + mdstat_wait(period); } test = 0; } diff --git a/ReadMe.c b/ReadMe.c index 7cd82400..6ba33ba1 100644 --- a/ReadMe.c +++ b/ReadMe.c @@ -29,7 +29,7 @@ #include "mdadm.h" -char Version[] = Name " - v1.5.0 - 22 Jan 2004\n"; +char Version[] = Name " - v1.6.0 - 4 June 2004\n"; /* * File: ReadMe.c * @@ -58,7 +58,7 @@ char Version[] = Name " - v1.5.0 - 22 Jan 2004\n"; */ /* - * mdadm has 6 major modes of operation: + * mdadm has 7 major modes of operation: * 1/ Create * This mode is used to create a new array with a superblock * It can progress in several step create-add-add-run @@ -84,9 +84,13 @@ char Version[] = Name " - v1.5.0 - 22 Jan 2004\n"; * Also query will treat it as either * 6/ Monitor * This mode never exits but just monitors arrays and reports changes. + * 7/ Grow + * This mode allows for changing of key attributes of a raid array, such + * as size, number of devices, and possibly even layout. + * At the time if writing, there is only minimal support. */ -char short_options[]="-ABCDEFGQhVvbc:l:p:m:n:x:u:c:d:z:U:sarfRSow1t"; +char short_options[]="-ABCDEFGQhVvbc:l:p:m:n:x:u:c:d:z:U:sa::rfRSow1t"; struct option long_options[] = { {"manage", 0, 0, '@'}, {"misc", 0, 0, '#'}, @@ -96,7 +100,7 @@ struct option long_options[] = { {"detail", 0, 0, 'D'}, {"examine", 0, 0, 'E'}, {"follow", 0, 0, 'F'}, - {"grow", 0, 0, 'G'}, /* not yet implemented */ + {"grow", 0, 0, 'G'}, {"zero-superblock", 0, 0, 'K'}, /* deliberately no a short_option */ {"query", 0, 0, 'Q'}, @@ -119,7 +123,9 @@ struct option long_options[] = { {"raid-devices",1, 0, 'n'}, {"spare-disks",1,0, 'x'}, {"spare-devices",1,0, 'x'}, - {"size" ,1, 0, 'z'}, + {"size", 1, 0, 'z'}, + {"auto", 2, 0, 'a'}, /* also for --assemble */ + {"assume-clean",0,0, 3 }, /* For assemble */ {"uuid", 1, 0, 'u'}, @@ -213,6 +219,8 @@ char OptionHelp[] = " --size= -z : Size (in K) of each drive in RAID1/4/5/6 - optional\n" " --force -f : Honour devices as listed on command line. Don't\n" " : insert a missing drive for RAID5.\n" +" --auto(=p) -a : Automatically allocate new (partitioned) md array if needed.\n" +" --assume-clean : Assume the array is already in-sync. This is dangerous.\n" "\n" " For assemble:\n" " --uuid= -u : uuid of array to assemble. Devices which don't\n" @@ -223,6 +231,7 @@ char OptionHelp[] = " --scan -s : scan config file for missing information\n" " --force -f : Assemble the array even if some superblocks appear out-of-date\n" " --update= -U : Update superblock: one of sparc2.2, super-minor or summaries\n" +" --auto(=p) -a : Automatically allocate new (partitioned) md array if needed.\n" "\n" " For detail or examine:\n" " --brief -b : Just print device name and UUID\n" @@ -401,7 +410,7 @@ char Help_monitor[] = "If no mail address or program are specified, then mdadm reports all\n" "state changes to stdout.\n" "\n" -"Options that are valid with the monitor (--F --follow) mode are:\n" +"Options that are valid with the monitor (-F --follow) mode are:\n" " --mail= -m : Address to mail alerts of failure to\n" " --program= -p : Program to run when an event is detected\n" " --alert= : same as --program\n" @@ -413,6 +422,22 @@ char Help_monitor[] = " --test -t : Generate a TestMessage event against each array at startup\n" ; +char Help_grow[] = +"Usage: mdadm --grow device options\n" +"\n" +"This usage causes mdadm to attempt to reconfigure a running array.\n" +"This is only possibly if the kernel being used supports a particular\n" +"reconfiguration. This version only supports changing the number of\n" +"devices in a RAID1, and changing the active size of all devices in\n" +"a RAID1/4/5/6.\n" +"\n" +"Options that are valid with the grow (-F --grow) mode are:\n" +" --size= -z : Change the active size of devices in an array.\n" +" : This is useful if all devices have been replaced\n" +" : with larger devices.\n" +" --raid-disks= -n : Change the number of active devices in a RAID1\n" +" : array.\n" +; @@ -494,4 +519,5 @@ mapping_t modes[] = { { "manage", MANAGE}, { "misc", MISC}, { "monitor", MONITOR}, + { "grow", GROW}, }; diff --git a/TODO b/TODO index 4cfa1ebe..a282744a 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,15 @@ +2004-june-02 + * Don't print 'errors' flag, it is meaningless. DONE + * Handle new superblock format + * create device file on demand, particularly partitionable devices. DONE + BUT figure a way to create the partition devices. + auto=partN + * Use Event: interface to listen for events. DONE, untested + * Make sure mdadm -As can assemble multi-level RAIDs ok. + * --build to build raid1 or multipath arrays + clean or not ??? + +---------------------------------------------------------------------------- * mdadm --monitor to monitor failed multipath paths and re-instate them. * Maybe make "--help" fit in 80x24 and have a --long-help with more info. DONE diff --git a/config.c b/config.c index 067beb6c..1671d266 100644 --- a/config.c +++ b/config.c @@ -32,6 +32,7 @@ #include #include #include +#include /* * Read the config file @@ -230,6 +231,7 @@ void load_partitions(void) cdevlist = cd; } } + fclose(f); } @@ -272,6 +274,7 @@ void arrayline(char *line) mis.devices = NULL; mis.devname = NULL; mis.spare_group = NULL; + mis.autof = 0; for (w=dl_next(line); w!=line; w=dl_next(w)) { if (w[0] == '/') { @@ -326,13 +329,41 @@ void arrayline(char *line) } else if (strncasecmp(w, "spares=", 7) == 0 ) { /* for warning if not all spares present */ mis.spare_disks = atoi(w+7); + } else if (strncasecmp(w, "auto=", 5) == 0 ) { + /* whether to create device special files as needed */ + if (strcasecmp(w+5, "no")==0) + mis.autof = 0; + else if (strcasecmp(w+5,"yes")==0 || strcasecmp(w+5,"md")==0) + mis.autof = -1; + else { + /* There might be digits, and maybe a hypen, at the end */ + char *e = w+5 + strlen(w+5); + int num = 4; + int len; + while (e > w+5 && isdigit(e[-1])) + e--; + if (*e) { + num = atoi(e); + if (num <= 0) num = 1; + } + if (e > w+5 && e[-1] == '-') + e--; + len = e - (w+5); + if ((len == 3 && strncasecmp(w+5,"mdp",3)==0) || + (len == 1 && strncasecmp(w+5,"p",1)==0) || + (len >= 4 && strncasecmp(w+5,"part",4)==0)) + mis.autof = num; + else + fprintf(stderr, Name ": auto type of \"%s\" ignored for %s\n", + w+5, mis.devname?mis.devname:"unlabeled-array"); + } } else { fprintf(stderr, Name ": unrecognised word on ARRAY line: %s\n", w); } } if (mis.devname == NULL) - fprintf(stderr, Name ": ARRAY line with a device\n"); + fprintf(stderr, Name ": ARRAY line with no device\n"); else if (mis.uuid_set == 0 && mis.devices == NULL && mis.super_minor < 0) fprintf(stderr, Name ": ARRAY line %s has no identity information.\n", mis.devname); else { @@ -420,6 +451,7 @@ void load_conffile(char *conffile) free_line(line); } + fclose(f); /* printf("got file\n"); */ } diff --git a/mdadm.8 b/mdadm.8 index 38bcb737..6e20b7ea 100644 --- a/mdadm.8 +++ b/mdadm.8 @@ -1,5 +1,5 @@ .\" -*- nroff -*- -.TH MDADM 8 "" v1.5.0 +.TH MDADM 8 "" v1.6.0 .SH NAME mdadm \- manage MD devices .I aka @@ -76,7 +76,7 @@ configuration file, at all. It has a different configuration file with a different format and an different purpose. .SH MODES -mdadm has 6 major modes of operation: +mdadm has 7 major modes of operation: .TP .B Assemble Assemble the parts of a previously created @@ -114,6 +114,12 @@ only meaningful for raid1, 4, 5, 6 or multipath arrays as only these have interesting state. raid0 or linear never have missing, spare, or failed drives, so there is nothing to monitor. +.TP +.B "Grow" +Grow (or shrink) an array, or otherwise reshape it in some way. +Currently supported growth options including changing the active size +of componenet devices in RAID level 1/4/5/6 and changing the number of +active devices in RAID1. .SH OPTIONS @@ -152,6 +158,10 @@ Select .B Monitor mode. +.TP +.BR -G ", " --grow +Change the size or shape of an active array. + .TP .BR -h ", " --help Display help message or, after above option, mode specific help @@ -261,13 +271,17 @@ Specify the number of active devices in the array. This, plus the number of spare devices (see below) must equal the number of .I component-devices (including "\fBmissing\fP" devices) -that are listed on the command line. Setting a value of 1 is probably +that are listed on the command line for +.BR --create . +Setting a value of 1 is probably a mistake and so requires that .B --force be specified first. A value of 1 will then be allowed for linear, multipath, raid0 and raid1. It is never allowed for raid4 or raid5. .br -Note that this number cannot be changed once the array has been created. +This number can only be changed using +.B --grow +for RAID1 arrays, and only on kernels which provide necessary support. .TP .BR -x ", " --spare-devices= @@ -288,6 +302,63 @@ If this is not specified size, though if there is a variance among the drives of greater than 1%, a warning is issued. +This value can be set with +.B --grow +for RAID level 1/4/5/6. If the array was created with a size smaller +than the currently active drives, the extra space can be accessed +using +.BR --grow . + +.TP +.BR --assume-clean +Tell +.I mdadm +that the array pre-existed and is known to be clean. This is only +really useful for Building RAID1 array. Only use this if you really +know what you are doing. This is currently only supported for --build. + +.TP +.BR -R ", " --run +Insist that +.I mdadm +run the array, even if some of the components +appear to be active in another array or filesystem. Normally +.I mdadm +will ask for confirmation before including such components in an +array. This option causes that question to be suppressed. + +.TP +.BR -f ", " --force +Insist that +.I mdadm +accept the geometry and layout specified without question. Normally +.I mdadm +will not allow creation of an array with only one device, and will try +to create a raid5 array with one missing drive (as this makes the +initial resync work faster). With +.BR --force , +.I mdadm +will not try to be so clever. + +.TP +.BR -a ", " "--auto{=no,yes,md,mdp,part,p}{NN}" +Instruct mdadm to create the device file if needed, and to allocate +an unused minor number. "yes" or "md" causes a non-partitionable array +to be used. "mdp", "part" or "p" causes a partitionable array (2.6 and +later) to be used. The argumentment can also come immediately after +"-a". e.g. "-ap". + +For partitionable arrays, +.I mdadm +will create the device file for the whole array and for the first 4 +partitions. A different number of partitions can be specified at the +end of this option (e.g. +.BR --auto=p7 ). +If the device name ends with a digit, the partition names add an +underscore, a 'p', and a number, e.g. "/dev/home1_p3". If there is no +trailing digit, then the partition names just have a number added, +e.g. "/dev/scratch3". + .SH For assemble: .TP @@ -326,6 +397,10 @@ With .B --run an attempt will be made to start it anyway. +.TP +.BR -a ", " "--auto{=no,yes,md,mdp,part}" +See this option under Create and Build options. + .TP .BR -U ", " --update= Update the superblock on each device while assembling the array. The @@ -504,7 +579,7 @@ listed in the configuration file are assembled. If precisely one device is listed, but .B --scan -is not given, that +is not given, then .I mdadm acts as though .B --scan @@ -545,6 +620,46 @@ may work for RAID1, 4, 5 or 6), give the .B --run flag. +If an +.B auto +option is given, either on the command line (--auto) or in the +configuration file (e.g. auto=part), then +.I mdadm +will create the md device if necessary or will re-create it if it +doesn't look usable as it is. + +This can be useful for handling partitioned devices (which don't have +a stable device number - it can change after a reboot) and when using +"udev" to manage your +.B /dev +tree (udev cannot handle md devices because of the unusual device +initialisation conventions). + +If the option to "auto" is "mdp" or "part" or (on the command line +only) "p", then mdadm will create a partitionable array, using the +first free one that is not inuse, and does not already have an entry +in /dev (apart from numeric /dev/md* entries). + +If the option to "auto" is "yes" or "md" or (on the command line) +nothing, then mdadm will create a traditional, non-partitionable md +array. + +It is expected that the "auto" functionality will be used to create +device entries with meaningful names such as "/dev/md/home" or +"/dev/md/root", rather than names based on the numerical array number. + +When using this option to create a partitionable array, the device +files for the first 4 partitions are also created. If a different +number is required it can be simply appended to the auto option. +e.g. "auto=part8". Partition names are created by appending a digit +string to the device name, with an intervening "_p" if the device name +ends with a digit. + +The +.B --auto +option is also available in Build and Create modes. As those modes do +not use a config file, the "auto=" config option does not apply to +these modes. .SH BUILD MODE @@ -584,6 +699,12 @@ Usage: This usage will initialise a new md array, associate some devices with it, and activate the array. +This the +.B --auto +option is given (as described in more detail in the section on +Assemble mode), then the md device will be created with a suitable +device number if necessary. + As devices are added, they are checked to see if they contain raid superblocks or filesystems. They are also checked to see if the variance in device size exceeds 1%. @@ -625,7 +746,7 @@ option. The General Management options that are valid with --create are: .TP .B --run -insist of running the array even if some devices look like they might +insist on running the array even if some devices look like they might be in use. .TP @@ -921,6 +1042,43 @@ first. If the removal succeeds but the adding fails, then it is added back to the original array. +.SH GROW MODE +The GROW mode is used for changing the size or shape of an active +array. +For this to work, the kernel must support the necessary change. +Various types of growth may be added during 2.6 development, possibly +including restructuring a raid5 array to have more active devices. + +Currently the only support available is to change the "size" attribute +for arrays with redundancy, and the raid-disks attribute of RAID1 +arrays. + +Normally when an array is build the "size" it taken from the smallest +of the drives. If all the small drives in an arrays are, one at a +time, removed and replaced with larger drives, then you could have an +array of large drives with only a small amount used. In this +situation, changing the "size" with "GROW" mode will allow the extra +space to start being used. If the size is increased in this way, a +"resync" process will start to make sure the new parts of the array +are synchronised. + +Note that when an array changes size, any filesystem that may be +stored in the array will not automatically grow to use the space. The +filesystem will need to be explicitly told to use the extra space. + +A RAID1 array can work with any number of devices from 1 upwards +(though 1 is not very useful). There may be times which you want to +increase or decrease the number of active devices. Note that this is +different to hot-add or hot-remove which changes the number of +inactive devices. + +When reducing the number of devices in a RAID1 array, the slots which +are to be removed from the array must already be vacant. That is, the +devices that which were in those slots must be failed and removed. + +When the number of devices is increased, any hot spares that are +present may be activated immediately. + .SH EXAMPLES .B " mdadm --query /dev/name-of-device" diff --git a/mdadm.c b/mdadm.c index d17e0b17..827f334d 100644 --- a/mdadm.c +++ b/mdadm.c @@ -29,10 +29,157 @@ #include "mdadm.h" #include "md_p.h" +#include -int open_mddev(char *dev) + +void make_parts(char *dev, int cnt) { - int mdfd = open(dev, O_RDWR, 0); + /* make 'cnt' partition devices for 'dev' + * We use the major/minor from dev and add 1..cnt + * If dev ends with a digit, we add "_p%d" else "%d" + * If the name exists, we use it's owner/mode, + * else that of dev + */ + struct stat stb; + int major, minor; + int i; + char *name = malloc(strlen(dev) + 20); + int dig = isdigit(dev[strlen(dev)-1]); + + if (stat(dev, &stb)!= 0) + return; + if (!S_ISBLK(stb.st_mode)) + return; + major = MAJOR(stb.st_rdev); + minor = MINOR(stb.st_rdev); + for (i=1; i <= cnt ; i++) { + struct stat stb2; + sprintf(name, "%s%s%d", dev, dig?"_p":"", i); + if (stat(name, &stb2)==0) { + if (!S_ISBLK(stb2.st_mode)) + continue; + if (stb2.st_rdev == MKDEV(major, minor+i)) + continue; + unlink(name); + } else { + stb2 = stb; + } + mknod(name, S_IFBLK | 0600, MKDEV(major, minor+i)); + chown(name, stb2.st_uid, stb2.st_gid); + chmod(name, stb2.st_mode & 07777); + } +} + +/* + * 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 few md array and create the device. + * Default ownership is user=0, group=0 perm=0600 + */ +int open_mddev(char *dev, int autof) +{ + int mdfd; + struct stat stb; + int major = MD_MAJOR; + int minor; + int must_remove = 0; + struct mdstat_ent *mdlist; + int num; + + if (autof) { + /* autof is set, so we need to check that the name is ok, + * and possibly create one if not + */ + stb.st_mode = 0; + if (lstat(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 */ + if (autof>0) + major = get_mdp_major(); + if (stb.st_mode && MAJOR(stb.st_rdev) != major) + 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) { + 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 (ioctl(mdfd, GET_ARRAY_INFO, &array)==0) { + /* already active */ + must_remove = 1; + close(mdfd); + } else { + if (autof > 0) + make_parts(dev, autof); + return mdfd; + } + } + /* Ok, need to find a minor that is not in use. + * Easiest to read /proc/mdstat, and hunt through for + * an unused number + */ + mdlist = mdstat_read(0); + for (num= (autof>0)?-1:0 ; ; num+= (autof>2)?-1:1) { + struct mdstat_ent *me; + for (me=mdlist; me; me=me->next) + if (me->devnum == num) + break; + if (!me) { + /* doesn't exist if mdstat. + * make sure it is new to /dev too + */ + char *dn; + if (autof > 0) + minor = (-1-num) << MdpMinorShift; + else + minor = num; + dn = map_dev(major,minor); + if (dn==NULL || is_standard(dn)) { + /* this number only used by a 'standard' name, + * so it is safe to use + */ + break; + } + } + } + /* 'num' is the number to use, >=0 for md, <0 for mdp */ + if (must_remove) { + /* never remove a device name that ends /mdNN or /dNN, + * that would be confusing + */ + if (is_standard(dev)) { + fprintf(stderr, Name ": --auto refusing to remove %s as it looks like a standard name.\n", + dev); + return -1; + } + unlink(dev); + } + + if (mknod(dev, S_IFBLK|0600, MKDEV(major, minor))!= 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); + } + make_parts(dev,autof); + } + mdfd = open(dev, O_RDWR, 0); if (mdfd < 0) fprintf(stderr, Name ": error opening %s: %s\n", dev, strerror(errno)); @@ -57,7 +204,7 @@ int main(int argc, char *argv[]) int rv; int chunk = 0; - int size = 0; + int size = -1; int level = UnSet; int layout = UnSet; int raiddisks = 0; @@ -79,6 +226,8 @@ int main(int argc, char *argv[]) int brief = 0; int force = 0; int test = 0; + int assume_clean = 0; + int autof = 0; /* -1 for non-partitions, 1 or more to create partitions */ char *mailaddr = NULL; char *program = NULL; @@ -114,6 +263,7 @@ int main(int argc, char *argv[]) case MANAGE : help_text = Help_manage; break; case MISC : help_text = Help_misc; break; case MONITOR : help_text = Help_monitor; break; + case GROW : help_text = Help_grow; break; } fputs(help_text,stderr); exit(0); @@ -150,6 +300,7 @@ int main(int argc, char *argv[]) case 'B': newmode = BUILD; break; case 'C': newmode = CREATE; break; case 'F': newmode = MONITOR;break; + case 'G': newmode = GROW; break; case '#': case 'D': @@ -200,13 +351,14 @@ int main(int argc, char *argv[]) case 'B': case 'C': case 'F': + case 'G': continue; } if (opt == 1) { /* an undecorated option - must be a device name. */ if (devs_found > 0 && mode == '@' && !devmode) { - fprintf(stderr, Name ": Must give on of -a/-r/-f for subsequent devices at %s\n", optarg); + fprintf(stderr, Name ": Must give one of -a/-r/-f for subsequent devices at %s\n", optarg); exit(2); } dv = malloc(sizeof(*dv)); @@ -243,17 +395,22 @@ int main(int argc, char *argv[]) } continue; + case O(GROW,'z'): case O(CREATE,'z'): /* size */ - if (size) { + if (size >= 0) { fprintf(stderr, Name ": size may only be specified once. " "Second value is %s.\n", optarg); exit(2); } - size = strtol(optarg, &c, 10); - if (!optarg[0] || *c || size < 4) { - fprintf(stderr, Name ": invalid size: %s\n", - optarg); - exit(2); + if (strcmp(optarg, "max")==0) + size = 0; + else { + size = strtol(optarg, &c, 10); + if (!optarg[0] || *c || size < 4) { + fprintf(stderr, Name ": invalid size: %s\n", + optarg); + exit(2); + } } continue; @@ -270,7 +427,7 @@ int main(int argc, char *argv[]) optarg); exit(2); } - if (level != 0 && level != -1 && mode == BUILD) { + if (level != 0 && level != -1 && level != 1 && level != -4 && mode == BUILD) { fprintf(stderr, Name ": Raid level %s not permitted with --build.\n", optarg); exit(2); @@ -310,6 +467,12 @@ int main(int argc, char *argv[]) } continue; + case O(CREATE,3): + case O(BUILD,3): /* assume clean */ + assume_clean = 1; + continue; + + case O(GROW,'n'): case O(CREATE,'n'): case O(BUILD,'n'): /* number of raid disks */ if (raiddisks) { @@ -350,6 +513,43 @@ int main(int argc, char *argv[]) exit(2); } continue; + + case O(CREATE,'a'): + case O(BUILD,'a'): + case O(ASSEMBLE,'a'): /* auto-creation of device node */ + if (optarg == NULL) + autof = -1; + else if (strcasecmp(optarg,"no")==0) + autof = 0; + else if (strcasecmp(optarg,"yes")==0 || strcasecmp(optarg,"md")==0) + autof = -1; + else { + /* There might be digits, and maybe a hypen, at the end */ + char *e = optarg + strlen(optarg); + int num = 4; + int len; + while (e > optarg && isdigit(e[-1])) + e--; + if (*e) { + num = atoi(e); + if (num <= 0) num = 1; + } + if (e > optarg && e[-1] == '-') + e--; + len = e - optarg; + if ((len == 3 && strncasecmp(optarg,"mdp",3)==0) || + (len == 1 && strncasecmp(optarg,"p",1)==0) || + (len >= 4 && strncasecmp(optarg,"part",4)==0)) + autof = num; + else { + fprintf(stderr, Name ": --auto flag arg of \"%s\" unrecognised: use no,yes,md,mdp,part\n" + " optionally followed by a number.\n", + optarg); + exit(2); + } + } + continue; + case O(BUILD,'f'): /* force honouring '-n 1' */ case O(CREATE,'f'): /* force honouring of device list */ case O(ASSEMBLE,'f'): /* force assembly */ @@ -463,10 +663,7 @@ int main(int argc, char *argv[]) /* now the general management options. Some are applicable * to other modes. None have arguments. */ - case O(MANAGE,'a'): - case O(CREATE,'a'): - case O(BUILD,'a'): - case O(ASSEMBLE,'a'): /* add a drive */ + case O(MANAGE,'a'): /* add a drive */ devmode = 'a'; continue; case O(MANAGE,'r'): /* remove a drive */ @@ -559,12 +756,17 @@ int main(int argc, char *argv[]) * we check that here and open it. */ - if (mode==MANAGE || mode == BUILD || mode == CREATE || (mode == ASSEMBLE && ! scan)) { + if (mode==MANAGE || mode == BUILD || mode == CREATE || mode == GROW || + (mode == ASSEMBLE && ! scan)) { if (devs_found < 1) { fprintf(stderr, Name ": an md device must be given in this mode\n"); exit(2); } - mdfd = open_mddev(devlist->devname); + if ((int)ident.super_minor == -2 && autof) { + fprintf(stderr, Name ": --super-minor=dev is incompatible with --auto\n"); + exit(2); + } + mdfd = open_mddev(devlist->devname, autof); if (mdfd < 0) exit(1); if ((int)ident.super_minor == -2) { @@ -593,7 +795,7 @@ int main(int argc, char *argv[]) ident.super_minor == UnSet && !scan ) { /* Only a device has been given, so get details from config file */ mddev_ident_t array_ident = conf_get_ident(configfile, devlist->devname); - mdfd = open_mddev(devlist->devname); + mdfd = open_mddev(devlist->devname, array_ident->autof); if (mdfd < 0) rv |= 1; else { @@ -618,7 +820,7 @@ int main(int argc, char *argv[]) } for (dv = devlist ; dv ; dv=dv->next) { mddev_ident_t array_ident = conf_get_ident(configfile, dv->devname); - mdfd = open_mddev(dv->devname); + mdfd = open_mddev(dv->devname, array_ident->autof); if (mdfd < 0) { rv |= 1; continue; @@ -641,7 +843,7 @@ int main(int argc, char *argv[]) } else for (; array_list; array_list = array_list->next) { mdu_array_info_t array; - mdfd = open_mddev(array_list->devname); + mdfd = open_mddev(array_list->devname, array_list->autof); if (mdfd < 0) { rv |= 1; continue; @@ -657,10 +859,10 @@ int main(int argc, char *argv[]) } break; case BUILD: - rv = Build(devlist->devname, mdfd, chunk, level, raiddisks, devlist->next); + rv = Build(devlist->devname, mdfd, chunk, level, raiddisks, devlist->next, assume_clean); break; case CREATE: - rv = Create(devlist->devname, mdfd, chunk, level, layout, size, + rv = Create(devlist->devname, mdfd, chunk, level, layout, size<0 ? 0 : size, raiddisks, sparedisks, devs_found-1, devlist->next, runstop, verbose, force); break; @@ -682,7 +884,7 @@ int main(int argc, char *argv[]) if (devlist == NULL) { if ((devmode == 'S' ||devmode=='D') && scan) { /* apply to all devices in /proc/mdstat */ - struct mdstat_ent *ms = mdstat_read(); + struct mdstat_ent *ms = mdstat_read(0); struct mdstat_ent *e; for (e=ms ; e ; e=e->next) { char *name = get_md_name(e->devnum); @@ -695,7 +897,7 @@ int main(int argc, char *argv[]) if (devmode == 'D') rv |= Detail(name, !verbose, test); else if (devmode=='S') { - mdfd = open_mddev(name); + mdfd = open_mddev(name, 0); if (mdfd >= 0) rv |= Manage_runstop(name, mdfd, -1); } @@ -715,7 +917,7 @@ int main(int argc, char *argv[]) case 'Q': rv |= Query(dv->devname); continue; } - mdfd = open_mddev(dv->devname); + mdfd = open_mddev(dv->devname, 0); if (mdfd>=0) switch(dv->disposition) { case 'R': @@ -739,6 +941,20 @@ int main(int argc, char *argv[]) rv= Monitor(devlist, mailaddr, program, delay?delay:60, daemonise, scan, oneshot, configfile, test); break; + + case GROW: + if (devs_found > 1) { + fprintf(stderr, Name ": Only one device may be given for --grow\n"); + rv = 1; + break; + } + if (size >= 0 && raiddisks) { + fprintf(stderr, Name ": can only grow size OR raiddisks, not both\n"); + rv = 1; + break; + } + rv = Manage_resize(devlist->devname, mdfd, size, raiddisks); + break; } exit(rv); } diff --git a/mdadm.conf.5 b/mdadm.conf.5 index 7b455223..97390102 100644 --- a/mdadm.conf.5 +++ b/mdadm.conf.5 @@ -128,6 +128,22 @@ a group of arrays is that will, when monitoring the arrays, move a spare drive from one array in a group to another array in that group if the first array had a failed or missing drive but no spare. + +.TP +.B auto= +This option declares to +.B mdadm +that it should try to create the device file of the array if it +doesn't already exist, or exists but with the wrong device number. + +The value of this option can be "yes" or "md" to indicate that a +traditional, non-partitionable md array should be created, or "mdp", +"part" or "partition" to indicate that a partitionable md array (only +available in linux 2.6 and later) should be used. This later set can +also have a number appended to indicate how many partitions to create +device files for, e.g. +.BR auto=mdp5 . +The default is 4. .RE .TP @@ -191,6 +207,14 @@ ARRAY /dev/md4 uuid=b23f3c6d:aec43a9f:fd65db85:369432df ARRAY /dev/md5 uuid=19464854:03f71b1b:e0df2edd:246cc977 .br spare-group=group1 +.br +# /dev/md/home is created if need to be a partitionable md array +.br +# any spare device number is allocated. +.br +ARRAY /dev/md/home UUID=9187a482:5dde19d9:eea3cc4a:d646ab8b +.br + auto=part MAILADDR root@mydomain.tld .br diff --git a/mdadm.h b/mdadm.h index 6c6cc7fe..90d3a094 100644 --- a/mdadm.h +++ b/mdadm.h @@ -55,6 +55,7 @@ char *strncpy(char *dest, const char *src, size_t n) __THROW; #include #include #define MD_MAJOR 9 +#define MdpMinorShift 6 #ifndef BLKGETSIZE64 #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ @@ -73,12 +74,13 @@ enum mode { MANAGE, MISC, MONITOR, + GROW, }; extern char short_options[]; extern struct option long_options[]; extern char Version[], Usage[], Help[], OptionHelp[], - Help_create[], Help_build[], Help_assemble[], + Help_create[], Help_build[], Help_assemble[], Help_grow[], Help_manage[], Help_misc[], Help_monitor[], Help_config[]; /* structures read from config file */ @@ -93,20 +95,22 @@ extern char Version[], Usage[], Help[], OptionHelp[], */ #define UnSet (0xfffe) typedef struct mddev_ident_s { - char *devname; + char *devname; - int uuid_set; - __u32 uuid[4]; + int uuid_set; + __u32 uuid[4]; unsigned int super_minor; - char *devices; /* comma separated list of device + char *devices; /* comma separated list of device * names with wild cards */ - int level; + int level; unsigned int raid_disks; unsigned int spare_disks; - char *spare_group; + int autof; /* 1 for normal, 2 for partitioned */ + char *spare_group; + struct mddev_ident_s *next; } *mddev_ident_t; @@ -135,8 +139,9 @@ struct mdstat_ent { struct mdstat_ent *next; }; -extern struct mdstat_ent *mdstat_read(void); +extern struct mdstat_ent *mdstat_read(int); extern void free_mdstat(struct mdstat_ent *ms); +extern void mdstat_wait(int seconds); #ifndef Sendmail #define Sendmail "/usr/lib/sendmail -t" @@ -151,6 +156,7 @@ extern char *map_dev(int major, int minor); extern int Manage_ro(char *devname, int fd, int readonly); extern int Manage_runstop(char *devname, int fd, int runstop); +extern int Manage_resize(char *devname, int fd, long long size, int raid_disks); extern int Manage_subdevs(char *devname, int fd, mddev_dev_t devlist); @@ -165,7 +171,7 @@ extern int Assemble(char *mddev, int mdfd, extern int Build(char *mddev, int mdfd, int chunk, int level, int raiddisks, - mddev_dev_t devlist); + mddev_dev_t devlist, int assume_clean); extern int Create(char *mddev, int mdfd, @@ -190,6 +196,10 @@ extern int check_ext2(int fd, char *name); extern int check_reiser(int fd, char *name); extern int check_raid(int fd, char *name); +extern int get_mdp_major(void); +extern int is_standard(char *dev); + + extern mddev_ident_t conf_get_ident(char *conffile, char *dev); extern mddev_dev_t conf_get_devs(char *conffile); extern char *conf_get_mailaddr(char *conffile); diff --git a/mdadm.spec b/mdadm.spec index 815ccd94..7465b340 100644 --- a/mdadm.spec +++ b/mdadm.spec @@ -1,6 +1,6 @@ Summary: mdadm is used for controlling Linux md devices (aka RAID arrays) Name: mdadm -Version: 1.5.0 +Version: 1.6.0 Release: 1 Source: http://www.cse.unsw.edu.au/~neilb/source/mdadm/mdadm-%{version}.tgz URL: http://www.cse.unsw.edu.au/~neilb/source/mdadm/ diff --git a/mdassemble b/mdassemble new file mode 100755 index 00000000..cd8f0422 Binary files /dev/null and b/mdassemble differ diff --git a/mdstat.c b/mdstat.c index 9711e54a..3204d2e4 100644 --- a/mdstat.c +++ b/mdstat.c @@ -85,6 +85,7 @@ #include "mdadm.h" #include "dlink.h" +#include void free_mdstat(struct mdstat_ent *ms) { @@ -99,13 +100,18 @@ void free_mdstat(struct mdstat_ent *ms) } } -struct mdstat_ent *mdstat_read() +static int mdstat_fd = -1; +struct mdstat_ent *mdstat_read(int hold) { FILE *f; struct mdstat_ent *all, **end; char *line; - f = fopen("/proc/mdstat", "r"); + if (hold && mdstat_fd != -1) { + lseek(mdstat_fd, 0L, 0); + f = fdopen(dup(mdstat_fd), "r"); + } else + f = fopen("/proc/mdstat", "r"); if (f == NULL) return NULL; @@ -141,8 +147,7 @@ struct mdstat_ent *mdstat_read() if (!ent) { fprintf(stderr, Name ": malloc failed reading /proc/mdstat.\n"); free_line(line); - fclose(f); - return all; + break; } ent->dev = ent->level = ent->pattern= NULL; ent->next = NULL; @@ -184,6 +189,20 @@ struct mdstat_ent *mdstat_read() *end = ent; end = &ent->next; } + if (hold && mdstat_fd == -1) + mdstat_fd = dup(fileno(f)); fclose(f); return all; } + +void mdstat_wait(int seconds) +{ + fd_set fds; + struct timeval tm; + FD_ZERO(&fds); + if (mdstat_fd >= 0) + FD_SET(mdstat_fd, &fds); + tm.tv_sec = seconds; + tm.tv_usec = 0; + select(mdstat_fd >2 ? mdstat_fd+1:3, NULL, NULL, &fds, &tm); +} diff --git a/t b/t new file mode 100644 index 00000000..baf4ab55 --- /dev/null +++ b/t @@ -0,0 +1 @@ +ARRAY /dev/fred auto=parti /dev/fred diff --git a/util.c b/util.c index bdafe66c..2f4ad963 100644 --- a/util.c +++ b/util.c @@ -421,23 +421,57 @@ int add_dev(const char *name, const struct stat *stb, int flag) return 0; } +int is_standard(char *dev) +{ + /* tests if dev is a "standard" md dev name. + * i.e if the last component is "/dNN" or "/mdNN", + * where NN is a string of digits + */ + dev = strrchr(dev, '/'); + if (!dev) + return 0; + if (strncmp(dev, "/d",2)==0) + dev += 2; + else if (strncmp(dev, "/md", 3)==0) + dev += 3; + else + return 0; + if (!*dev) + return 0; + while (isdigit(*dev)) + dev++; + if (*dev) + return 0; + return 1; +} + + +/* + * Find a block device with the right major/minor number. + * Avoid /dev/mdNN and /dev/md/dNN is possible + */ char *map_dev(int major, int minor) { - struct devmap *p; - if (!devlist_ready) { + struct devmap *p; + char *std = NULL; + if (!devlist_ready) { #ifndef __dietlibc__ - nftw("/dev", add_dev, 10, FTW_PHYS); + nftw("/dev", add_dev, 10, FTW_PHYS); #else - ftw("/dev", add_dev, 10); + ftw("/dev", add_dev, 10); #endif - devlist_ready=1; - } + devlist_ready=1; + } - for (p=devlist; p; p=p->next) - if (p->major == major && - p->minor == minor) - return p->name; - return NULL; + for (p=devlist; p; p=p->next) + if (p->major == major && + p->minor == minor) { + if (is_standard(p->name)) + std = p->name; + else + return p->name; + } + return std; } #endif @@ -504,16 +538,20 @@ char *human_size_brief(long long bytes) return buf; } -static int mdp_major = -1; -void get_mdp_major(void) +int get_mdp_major(void) { - FILE *fl = fopen("/proc/devices", "r"); +static int mdp_major = -1; + FILE *fl; char *w; int have_block = 0; int have_devices = 0; int last_num = -1; + + if (mdp_major != -1) + return mdp_major; + fl = fopen("/proc/devices", "r"); if (!fl) - return; + return -1; while ((w = conf_word(fl, 1))) { if (have_block && strcmp(w, "devices:")==0) have_devices = 1; @@ -525,6 +563,7 @@ void get_mdp_major(void) free(w); } fclose(fl); + return mdp_major; } @@ -536,12 +575,12 @@ char *get_md_name(int dev) static char devname[50]; struct stat stb; dev_t rdev; + char *dn; if (dev < 0) { - - if (mdp_major < 0) get_mdp_major(); - if (mdp_major < 0) return NULL; - rdev = MKDEV(mdp_major, (-1-dev)<<6); + int mdp = get_mdp_major(); + if (mdp < 0) return NULL; + rdev = MKDEV(mdp, (-1-dev)<<6); sprintf(devname, "/dev/md/d%d", -1-dev); if (stat(devname, &stb) == 0 && (S_IFMT&stb.st_mode) == S_IFBLK @@ -561,9 +600,13 @@ char *get_md_name(int dev) && (stb.st_rdev == rdev)) return devname; } + dn = map_dev(MAJOR(rdev), MINOR(rdev)); + if (dn) + return dn; sprintf(devname, "/dev/.tmp.md%d", dev); if (mknod(devname, S_IFBLK | 0600, rdev) == -1) - return NULL; + if (errno != EEXIST) + return NULL; if (stat(devname, &stb) == 0 && (S_IFMT&stb.st_mode) == S_IFBLK