summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeil Brown <neilb@suse.de>2001-07-26 07:00:09 +0000
committerNeil Brown <neilb@suse.de>2001-07-26 07:00:09 +0000
commit82b27616de634964db1a71bd5d9813db71e391a1 (patch)
tree310a2ed60dbb0903c73ba261914da269e9ec9072
parent682c705194a869b882cd710d5f996142912db390 (diff)
downloadmdadm-82b27616de634964db1a71bd5d9813db71e391a1.tar.gz
mdadm-82b27616de634964db1a71bd5d9813db71e391a1.tar.xz
mdadm-82b27616de634964db1a71bd5d9813db71e391a1.zip
mdctl-v0.4mdctl-v0.4
-rw-r--r--Assemble.c97
-rw-r--r--Build.c133
-rw-r--r--Create.c53
-rw-r--r--Detail.c38
-rw-r--r--Examine.c25
-rw-r--r--Makefile4
-rw-r--r--Manage.c19
-rw-r--r--ReadMe.c2
-rw-r--r--TODO16
-rw-r--r--config.c289
-rw-r--r--dlink.c76
-rw-r--r--dlink.h25
-rw-r--r--mdctl.c6
-rw-r--r--mdctl.h1
-rw-r--r--testconfig12
-rw-r--r--testconfig23
-rw-r--r--util.c95
17 files changed, 855 insertions, 39 deletions
diff --git a/Assemble.c b/Assemble.c
index fdaff09..fe0b99a 100644
--- a/Assemble.c
+++ b/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));
+ }
+
}
}
diff --git a/Build.c b/Build.c
index bcad0a6..40b631f 100644
--- a/Build.c
+++ b/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;
+
}
+
diff --git a/Create.c b/Create.c
index ac776c2..37a027d 100644
--- a/Create.c
+++ b/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, &param)) {
fprintf(stderr, Name ": RUN_ARRAY failed: %s\n",
strerror(errno));
diff --git a/Detail.c b/Detail.c
index 3349c3a..cb6dd66 100644
--- a/Detail.c
+++ b/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;
}
diff --git a/Examine.c b/Examine.c
index f2498ec..4ed4f6b 100644
--- a/Examine.c
+++ b/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;
diff --git a/Makefile b/Makefile
index 0168923..a7a7c48 100644
--- a/Makefile
+++ b/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)
diff --git a/Manage.c b/Manage.c
index 7357339..eda275a 100644
--- a/Manage.c
+++ b/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, &param)) {
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));
diff --git a/ReadMe.c b/ReadMe.c
index 9bee800..7252c7c 100644
--- a/ReadMe.c
+++ b/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
*
diff --git a/TODO b/TODO
index f093277..c4d1309 100644
--- a/TODO
+++ b/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.
diff --git a/config.c b/config.c
index b4d235c..4437de7 100644
--- a/config.c
+++ b/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;
}
diff --git a/dlink.c b/dlink.c
new file mode 100644
index 0000000..de42632
--- /dev/null
+++ b/dlink.c
@@ -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));
+}
diff --git a/dlink.h b/dlink.h
new file mode 100644
index 0000000..aa178e6
--- /dev/null
+++ b/dlink.h
@@ -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*);
diff --git a/mdctl.c b/mdctl.c
index 5a16646..70e8129 100644
--- a/mdctl.c
+++ b/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);
diff --git a/mdctl.h b/mdctl.h
index 0e3b917..b038555 100644
--- a/mdctl.h
+++ b/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);
diff --git a/testconfig b/testconfig
new file mode 100644
index 0000000..0ed8b2c
--- /dev/null
+++ b/testconfig
@@ -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 \ No newline at end of file
diff --git a/testconfig2 b/testconfig2
new file mode 100644
index 0000000..6cd7a78
--- /dev/null
+++ b/testconfig2
@@ -0,0 +1,3 @@
+
+DEV /dev/hda* /dev/sd?
+ARRAY /dev/md0 uuid=1234.5678.abcd.ef09:1234.5678.abcd.ef90
diff --git a/util.c b/util.c
index bb370dd..ea30b31 100644
--- a/util.c
+++ b/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;
+}