mdctl-v0.4 mdctl-v0.4
authorNeil Brown <neilb@suse.de>
Thu, 26 Jul 2001 07:00:09 +0000 (07:00 +0000)
committerNeil Brown <neilb@suse.de>
Thu, 26 Jul 2001 07:00:09 +0000 (07:00 +0000)
17 files changed:
Assemble.c
Build.c
Create.c
Detail.c
Examine.c
Makefile
Manage.c
ReadMe.c
TODO
config.c
dlink.c [new file with mode: 0644]
dlink.h [new file with mode: 0644]
mdctl.c
mdctl.h
testconfig [new file with mode: 0644]
testconfig2 [new file with mode: 0644]
util.c

index fdaff09..fe0b99a 100644 (file)
@@ -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 (file)
--- a/Build.c
+++ b/Build.c
 
 #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;
+               
 }
+
index ac776c2..37a027d 100644 (file)
--- 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));
index 3349c3a..cb6dd66 100644 (file)
--- 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;
 }
index f2498ec..4ed4f6b 100644 (file)
--- 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;
index 0168923..a7a7c48 100644 (file)
--- 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)
index 7357339..eda275a 100644 (file)
--- a/Manage.c
+++ b/Manage.c
 #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));
index 9bee800..7252c7c 100644 (file)
--- 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 (file)
--- 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.
index b4d235c..4437de7 100644 (file)
--- a/config.c
+++ b/config.c
@@ -28,7 +28,8 @@
  */
 
 #include       "mdctl.h"
-
+#include       "dlink.h"
+#include       <glob.h>
 /*
  * Read the config file
  *
  * 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 (file)
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 (file)
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 (file)
--- 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 (file)
--- 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 (file)
index 0000000..0ed8b2c
--- /dev/null
@@ -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 (file)
index 0000000..6cd7a78
--- /dev/null
@@ -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 (file)
--- 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;
+}