mdctl-v0.4
This commit is contained in:
parent
682c705194
commit
82b27616de
97
Assemble.c
97
Assemble.c
|
@ -107,7 +107,7 @@ int Assemble(char *mddev, int mdfd,
|
|||
int most_recent = 0;
|
||||
|
||||
if (!mddev && !scan) {
|
||||
fputs(Name ": internal error - Assemble called with no devie or scan\n", stderr);
|
||||
fputs(Name ": internal error - Assemble called with no device or --scan\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
if (!mddev) {
|
||||
|
@ -118,7 +118,7 @@ int Assemble(char *mddev, int mdfd,
|
|||
fprintf(stderr, Name ": No devices found in config file\n");
|
||||
return 1;
|
||||
}
|
||||
while (device_list) {
|
||||
for (; device_list; device_list=device_list->next) {
|
||||
if (!uuidset || same_uuid(device_list->uuid,uuid)) {
|
||||
mdfd = open(device_list->devname, O_RDONLY, 0);
|
||||
if (mdfd < 0) {
|
||||
|
@ -136,11 +136,10 @@ int Assemble(char *mddev, int mdfd,
|
|||
found++;
|
||||
close(mdfd);
|
||||
}
|
||||
device_list = device_list->next;
|
||||
}
|
||||
if (found)
|
||||
return 0;
|
||||
fprintf(stderr,Name ": Did not successful Assemble any devices\n");
|
||||
fprintf(stderr,Name ": Did not successfully Assemble any devices\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -206,6 +205,10 @@ int Assemble(char *mddev, int mdfd,
|
|||
for (i=0; i<MD_SB_DISKS; i++)
|
||||
best[i] = -1;
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, Name ": looking for devices for %s\n",
|
||||
mddev);
|
||||
|
||||
while (subdevs || devlist) {
|
||||
char *devname;
|
||||
int this_uuid[4];
|
||||
|
@ -250,6 +253,13 @@ int Assemble(char *mddev, int mdfd,
|
|||
continue;
|
||||
}
|
||||
close(dfd);
|
||||
uuid_from_super(this_uuid, &super);
|
||||
if (uuidset && !same_uuid(this_uuid, uuid)) {
|
||||
if (inargv || verbose)
|
||||
fprintf(stderr, Name ": %s has wrong uuid.\n",
|
||||
devname);
|
||||
continue;
|
||||
}
|
||||
if (compare_super(&first_super, &super)) {
|
||||
if (inargv || verbose)
|
||||
fprintf(stderr, Name ": superblock on %s doesn't match\n",
|
||||
|
@ -341,8 +351,8 @@ int Assemble(char *mddev, int mdfd,
|
|||
devices[j].devname,
|
||||
mddev,
|
||||
strerror(errno));
|
||||
} else
|
||||
okcnt--;
|
||||
}
|
||||
} else if (verbose)
|
||||
fprintf(stderr, Name ": no uptodate device for slot %d of %s\n",
|
||||
i, mddev);
|
||||
|
@ -350,17 +360,84 @@ int Assemble(char *mddev, int mdfd,
|
|||
if (runstop == 1 ||
|
||||
(runstop == 0 &&
|
||||
enough(first_super.level, first_super.raid_disks, okcnt))) {
|
||||
if (ioctl(mdfd, RUN_ARRAY, NULL)==0)
|
||||
if (ioctl(mdfd, RUN_ARRAY, NULL)==0) {
|
||||
fprintf(stderr, Name ": %s has been started with %d drives\n",
|
||||
mddev, okcnt);
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, Name ": failed to RUN_ARRAY %s: %s\n",
|
||||
mddev, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (runstop == -1)
|
||||
if (runstop == -1) {
|
||||
fprintf(stderr, Name ": %s assembled from %d drives, but not started.\n",
|
||||
mddev, okcnt);
|
||||
return 0;
|
||||
else return 1;
|
||||
} else {
|
||||
/* FIXME */
|
||||
}
|
||||
fprintf(stderr, Name ": %s assembled from %d drives - not enough to start it.\n",
|
||||
mddev, okcnt);
|
||||
return 1;
|
||||
} else {
|
||||
/* It maybe just a case of calling START_ARRAY, but it may not..
|
||||
* We need to pick a working device, read it's super block, and make
|
||||
* sure all the device numbers and the minor number are right.
|
||||
* Then we might need to re-write the super block.
|
||||
* THEN we call START_ARRAY
|
||||
* If the md_minor is wrong, wejust give up for now. The alternate is to
|
||||
* re-write ALL super blocks.
|
||||
*/
|
||||
int chosen_drive = -1;
|
||||
int change = 0;
|
||||
int dev;
|
||||
for (i=0; i<first_super.nr_disks; i++) {
|
||||
if (!devices[i].uptodate)
|
||||
continue;
|
||||
if (chosen_drive == -1) {
|
||||
int fd;
|
||||
chosen_drive = i;
|
||||
if (open(devices[i].devname, O_RDONLY)>= 0) {
|
||||
fprintf(stderr, Name ": Cannot open %s: %s\n",
|
||||
devices[i].devname, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (load_super(fd, &super)) {
|
||||
close(fd);
|
||||
fprintf(stderr, Name ": RAID superblock has disappeared from %s\n",
|
||||
devices[i].devname);
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
if (devices[i].major != super.disks[i].major ||
|
||||
devices[i].minor != super.disks[i].minor) {
|
||||
change = 1;
|
||||
super.disks[i].major = devices[i].major;
|
||||
super.disks[i].minor = devices[i].minor;
|
||||
}
|
||||
}
|
||||
if (change) {
|
||||
int fd;
|
||||
super.sb_csum = calc_sb_csum(super);
|
||||
fd = open(devices[chosen_drive].devname, O_RDWR);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, Name ": Could open %s for write - cannot Assemble array.\n",
|
||||
devices[chosen_drive].devname);
|
||||
return 1;
|
||||
}
|
||||
if (store_super(fd, &super)) {
|
||||
close(fd);
|
||||
fprintf(stderr, Name ": Could not re-write superblock on %s\n",
|
||||
devices[chosen_drive].devname);
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
dev = MKDEV(devices[chosen_drive].major,
|
||||
devices[chosen_drive].minor);
|
||||
if (ioctl(mdfd, START_ARRAY, dev)) {
|
||||
fprintf(stderr, Name ": Cannot start array: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
133
Build.c
133
Build.c
|
@ -29,8 +29,141 @@
|
|||
|
||||
#include "mdctl.h"
|
||||
|
||||
#define REGISTER_DEV _IO (MD_MAJOR, 1)
|
||||
#define START_MD _IO (MD_MAJOR, 2)
|
||||
#define STOP_MD _IO (MD_MAJOR, 3)
|
||||
|
||||
int Build(char *mddev, int mdfd, int chunk, int level,
|
||||
int raiddisks,
|
||||
int subdevs, char *subdev[])
|
||||
{
|
||||
/* Build a linear or raid0 arrays without superblocks
|
||||
* We cannot really do any checks, we just do it.
|
||||
* For md_version < 0.90.0, we call REGISTER_DEV
|
||||
* with the device numbers, and then
|
||||
* START_MD giving the "geometry"
|
||||
* geometry is 0xpp00cc
|
||||
* where pp is personality: 1==linear, 2=raid0
|
||||
* cc = chunk size factor: 0==4k, 1==8k etc.
|
||||
*
|
||||
* For md_version >= 0.90.0 we call
|
||||
* SET_ARRAY_INFO, ADD_NEW_DISK, RUN_ARRAY
|
||||
*
|
||||
*/
|
||||
int i;
|
||||
int vers;
|
||||
struct stat stb;
|
||||
if (raiddisks != subdevs) {
|
||||
fprintf(stderr, Name ": requested %d devices in array but listed %d\n",
|
||||
raiddisks, subdevs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* scan all devices, make sure they really are block devices */
|
||||
for (i=0; i<subdevs; i++) {
|
||||
if (stat(subdev[i], &stb)) {
|
||||
fprintf(stderr, Name ": Cannot find %s: %s\n",
|
||||
subdev[i], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if ((stb.st_mode & S_IFMT) != S_IFBLK) {
|
||||
fprintf(stderr, Name ": %s is not a block device.\n",
|
||||
subdev[i]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
vers = md_get_version(mdfd);
|
||||
|
||||
/* looks Ok, go for it */
|
||||
if (vers >= 90000) {
|
||||
mdu_array_info_t array;
|
||||
array.level = level;
|
||||
array.size = 0;
|
||||
array.nr_disks = raiddisks;
|
||||
array.raid_disks = raiddisks;
|
||||
array.md_minor = 0;
|
||||
if (fstat(mdfd, &stb)==0)
|
||||
array.md_minor = MINOR(stb.st_rdev);
|
||||
array.not_persistent = 0;
|
||||
array.state = 0; /* not clean, but no errors */
|
||||
array.active_disks = raiddisks;
|
||||
array.working_disks = raiddisks;
|
||||
array.spare_disks = 0;
|
||||
array.failed_disks = 0;
|
||||
array.chunk_size = chunk*1024;
|
||||
if (ioctl(mdfd, SET_ARRAY_INFO, &array)) {
|
||||
fprintf(stderr, Name ": SET_ARRAY_INFO failed for %s: %s\n",
|
||||
mddev, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* now add the devices */
|
||||
for (i=0; i<subdevs; i++) {
|
||||
if (stat(subdev[i], &stb)) {
|
||||
fprintf(stderr, Name ": Wierd: %s has disappeared.\n",
|
||||
subdev[i]);
|
||||
goto abort;
|
||||
}
|
||||
if ((stb.st_rdev & S_IFMT)!= S_IFBLK) {
|
||||
fprintf(stderr, Name ": Wierd: %s is no longer a block device.\n",
|
||||
subdev[i]);
|
||||
goto abort;
|
||||
}
|
||||
if (vers> 90000) {
|
||||
mdu_disk_info_t disk;
|
||||
disk.number = i;
|
||||
disk.raid_disk = i;
|
||||
disk.state = 6;
|
||||
disk.major = MAJOR(stb.st_rdev);
|
||||
disk.minor = MINOR(stb.st_rdev);
|
||||
if (ioctl(mdfd, ADD_NEW_DISK, &disk)) {
|
||||
fprintf(stderr, Name ": ADD_NEW_DISK failed for %s: %s\n",
|
||||
subdev[i], strerror(errno));
|
||||
goto abort;
|
||||
}
|
||||
} else {
|
||||
if (ioctl(mdfd, REGISTER_DEV, &stb.st_rdev)) {
|
||||
fprintf(stderr, Name ": REGISTER_DEV failed for %s.\n",
|
||||
subdev[i], strerror(errno));
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* now to start it */
|
||||
if (vers > 90000) {
|
||||
mdu_param_t param; /* not used by syscall */
|
||||
if (ioctl(mdfd, RUN_ARRAY, param)) {
|
||||
fprintf(stderr, Name ": RUN_ARRAY failed: %s\n",
|
||||
strerror(errno));
|
||||
goto abort;
|
||||
}
|
||||
} else {
|
||||
int arg;
|
||||
arg=0;
|
||||
while (chunk > 4096) {
|
||||
arg++;
|
||||
chunk >>= 1;
|
||||
}
|
||||
if (level == 0)
|
||||
chunk |= 0x20000;
|
||||
else chunk |= 0x10000;
|
||||
if (ioctl(mdfd, START_MD, arg)) {
|
||||
fprintf(stderr, Name ": START_MD failed: %s\n",
|
||||
strerror(errno));
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, Name ": array %s built and started.\n",
|
||||
mddev);
|
||||
return 0;
|
||||
|
||||
abort:
|
||||
if (vers > 900000)
|
||||
ioctl(mdfd, STOP_ARRAY, 0);
|
||||
else
|
||||
ioctl(mdfd, STOP_MD, 0);
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
|
|
53
Create.c
53
Create.c
|
@ -56,9 +56,9 @@ int Create(char *mddev, int mdfd,
|
|||
int maxdisc= -1, mindisc = -1;
|
||||
int i;
|
||||
int fail=0, warn=0;
|
||||
struct stat stb;
|
||||
|
||||
mdu_array_info_t array;
|
||||
mdu_param_t param;
|
||||
|
||||
|
||||
if (md_get_version(mdfd) < 9000) {
|
||||
|
@ -188,14 +188,42 @@ int Create(char *mddev, int mdfd,
|
|||
|
||||
array.level = level;
|
||||
array.size = size;
|
||||
array.nr_disks = raiddisks+sparedisks;
|
||||
array.nr_disks = raiddisks+sparedisks+(level==4||level==5);
|
||||
array.raid_disks = raiddisks;
|
||||
/* The kernel should *know* what md_minor we are dealing
|
||||
* with, but it chooses to trust me instead. Sigh
|
||||
*/
|
||||
array.md_minor = 0;
|
||||
if (fstat(mdfd, &stb)==0)
|
||||
array.md_minor = MINOR(stb.st_rdev);
|
||||
array.not_persistent = 0;
|
||||
array.state = 0; /* not clean, but no errors */
|
||||
array.active_disks=0;
|
||||
array.working_disks=0;
|
||||
array.spare_disks=0;
|
||||
if (level == 4 || level == 5)
|
||||
array.state = 1; /* clean, but one drive will be missing */
|
||||
else
|
||||
array.state = 0; /* not clean, but no errors */
|
||||
|
||||
/* There is lots of redundancy in these disk counts,
|
||||
* raid_disks is the most meaningful value
|
||||
* it describes the geometry of the array
|
||||
* it is constant
|
||||
* nr_disks is total number of used slots.
|
||||
* it should be raid_disks+spare_disks
|
||||
* spare_disks is the number of extra disks present
|
||||
* see above
|
||||
* active_disks is the number of working disks in
|
||||
* active slots. (With raid_disks)
|
||||
* working_disks is the total number of working disks,
|
||||
* including spares
|
||||
* failed_disks is the number of disks marked failed
|
||||
*
|
||||
* Ideally, the kernel would keep these (except raid_disks)
|
||||
* up-to-date as we ADD_NEW_DISK, but it doesn't (yet).
|
||||
* So for now, we assume that all raid and spare
|
||||
* devices will be given.
|
||||
*/
|
||||
array.active_disks=raiddisks-(level==4||level==5);
|
||||
array.working_disks=raiddisks+sparedisks;
|
||||
array.spare_disks=sparedisks + (level==4||level==5);
|
||||
array.failed_disks=0;
|
||||
array.layout = layout;
|
||||
array.chunk_size = chunk*1024;
|
||||
|
@ -217,13 +245,19 @@ int Create(char *mddev, int mdfd,
|
|||
}
|
||||
fstat(fd, &stb);
|
||||
disk.number = i;
|
||||
disk.raid_disk = i;
|
||||
disk.state = 6; /* active and in sync */
|
||||
if ((level==4 || level==5) &&
|
||||
disk.number >= raiddisks-1)
|
||||
disk.number++;
|
||||
disk.raid_disk = disk.number;
|
||||
if (disk.raid_disk < raiddisks)
|
||||
disk.state = 6; /* active and in sync */
|
||||
else
|
||||
disk.state = 0;
|
||||
disk.major = MAJOR(stb.st_rdev);
|
||||
disk.minor = MINOR(stb.st_rdev);
|
||||
close(fd);
|
||||
if (ioctl(mdfd, ADD_NEW_DISK, &disk)) {
|
||||
fprintf(stderr, Name ": ADD_NEW_DISK for %s failed: %s\b",
|
||||
fprintf(stderr, Name ": ADD_NEW_DISK for %s failed: %s\n",
|
||||
subdev[i], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
@ -231,6 +265,7 @@ int Create(char *mddev, int mdfd,
|
|||
|
||||
/* param is not actually used */
|
||||
if (runstop == 1 || subdevs >= raiddisks) {
|
||||
mdu_param_t param;
|
||||
if (ioctl(mdfd, RUN_ARRAY, ¶m)) {
|
||||
fprintf(stderr, Name ": RUN_ARRAY failed: %s\n",
|
||||
strerror(errno));
|
||||
|
|
38
Detail.c
38
Detail.c
|
@ -45,6 +45,9 @@ int Detail(char *dev)
|
|||
time_t atime;
|
||||
char *c;
|
||||
|
||||
mdp_super_t super;
|
||||
int have_super = 0;
|
||||
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, Name ": cannot open %s: %s\n",
|
||||
dev, strerror(errno));
|
||||
|
@ -102,11 +105,23 @@ int Detail(char *dev)
|
|||
c = map_num(r5layout, array.layout);
|
||||
printf(" Layout : %s\n", c?c:"-unknown-");
|
||||
}
|
||||
printf(" Chunk Size : %dK\n", array.chunk_size/1024);
|
||||
switch (array.level) {
|
||||
case 0:
|
||||
case 4:
|
||||
case 5:
|
||||
printf(" Chunk Size : %dK\n", array.chunk_size/1024);
|
||||
break;
|
||||
case -1:
|
||||
printf(" Rounding : %dK\n", array.chunk_size/1024);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
printf(" Number Major Minor RaidDisk State\n");
|
||||
for (d= 0; d<array.nr_disks; d++) {
|
||||
mdu_disk_info_t disk;
|
||||
char *dv;
|
||||
disk.number = d;
|
||||
if (ioctl(fd, GET_DISK_INFO, &disk) < 0) {
|
||||
fprintf(stderr, Name ": cannot get disk detail for disk %d: %s\n",
|
||||
|
@ -119,7 +134,28 @@ int Detail(char *dev)
|
|||
if (disk.state & (1<<MD_DISK_ACTIVE)) printf(" active");
|
||||
if (disk.state & (1<<MD_DISK_SYNC)) printf(" sync");
|
||||
if (disk.state & (1<<MD_DISK_REMOVED)) printf(" removed");
|
||||
if ((dv=map_dev(disk.major, disk.minor))) {
|
||||
printf(" %s", dv);
|
||||
if (!have_super) {
|
||||
/* try to read the superblock from this device
|
||||
* to get more info
|
||||
*/
|
||||
int fd = open(dv, O_RDONLY);
|
||||
if (fd >=0 &&
|
||||
load_super(fd, &super) ==0 &&
|
||||
super.ctime == array.ctime &&
|
||||
super.level == array.level)
|
||||
have_super = 1;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
if (have_super) {
|
||||
if (super.minor_version >= 90)
|
||||
printf(" UUID : %08x:%08x:%08x:%08x\n", super.set_uuid0, super.set_uuid1,
|
||||
super.set_uuid2, super.set_uuid3);
|
||||
else
|
||||
printf(" UUID : %08x\n", super.set_uuid0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
25
Examine.c
25
Examine.c
|
@ -29,6 +29,9 @@
|
|||
|
||||
#include "mdctl.h"
|
||||
|
||||
#if ! defined(__BIG_ENDIAN) && ! defined(__LITTLE_ENDIAN)
|
||||
#error no endian defined
|
||||
#endif
|
||||
#include "md_u.h"
|
||||
#include "md_p.h"
|
||||
int Examine(char *dev)
|
||||
|
@ -47,7 +50,7 @@ int Examine(char *dev)
|
|||
* utime, state etc
|
||||
*
|
||||
*/
|
||||
int fd = open(dev, O_RDONLY, 0);
|
||||
int fd = open(dev, O_RDONLY);
|
||||
time_t atime;
|
||||
mdp_super_t super;
|
||||
int d;
|
||||
|
@ -121,18 +124,32 @@ int Examine(char *dev)
|
|||
printf(" Working Drives : %d\n", super.working_disks);
|
||||
printf(" Failed Drives : %d\n", super.failed_disks);
|
||||
printf(" Spare Drives : %d\n", super.spare_disks);
|
||||
printf(" - checksum not checked yet - \n");
|
||||
if (calc_sb_csum(&super) == super.sb_csum)
|
||||
printf(" Checksum : %x - correct\n", super.sb_csum);
|
||||
else
|
||||
printf(" Checksum : %x - expected %x\n", super.sb_csum, calc_sb_csum(&super));
|
||||
printf(" Events : %d.%d\n", super.events_hi, super.events_lo);
|
||||
printf("\n");
|
||||
if (super.level == 5) {
|
||||
c = map_num(r5layout, super.layout);
|
||||
printf(" Layout : %s\n", c?c:"-unknown-");
|
||||
}
|
||||
printf(" Chunk Size : %dK\n", super.chunk_size/1024);
|
||||
switch(super.level) {
|
||||
case 0:
|
||||
case 4:
|
||||
case 5:
|
||||
printf(" Chunk Size : %dK\n", super.chunk_size/1024);
|
||||
break;
|
||||
case -1:
|
||||
printf(" Rounding : %dK\n", super.chunk_size/1024);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
printf("\n");
|
||||
printf(" Number Major Minor RaidDisk State\n");
|
||||
for (d= -1; d<(signed int)super.nr_disks; d++) {
|
||||
mdp_disk_t *dp;
|
||||
char *dv;
|
||||
char nb[5];
|
||||
if (d>=0) dp = &super.disks[d];
|
||||
else dp = &super.this_disk;
|
||||
|
@ -143,6 +160,8 @@ int Examine(char *dev)
|
|||
if (dp->state & (1<<MD_DISK_ACTIVE)) printf(" active");
|
||||
if (dp->state & (1<<MD_DISK_SYNC)) printf(" sync");
|
||||
if (dp->state & (1<<MD_DISK_REMOVED)) printf(" removed");
|
||||
if ((dv=map_dev(dp->major, dp->minor)))
|
||||
printf(" %s", dv);
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
|
|
4
Makefile
4
Makefile
|
@ -27,9 +27,9 @@
|
|||
# Australia
|
||||
#
|
||||
|
||||
CFLAGS = -Wall,error
|
||||
CFLAGS = -Wall,error,strict-prototypes -ggdb
|
||||
|
||||
OBJS = mdctl.o config.o ReadMe.o util.o Manage.o Assemble.o Build.o Create.o Detail.o Examine.o
|
||||
OBJS = mdctl.o config.o ReadMe.o util.o Manage.o Assemble.o Build.o Create.o Detail.o Examine.o dlink.o
|
||||
all : mdctl
|
||||
|
||||
mdctl : $(OBJS)
|
||||
|
|
19
Manage.c
19
Manage.c
|
@ -31,6 +31,10 @@
|
|||
#include "md_u.h"
|
||||
#include "md_p.h"
|
||||
|
||||
#define REGISTER_DEV _IO (MD_MAJOR, 1)
|
||||
#define START_MD _IO (MD_MAJOR, 2)
|
||||
#define STOP_MD _IO (MD_MAJOR, 3)
|
||||
|
||||
int Manage_ro(char *devname, int fd, int readonly)
|
||||
{
|
||||
/* switch to readonly or rw
|
||||
|
@ -60,7 +64,7 @@ int Manage_ro(char *devname, int fd, int readonly)
|
|||
}
|
||||
} else if (readonly < 0) {
|
||||
if (ioctl(fd, RESTART_ARRAY_RW, NULL)) {
|
||||
fprintf(stderr, Name ": fail to re writable for %s: %s\n",
|
||||
fprintf(stderr, Name ": failed to set writable for %s: %s\n",
|
||||
devname, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
@ -75,17 +79,26 @@ int Manage_runstop(char *devname, int fd, int runstop)
|
|||
*/
|
||||
mdu_array_info_t array;
|
||||
mdu_param_t param; /* unused */
|
||||
|
||||
if (runstop == -1 && md_get_version(fd) < 9000) {
|
||||
if (ioctl(fd, STOP_MD, 0)) {
|
||||
fprintf(stderr, Name ": stopping device %s failed: %s\n",
|
||||
devname, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (md_get_version(fd) < 9000) {
|
||||
fprintf(stderr, Name ": need md driver version 0.90.0 or later\n");
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
if (ioctl(fd, GET_ARRAY_INFO, &array)) {
|
||||
fprintf(stderr, Name ": %s does not appear to be active.\n",
|
||||
devname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*/
|
||||
if (runstop>0) {
|
||||
if (ioctl(fd, RUN_ARRAY, ¶m)) {
|
||||
fprintf(stderr, Name ": failed to run array %s: %s\n",
|
||||
|
@ -175,7 +188,7 @@ int Manage_subdevs(char *devname, int fd,
|
|||
|
||||
case 'r':
|
||||
/* hot remove */
|
||||
/* FIXME check that is is a current member */
|
||||
/* FIXME check that it is a current member */
|
||||
if (ioctl(fd, HOT_REMOVE_DISK, stb.st_rdev)) {
|
||||
fprintf(stderr, Name ": hot remove failed for %s: %s\n",
|
||||
devnames[i], strerror(errno));
|
||||
|
|
2
ReadMe.c
2
ReadMe.c
|
@ -29,7 +29,7 @@
|
|||
|
||||
#include "mdctl.h"
|
||||
|
||||
char Version[] = Name " - v0.3 - 14 June 2001\n";
|
||||
char Version[] = Name " - v0.4 - 26 July 2001\n";
|
||||
/*
|
||||
* File: ReadMe.c
|
||||
*
|
||||
|
|
16
TODO
16
TODO
|
@ -1,12 +1,22 @@
|
|||
|
||||
- check superblock checksum in examine
|
||||
- report "chunk" or "rounding" depending on raid level
|
||||
- check superblock checksum in examine DONE
|
||||
- report "chunk" or "rounding" depending on raid level DONE
|
||||
- report "linear" instead of "-1" for raid level DONE
|
||||
- decode ayout depending on raid level DONE
|
||||
- get Assemble to upgrade devices if force flag.
|
||||
- --verbose and --force flags.
|
||||
|
||||
- set md_minor, *_disks for Create
|
||||
- set md_minor, *_disks for Create - DONE
|
||||
- for create raid5, how to choose between
|
||||
all working, but not insync
|
||||
one missing, one spare, insync
|
||||
- when RUN_ARRAY, make sure *_disks counts are right
|
||||
|
||||
- get --detail to extract extra stuff from superblock,
|
||||
like uuid DONE
|
||||
- --detail --brief to give a config file line
|
||||
- parse config file. DONE
|
||||
- test...
|
||||
|
||||
- when --assemble --scan, if an underlying device is an md device,
|
||||
then try to assemble that device first.
|
||||
|
|
289
config.c
289
config.c
|
@ -28,7 +28,8 @@
|
|||
*/
|
||||
|
||||
#include "mdctl.h"
|
||||
|
||||
#include "dlink.h"
|
||||
#include <glob.h>
|
||||
/*
|
||||
* Read the config file
|
||||
*
|
||||
|
@ -38,17 +39,299 @@
|
|||
* Each keeps the returned list and frees it when asked to make
|
||||
* a new list.
|
||||
*
|
||||
* The format of the config file needs to be fairly extensible.
|
||||
* Now, arrays only have names and uuids and devices merely are.
|
||||
* But later arrays might want names, and devices might want superblock
|
||||
* versions, and who knows what else.
|
||||
* I like free format, abhore backslash line continuation, adore
|
||||
* indentation for structure and am ok about # comments.
|
||||
*
|
||||
* So, each line that isn't blank or a #comment must either start
|
||||
* with a key word, and not be indented, or must start with a
|
||||
* non-key-word and must be indented.
|
||||
*
|
||||
* Keywords are DEVICE and ARRAY
|
||||
* DEV{ICE} introduces some devices that might contain raid components.
|
||||
* e.g.
|
||||
* DEV style=0 /dev/sda* /dev/hd*
|
||||
* DEV style=1 /dev/sd[b-f]*
|
||||
* ARR{AY} describes an array giving md device and attributes like uuid=whatever
|
||||
* e.g.
|
||||
* ARRAY /dev/md0 uuid=whatever name=something
|
||||
* Spaces separate words on each line. Quoting, with "" or '' protects them,
|
||||
* but may not wrap over lines
|
||||
*
|
||||
*/
|
||||
|
||||
char DefaultConfFile[] = "/etc/mdctl.conf";
|
||||
|
||||
char *keywords[] = { "device", "array", NULL };
|
||||
|
||||
/*
|
||||
* match_keyword returns an index into the keywords array, or -1 for no match
|
||||
* case is ignored, and at least three characters must be given
|
||||
*/
|
||||
|
||||
int match_keyword(char *word)
|
||||
{
|
||||
int len = strlen(word);
|
||||
int n;
|
||||
|
||||
if (len < 3) return -1;
|
||||
for (n=0; keywords[n]; n++) {
|
||||
if (strncasecmp(word, keywords[n], len)==0)
|
||||
return n;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* conf_word gets one word from the conf file.
|
||||
* if "allow_key", then accept words at the start of a line,
|
||||
* otherwise stop when such a word is found.
|
||||
* We assume that the file pointer is at the end of a word, so the
|
||||
* next character is a space, or a newline. If not, it is the start of a line.
|
||||
*/
|
||||
|
||||
char *conf_word(FILE *file, int allow_key)
|
||||
{
|
||||
int wsize = 100;
|
||||
int len = 0;
|
||||
int c;
|
||||
int quote;
|
||||
int wordfound = 0;
|
||||
char *word = malloc(wsize);
|
||||
|
||||
if (!word) abort();
|
||||
|
||||
while (wordfound==0) {
|
||||
/* at the end of a word.. */
|
||||
c = getc(file);
|
||||
if (c == '#')
|
||||
while (c != EOF && c != '\n')
|
||||
c = getc(file);
|
||||
if (c == EOF) break;
|
||||
if (c == '\n') continue;
|
||||
|
||||
if (c != ' ' && c != '\t' && ! allow_key) {
|
||||
ungetc(c, file);
|
||||
break;
|
||||
}
|
||||
/* looks like it is safe to get a word here, if there is one */
|
||||
quote = 0;
|
||||
/* first, skip any spaces */
|
||||
while (c == ' ' || c == '\t')
|
||||
c = getc(file);
|
||||
if (c != EOF && c != '\n' && c != '#') {
|
||||
/* we really have a character of a word, so start saving it */
|
||||
while (c != EOF && c != '\n' && (quote || (c!=' ' && c != '\t'))) {
|
||||
wordfound = 1;
|
||||
if (quote && c == quote) quote = 0;
|
||||
else if (quote == 0 && (c == '\'' || c == '"'))
|
||||
quote = c;
|
||||
else {
|
||||
if (len == wsize-1) {
|
||||
wsize += 100;
|
||||
word = realloc(word, wsize);
|
||||
if (!word) abort();
|
||||
}
|
||||
word[len++] = c;
|
||||
}
|
||||
c = getc(file);
|
||||
}
|
||||
}
|
||||
if (c != EOF) ungetc(c, file);
|
||||
}
|
||||
word[len] = 0;
|
||||
/* printf("word is <%s>\n", word); */
|
||||
if (!wordfound) {
|
||||
free(word);
|
||||
word = NULL;
|
||||
}
|
||||
return word;
|
||||
}
|
||||
|
||||
/*
|
||||
* conf_line reads one logical line from the conffile.
|
||||
* It skips comments and continues until it finds a line that starts
|
||||
* with a non blank/comment. This character is pushed back for the next call
|
||||
* A doubly linked list of words is returned.
|
||||
* the first word will be a keyword. Other words will have had quotes removed.
|
||||
*/
|
||||
|
||||
char *conf_line(FILE *file)
|
||||
{
|
||||
char *w;
|
||||
char *list;
|
||||
|
||||
w = conf_word(file, 1);
|
||||
if (w == NULL) return NULL;
|
||||
|
||||
list = dl_strdup(w);
|
||||
free(w);
|
||||
dl_init(list);
|
||||
|
||||
while ((w = conf_word(file,0))){
|
||||
char *w2 = dl_strdup(w);
|
||||
free(w);
|
||||
dl_add(list, w2);
|
||||
}
|
||||
/* printf("got a line\n");*/
|
||||
return list;
|
||||
}
|
||||
|
||||
void free_line(char *line)
|
||||
{
|
||||
char *w;
|
||||
for (w=dl_next(line); w != line; w=dl_next(line)) {
|
||||
dl_del(w);
|
||||
dl_free(w);
|
||||
}
|
||||
dl_free(line);
|
||||
}
|
||||
|
||||
|
||||
struct conf_dev {
|
||||
struct conf_dev *next;
|
||||
char *name;
|
||||
} *cdevlist = NULL;
|
||||
|
||||
|
||||
|
||||
int devline(char *line)
|
||||
{
|
||||
char *w;
|
||||
struct conf_dev *cd;
|
||||
|
||||
for (w=dl_next(line); w != line; w=dl_next(w)) {
|
||||
if (w[0] == '/') {
|
||||
cd = malloc(sizeof(*cd));
|
||||
cd->name = strdup(w);
|
||||
cd->next = cdevlist;
|
||||
cdevlist = cd;
|
||||
} else {
|
||||
fprintf(stderr, Name ": unreconised word on DEVICE line: %s\n",
|
||||
w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mddev_uuid_t uuidlist = NULL;
|
||||
|
||||
void arrayline(char *line)
|
||||
{
|
||||
char *w;
|
||||
char *dev = NULL;
|
||||
__u32 uuid[4];
|
||||
int uidset=0;
|
||||
mddev_uuid_t mu;
|
||||
|
||||
for (w=dl_next(line); w!=line; w=dl_next(w)) {
|
||||
if (w[0] == '/') {
|
||||
if (dev)
|
||||
fprintf(stderr, Name ": only give one device per ARRAY line: %s and %s\n",
|
||||
dev, w);
|
||||
else dev = w;
|
||||
} else if (strncasecmp(w, "uuid=", 5)==0 ) {
|
||||
if (uidset)
|
||||
fprintf(stderr, Name ": only specify uuid once, %s ignored.\n",
|
||||
w);
|
||||
else {
|
||||
if (parse_uuid(w+5, uuid))
|
||||
uidset = 1;
|
||||
else
|
||||
fprintf(stderr, Name ": bad uuid: %s\n", w);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, Name ": unrecognised word on ARRAY line: %s\n",
|
||||
w);
|
||||
}
|
||||
}
|
||||
if (dev == NULL)
|
||||
fprintf(stderr, Name ": ARRAY line with a device\n");
|
||||
else if (uidset == 0)
|
||||
fprintf(stderr, Name ": ARRAY line %s has no uuid\n", dev);
|
||||
else {
|
||||
mu = malloc(sizeof(*mu));
|
||||
mu->devname = strdup(dev);
|
||||
memcpy(mu->uuid, uuid, sizeof(uuid));
|
||||
mu->next = uuidlist;
|
||||
uuidlist = mu;
|
||||
}
|
||||
}
|
||||
|
||||
int loaded = 0;
|
||||
|
||||
void load_conffile(char *conffile)
|
||||
{
|
||||
FILE *f;
|
||||
char *line;
|
||||
|
||||
if (loaded) return;
|
||||
if (conffile == NULL)
|
||||
conffile = DefaultConfFile;
|
||||
|
||||
f = fopen(conffile, "r");
|
||||
if (f ==NULL)
|
||||
return;
|
||||
|
||||
loaded = 1;
|
||||
while ((line=conf_line(f))) {
|
||||
switch(match_keyword(line)) {
|
||||
case 0: /* DEVICE */
|
||||
devline(line);
|
||||
break;
|
||||
case 1:
|
||||
arrayline(line);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, Name ": Unknown keyword %s\n", line);
|
||||
}
|
||||
free_line(line);
|
||||
}
|
||||
|
||||
|
||||
/* printf("got file\n"); */
|
||||
}
|
||||
|
||||
|
||||
mddev_uuid_t conf_get_uuids(char *conffile)
|
||||
{
|
||||
return NULL;
|
||||
load_conffile(conffile);
|
||||
return uuidlist;
|
||||
}
|
||||
|
||||
mddev_dev_t conf_get_devs(char *conffile)
|
||||
{
|
||||
return NULL;
|
||||
glob_t globbuf;
|
||||
struct conf_dev *cd;
|
||||
int flags = 0;
|
||||
static mddev_dev_t dlist = NULL;
|
||||
int i;
|
||||
|
||||
while (dlist) {
|
||||
mddev_dev_t t = dlist;
|
||||
dlist = dlist->next;
|
||||
free(t->devname);
|
||||
free(t);
|
||||
}
|
||||
|
||||
load_conffile(conffile);
|
||||
|
||||
for (cd=cdevlist; cd; cd=cd->next) {
|
||||
glob(cd->name, flags, NULL, &globbuf);
|
||||
flags |= GLOB_APPEND;
|
||||
}
|
||||
|
||||
for (i=0; i<globbuf.gl_pathc; i++) {
|
||||
mddev_dev_t t = malloc(sizeof(*t));
|
||||
t->devname = strdup(globbuf.gl_pathv[i]);
|
||||
t->next = dlist;
|
||||
dlist = t;
|
||||
/* printf("one dev is %s\n", t->devname);*/
|
||||
}
|
||||
globfree(&globbuf);
|
||||
|
||||
|
||||
return dlist;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
/* doubly linked lists */
|
||||
/* This is free software. No strings attached. No copyright claimed */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "dlink.h"
|
||||
|
||||
|
||||
void *dl_head()
|
||||
{
|
||||
void *h;
|
||||
h = dl_alloc(0);
|
||||
dl_next(h) = h;
|
||||
dl_prev(h) = h;
|
||||
return h;
|
||||
}
|
||||
|
||||
void dl_free(void *v)
|
||||
{
|
||||
struct __dl_head *vv = v;
|
||||
free(vv-1);
|
||||
}
|
||||
|
||||
void dl_init(void *v)
|
||||
{
|
||||
dl_next(v) = v;
|
||||
dl_prev(v) = v;
|
||||
}
|
||||
|
||||
void dl_insert(void *head, void *val)
|
||||
{
|
||||
dl_next(val) = dl_next(head);
|
||||
dl_prev(val) = head;
|
||||
dl_next(dl_prev(val)) = val;
|
||||
dl_prev(dl_next(val)) = val;
|
||||
}
|
||||
|
||||
void dl_add(void *head, void *val)
|
||||
{
|
||||
dl_prev(val) = dl_prev(head);
|
||||
dl_next(val) = head;
|
||||
dl_next(dl_prev(val)) = val;
|
||||
dl_prev(dl_next(val)) = val;
|
||||
}
|
||||
|
||||
void dl_del(void *val)
|
||||
{
|
||||
if (dl_prev(val) == 0 || dl_next(val) == 0)
|
||||
return;
|
||||
dl_prev(dl_next(val)) = dl_prev(val);
|
||||
dl_next(dl_prev(val)) = dl_next(val);
|
||||
dl_prev(val) = dl_next(val) = 0;
|
||||
}
|
||||
|
||||
char *dl_strndup(char *s, int l)
|
||||
{
|
||||
char *n;
|
||||
if (s == NULL)
|
||||
return NULL;
|
||||
n = dl_newv(char, l+1);
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
else
|
||||
{
|
||||
strncpy(n, s, l);
|
||||
n[l] = 0;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
char *dl_strdup(char *s)
|
||||
{
|
||||
return dl_strndup(s, (int)strlen(s));
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
/* doubley linked lists */
|
||||
/* This is free software. No strings attached. No copyright claimed */
|
||||
|
||||
struct __dl_head
|
||||
{
|
||||
struct __dl_head * dh_prev;
|
||||
struct __dl_head * dh_next;
|
||||
};
|
||||
|
||||
#define dl_alloc(size) ((void*)(((char*)calloc(1,(size)+sizeof(struct __dl_head)))+sizeof(struct __dl_head)))
|
||||
#define dl_new(t) ((t*)dl_alloc(sizeof(t)))
|
||||
#define dl_newv(t,n) ((t*)dl_alloc(sizeof(t)*n))
|
||||
|
||||
#define dl_next(p) *((void**)&(((struct __dl_head*)(p))[-1].dh_next))
|
||||
#define dl_prev(p) *((void**)&(((struct __dl_head*)(p))[-1].dh_prev))
|
||||
|
||||
void *dl_head();
|
||||
char *dl_strdup(char *);
|
||||
char *dl_strndup(char *, int);
|
||||
void dl_insert(void*, void*);
|
||||
void dl_add(void*, void*);
|
||||
void dl_del(void*);
|
||||
void dl_free(void*);
|
||||
void dl_init(void*);
|
6
mdctl.c
6
mdctl.c
|
@ -347,6 +347,7 @@ int main(int argc, char *argv[])
|
|||
* There must be an mddev unless D or E or (A and scan)
|
||||
* If there is one, we open it.
|
||||
*/
|
||||
|
||||
if (mode !='D' && mode !='E' && ! (mode =='A' && scan)) {
|
||||
if (!mddev) {
|
||||
fprintf(stderr, Name ": an md device must be given in this mode\n");
|
||||
|
@ -366,15 +367,16 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
rv =0;
|
||||
switch(mode) {
|
||||
case '@':/* Management */
|
||||
/* readonly, add/remove, readwrite, runstop */
|
||||
if (readonly>1)
|
||||
if (readonly>0)
|
||||
rv = Manage_ro(mddev, mdfd, readonly);
|
||||
if (!rv && subdevs)
|
||||
rv = Manage_subdevs(mddev, mdfd, subdevs, subdev, devmodes);
|
||||
if (!rv && readonly < 1)
|
||||
if (!rv && readonly < 0)
|
||||
rv = Manage_ro(mddev, mdfd, readonly);
|
||||
if (!rv && runstop)
|
||||
rv = Manage_runstop(mddev, mdfd, runstop);
|
||||
|
|
1
mdctl.h
1
mdctl.h
|
@ -78,6 +78,7 @@ extern char *map_num(mapping_t *map, int num);
|
|||
extern int map_name(mapping_t *map, char *name);
|
||||
extern mapping_t r5layout[], pers[];
|
||||
|
||||
extern char *map_dev(int major, int minor);
|
||||
|
||||
|
||||
extern int Manage_ro(char *devname, int fd, int readonly);
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
Hello there, "this is a" conf file
|
||||
which has several lines
|
||||
# there are comments
|
||||
# losts of comments
|
||||
with comments # really truely
|
||||
Dev lines are needed
|
||||
|
||||
and might have
|
||||
"blanks in them,
|
||||
they really could
|
||||
I think
|
||||
that empty "" strings are confusing
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
DEV /dev/hda* /dev/sd?
|
||||
ARRAY /dev/md0 uuid=1234.5678.abcd.ef09:1234.5678.abcd.ef90
|
95
util.c
95
util.c
|
@ -56,8 +56,10 @@ int parse_uuid(char *str, int uuid[4])
|
|||
continue;
|
||||
else return 0;
|
||||
|
||||
uuid[hit/4] <<= 4;
|
||||
uuid[hit/4] += n;
|
||||
if (hit<32) {
|
||||
uuid[hit/8] <<= 4;
|
||||
uuid[hit/8] += n;
|
||||
}
|
||||
hit++;
|
||||
}
|
||||
if (hit == 32)
|
||||
|
@ -222,6 +224,31 @@ int load_super(int fd, mdp_super_t *super)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int store_super(int fd, mdp_super_t *super)
|
||||
{
|
||||
long size;
|
||||
long long offset;
|
||||
|
||||
if (ioctl(fd, BLKGETSIZE, &size))
|
||||
return 1;
|
||||
|
||||
if (size < MD_RESERVED_SECTORS*2)
|
||||
return 2;
|
||||
|
||||
offset = MD_NEW_SIZE_SECTORS(size);
|
||||
|
||||
offset *= 512;
|
||||
|
||||
if (lseek64(fd, offset, 0)< 0LL)
|
||||
return 3;
|
||||
|
||||
if (write(fd, super, sizeof(*super)) != sizeof(*super))
|
||||
return 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int check_ext2(int fd, char *name)
|
||||
{
|
||||
|
@ -333,3 +360,67 @@ int map_name(mapping_t *map, char *name)
|
|||
}
|
||||
return -10;
|
||||
}
|
||||
|
||||
/*
|
||||
* convert a major/minor pair for a block device into a name in /dev, if possible.
|
||||
* On the first call, walk /dev collecting name.
|
||||
* Put them in a simple linked listfor now.
|
||||
*/
|
||||
struct devmap {
|
||||
int major, minor;
|
||||
char *name;
|
||||
struct devmap *next;
|
||||
} *devlist = NULL;
|
||||
int devlist_ready = 0;
|
||||
|
||||
#define __USE_XOPEN_EXTENDED
|
||||
#include <ftw.h>
|
||||
|
||||
|
||||
int add_dev(const char *name, const struct stat *stb, int flag, struct FTW *s)
|
||||
{
|
||||
if ((stb->st_mode&S_IFMT)== S_IFBLK) {
|
||||
char *n = strdup(name);
|
||||
struct devmap *dm = malloc(sizeof(*dm));
|
||||
if (dm) {
|
||||
dm->major = MAJOR(stb->st_rdev);
|
||||
dm->minor = MINOR(stb->st_rdev);
|
||||
dm->name = n;
|
||||
dm->next = devlist;
|
||||
devlist = dm;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *map_dev(int major, int minor)
|
||||
{
|
||||
struct devmap *p;
|
||||
if (!devlist_ready) {
|
||||
nftw("/dev", add_dev, 10, FTW_PHYS);
|
||||
devlist_ready=1;
|
||||
}
|
||||
|
||||
for (p=devlist; p; p=p->next)
|
||||
if (p->major == major &&
|
||||
p->minor == minor)
|
||||
return p->name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int calc_sb_csum(mdp_super_t *super)
|
||||
{
|
||||
unsigned int oldcsum = super->sb_csum;
|
||||
unsigned long long newcsum = 0; /* FIXME why does this make it work?? */
|
||||
unsigned long csum;
|
||||
int i;
|
||||
unsigned int *superc = (int*) super;
|
||||
super->sb_csum = 0;
|
||||
|
||||
for(i=0; i<MD_SB_BYTES/4; i++)
|
||||
newcsum+= superc[i];
|
||||
csum = (newcsum& 0xffffffff) + (newcsum>>32);
|
||||
super->sb_csum = oldcsum;
|
||||
return csum;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue