mdadm-1.6.0

This commit is contained in:
Neil Brown 2004-06-04 12:03:19 +00:00
parent 98c6faba80
commit dd0781e505
20 changed files with 736 additions and 102 deletions

36
ANNOUNCE-1.6.0 Normal file
View File

@ -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

View File

@ -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;

View File

@ -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 <bluca@comedia.it>.
@ -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.

View File

@ -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, &param)) {
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);

View File

@ -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<<MD_SB_CLEAN))?"clean":"dirty",
(array.state&(1<<MD_SB_ERRORS))?"":"no-");
array.active_disks < array.raid_disks? ", degraded":"",
(e && e->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; d<MD_SB_DISKS; d++) {

View File

@ -152,9 +152,8 @@ int Examine(mddev_dev_t devlist, int brief, int scan, int SparcAdjust)
printf("\n");
atime = super.utime;
printf(" Update Time : %.24s\n", ctime(&atime));
printf(" State : %s, %serrors\n",
(super.state&(1<<MD_SB_CLEAN))?"clean":"dirty",
(super.state&(1<<MD_SB_ERRORS))?"":"no-");
printf(" State : %s\n",
(super.state&(1<<MD_SB_CLEAN))?"clean":"dirty");
printf(" Active Devices : %d\n", super.active_disks);
printf("Working Devices : %d\n", super.working_disks);
printf(" Failed Devices : %d\n", super.failed_disks);
@ -236,8 +235,9 @@ int Examine(mddev_dev_t devlist, int brief, int scan, int SparcAdjust)
char sep='=';
char *c=map_num(pers, ap->super.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,

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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},
};

12
TODO
View File

@ -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

View File

@ -32,6 +32,7 @@
#include <sys/dir.h>
#include <glob.h>
#include <fnmatch.h>
#include <ctype.h>
/*
* 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"); */
}

170
mdadm.8
View File

@ -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"

266
mdadm.c
View File

@ -29,10 +29,157 @@
#include "mdadm.h"
#include "md_p.h"
#include <ctype.h>
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);
}

View File

@ -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

28
mdadm.h
View File

@ -55,6 +55,7 @@ char *strncpy(char *dest, const char *src, size_t n) __THROW;
#include <asm/types.h>
#include <sys/ioctl.h>
#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);

View File

@ -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/

BIN
mdassemble Executable file

Binary file not shown.

View File

@ -85,6 +85,7 @@
#include "mdadm.h"
#include "dlink.h"
#include <sys/select.h>
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);
}

1
t Normal file
View File

@ -0,0 +1 @@
ARRAY /dev/fred auto=parti /dev/fred

83
util.c
View File

@ -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