From 64c4757e27fe7d688685e9a86ff87a3331a88979 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 8 Jun 2001 02:36:23 +0000 Subject: [PATCH] mdctl-v0.2 --- Assemble.c | 366 +++++++++++++++++++++++++++++++++++++++++++++++++ Build.c | 36 +++++ Create.c | 37 +++++ Detail.c | 120 ++++++++++++++++ Examine.c | 144 +++++++++++++++++++ Makefile | 44 ++++++ Manage.c | 43 ++++++ ReadMe.c | 246 +++++++++++++++++++++++++++++++++ TODO | 7 + config.c | 54 ++++++++ makedist | 20 +++ md_p.h | 172 +++++++++++++++++++++++ md_u.h | 116 ++++++++++++++++ mdctl.c | 395 +++++++++++++++++++++++++++++++++++++++++++++++++++++ mdctl.h | 110 +++++++++++++++ util.c | 224 ++++++++++++++++++++++++++++++ 16 files changed, 2134 insertions(+) create mode 100644 Assemble.c create mode 100644 Build.c create mode 100644 Create.c create mode 100644 Detail.c create mode 100644 Examine.c create mode 100644 Makefile create mode 100644 Manage.c create mode 100644 ReadMe.c create mode 100644 TODO create mode 100644 config.c create mode 100755 makedist create mode 100644 md_p.h create mode 100644 md_u.h create mode 100644 mdctl.c create mode 100644 mdctl.h create mode 100644 util.c diff --git a/Assemble.c b/Assemble.c new file mode 100644 index 00000000..504bde78 --- /dev/null +++ b/Assemble.c @@ -0,0 +1,366 @@ +/* + * mdctl - manage Linux "md" devices aka RAID arrays. + * + * Copyright (C) 2001 Neil Brown + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#include "mdctl.h" +#include "md_p.h" +#include "md_u.h" + +int Assemble(char *mddev, int mdfd, + int uuid[4], int uuidset, + char *conffile, int scan, + int subdevs, char **subdev, + int readonly, int runstop, + int verbose, int force) +{ + /* + * The task of Assemble is to submit a + * SET_ARRAY_INFO ioctl with no arg - to prepare + * the array - and then submit a number of + * ADD_NEW_DISK ioctls to add disks into + * the array. Finally RUN_ARRAY might + * be submitted to start the array. + * + * Much of the work of Assemble is in finding and/or + * checking the disks to make sure they look right. + * + * If mddev is not set, then scan must be and we + * read through the config file for dev+uuid mapping + * We recurse, setting mddev, for each device that + * - isn't running + * - has a valid uuid (or any uuid if !uuidset + * + * If mddev is set, we try to determine state of md. + * check version - must be at least 0.90.0 + * check kernel version. must be at least 2.4. + * If not, we can possibly fall back on START_ARRAY + * Try to GET_ARRAY_INFO. + * If possible, give up + * If not, try to STOP_ARRAY just to make sure + * + * If !uuidset and scan, look in conf-file for uuid + * If not found, give up + * If !subdevs and scan and uuidset, get list of devs from conf-file + * + * For each device: + * Check superblock - discard if bad + * Check uuid (set if we don't have one) - discard if no match + * Check superblock similarity if we have a superbloc - discard if different + * Record events, devicenum, utime + * This should give us a list of devices for the array + * We should collect the most recent event and utime numbers + * + * Count disks with recent enough event count + * While force && !enough disks + * Choose newest rejected disks, update event count + * mark clean and rewrite superblock + * If recent kernel: + * SET_ARRAY_INFO + * foreach device with recent events : ADD_NEW_DISK + * if runstop == 1 || "enough" disks and runstop==0 -> RUN_ARRAY + * If old kernel: + * Check the device numbers in superblock are right + * update superblock if any changes + * START_ARRAY + * + */ + int old_linux = 0; + int vers; + mdu_array_info_t array; + mddev_dev_t devlist = NULL; + mdp_super_t first_super, super; + struct { + char *devname; + int major, minor; + long long events; + time_t utime; + int uptodate; + } devices[MD_SB_DISKS]; + int best[MD_SB_DISKS]; /* indexed by raid_disk */ + int devcnt = 0, okcnt; + int i; + int most_recent = 0; + + if (!mddev && !scan) { + fputs("mdctl: internal error - Assemble called with no devie or scan\n", stderr); + return 1; + } + if (!mddev) { + mddev_uuid_t device_list; + int found = 0; + device_list = conf_get_uuids(conffile); + if (!device_list) { + fprintf(stderr, "mdctl: No devices found in config file\n"); + return 1; + } + while (device_list) { + if (!uuidset || same_uuid(device_list->uuid,uuid)) { + mdfd = open(device_list->devname, O_RDONLY, 0); + if (mdfd < 0) { + fprintf(stderr, + "mdctl: error opening %s: %s\n", + device_list->devname, + strerror(errno)); + continue; + } + if (Assemble(device_list->devname, mdfd, + device_list->uuid, 1, + conffile, 1, + subdevs, subdev, + readonly, runstop, verbose, force)==0) + found++; + close(mdfd); + } + device_list = device_list->next; + } + if (found) + return 0; + fprintf(stderr,"mdctl: Did not successful Assemble any devices\n"); + return 1; + } + + /* + * Ok, we have an mddev, check it out + */ + vers = md_get_version(mdfd); + if (vers <= 0) { + fprintf(stderr, "mdctl: %s appears not to be an md device.\n"); + return 1; + } + if (vers < (90<<8)) { + fprintf(stderr, "mdctl: Assemble requires driver version 0.90.0 or later.\n" + " Upgrade your kernel or try --Build\n"); + return 1; + } + if (get_linux_version() < 0x020400) + old_linux = 1; + + if (ioctl(mdfd, GET_ARRAY_INFO, &array)>=0) { + fprintf(stderr, "mdctl: device %s already active - cannot assemble it\n", + mddev); + return 1; + } + ioctl(mdfd, STOP_ARRAY, NULL); /* just incase it was started but has no content */ + + /* + * We have a valid mddev, check out uuid + */ + if (!uuidset && scan) { + /* device must be listed with uuid in conf file */ + mddev_uuid_t device_list; + device_list = conf_get_uuids(conffile); + while (device_list && + strcmp(device_list->devname, mddev) != 0) + device_list = device_list->next; + + if (!device_list) { + fprintf(stderr, "mdctl: --scan set and no uuid found for %s in config file.\n", + mddev); + return 1; + } + /* the uuid is safe until next call to conf_get_uuids */ + uuid = device_list->uuid; + uuidset = 1; + } + + /* Now to start looking at devices. + * If no devices were given, but a uuid is available and + * --scan was set, then we should scan all devices listed in the + * config file + * + */ + if (subdevs==0 && scan && uuidset) + devlist = conf_get_devs(conffile); + + if (subdevs == 0 && devlist == NULL) { + fprintf(stderr, "mdctl: no devices given for %s\n", mddev); + return 1; + } + /* now for each device */ + first_super.md_magic = 0; + for (i=0; idevname; + devlist = devlist->next; + inargv=0; + } + + dfd = open(devname, O_RDONLY, 0); + if (dfd < 0) { + if (inargv || verbose) + fprintf(stderr, "mdctl: cannot open device %s: %s\n", + devname, strerror(errno)); + continue; + } + if (fstat(dfd, &stb)< 0) { + /* Impossible! */ + fprintf(stderr, "mdctl: fstat failed for %s: %s\n", + devname, strerror(errno)); + close(dfd); + continue; + } + if ((stb.st_mode & S_IFMT) != S_IFBLK) { + fprintf(stderr, "mdctl: %d is not a block device.\n", + devname); + close(dfd); + continue; + } + if (load_super(dfd, &super)) { + if (inargv || verbose) + fprintf( stderr, "mdctl: no RAID superblock on %s\n", + devname); + close(dfd); + continue; + } + close(dfd); + if (compare_super(&first_super, &super)) { + if (inargv || verbose) + fprintf(stderr, "mdctl: superblock on %s doesn't match\n", + devname); + continue; + } + if (uuidset) { + uuid_from_super(this_uuid, &first_super); + if (!same_uuid(this_uuid, uuid)) { + if (inargv || verbose) + fprintf(stderr, "mdctl: %s has wrong uuid.\n", + devname); + continue; + } + } else { + uuid_from_super(uuid, &first_super); + uuidset = 1; + } + + /* Ok, this one is at least worth considering */ + if (devcnt >= MD_SB_DISKS) { + fprintf(stderr, "mdctl: ouch - too many devices appear to be in this array. Ignoring %s\n", + devname); + continue; + } + devices[devcnt].devname = devname; + devices[devcnt].major = MAJOR(stb.st_rdev); + devices[devcnt].minor = MINOR(stb.st_rdev); + devices[devcnt].events = md_event(&super); + devices[devcnt].utime = super.utime; + devices[devcnt].uptodate = 0; + if (most_recent < devcnt) { + if (devices[devcnt].events + > devices[most_recent].events) + most_recent = devcnt; + } + i = super.this_disk.raid_disk; + if (best[i] == -1 + || devices[best[i]].events < devices[devcnt].events) { + best[i] = devcnt; + } + devcnt++; + } + + if (devcnt == 0) { + fprintf(stderr, "mdctl: no devices found for %s\n", + mddev); + return 1; + } + /* now we have some devices that might be suitable. + * I wonder how many + */ + okcnt = 0; + for (i=0; i< first_super.raid_disks;i++) { + int j = best[i]; + if (j < 0) continue; + if (devices[j].events+1 >= + devices[most_recent].events) { + devices[j].uptodate = 1; + okcnt++; + } + } + while (force && !enough(first_super.level, first_super.raid_disks, okcnt)) { + /* Choose the newest best drive which is + * not up-to-date, update the superblock + * and add it. + */ + fprintf(stderr,"NoImplementedYet\n"); + /* FIXME */ + exit(2); + } + /* Almost ready to actually *do* something */ + if (!old_linux) { + if (ioctl(mdfd, SET_ARRAY_INFO, NULL) != 0) { + fprintf(stderr, "mdctl: SET_ARRAY_INFO failed for %s: %s\n", + mddev, strerror(errno)); + return 1; + } + /* First, add the raid disks */ + for (i=0; i + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#include "mdctl.h" + +int Build(char *mddev, int mdfd, int chunk, int level, + int raiddisks, + int subdevs, char *subdev[]) +{ +} diff --git a/Create.c b/Create.c new file mode 100644 index 00000000..dda54fda --- /dev/null +++ b/Create.c @@ -0,0 +1,37 @@ +/* + * mdctl - manage Linux "md" devices aka RAID arrays. + * + * Copyright (C) 2001 Neil Brown + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#include "mdctl.h" + +int Create(char *mddev, int mdfd, + int chunk, int level, int layout, int raiddisks, int sparedisks, + int subdevs, char *subdev[], + int runstop) +{ +} diff --git a/Detail.c b/Detail.c new file mode 100644 index 00000000..9f81e044 --- /dev/null +++ b/Detail.c @@ -0,0 +1,120 @@ +/* + * mdctl - manage Linux "md" devices aka RAID arrays. + * + * Copyright (C) 2001 Neil Brown + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#include "mdctl.h" +#include "md_p.h" +#include "md_u.h" + +int Detail(char *dev) +{ + /* + * Print out details for an md array by using + * GET_ARRAY_INFO and GET_DISK_INFO ioctl calls + */ + + int fd = open(dev, O_RDONLY, 0); + int vers; + mdu_array_info_t array; + int d; + time_t atime; + + if (fd < 0) { + fprintf(stderr, "mdctl: cannot open %s: %s\n", + dev, strerror(errno)); + return 1; + } + vers = md_get_version(fd); + if (vers < 0) { + fprintf(stderr, "mdctl: %s does not appear to be an md device\n", + dev); + close(fd); + return 1; + } + if (vers < (90<<8)) { + fprintf(stderr, "mdctl: cannot get detail for md device %s: driver version too old.\n", + dev); + close(fd); + return 1; + } + if (ioctl(fd, GET_ARRAY_INFO, &array)<0) { + if (errno == ENODEV) + fprintf(stderr, "mdctl: md device %s does not appear to be active.\n", + dev); + else + fprintf(stderr, "mdctl: cannot get array detail for %s: %s\n", + dev, strerror(errno)); + close(fd); + return 1; + } + /* Ok, we have some info to print... */ + printf("%s:\n", dev); + printf(" Version : %02d.%02d.%02d\n", + array.major_version, array.minor_version, array.patch_version); + atime = array.ctime; + printf(" Creation Time : %.24s\n", ctime(&atime)); + printf(" Raid Level : %d\n", array.level); + printf(" Size : %d\n", array.size); + printf(" Raid Disks : %d\n", array.raid_disks); + printf(" Total Disks : %d\n", array.nr_disks); + printf("Preferred Minor : %d\n", array.md_minor); + printf(" Persistance : Superblock is %spersistant\n", + array.not_persistent?"not ":""); + printf("\n"); + atime = array.utime; + printf(" Update Time : %.24s\n", ctime(&atime)); + printf(" State : %s, %serrors\n", + (array.state&(1< + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#include "mdctl.h" + +#include "md_u.h" +#include "md_p.h" +int Examine(char *dev) +{ + + /* Read the raid superblock from a device and + * display important content. + * + * If cannot be found, print reason: too small, bad magic + * + * Print: + * version, ctime, level, size, raid+spare+ + * prefered minor + * uuid + * + * utime, state etc + * + */ + int fd = open(dev, O_RDONLY, 0); + time_t atime; + mdp_super_t super; + int d; + int rv; + + if (fd < 0) { + fprintf(stderr,"mdctl: cannot open %s: %s\n", + dev, strerror(errno)); + return 1; + } + + rv = load_super(fd, &super); + close(fd); + switch(rv) { + case 1: + fprintf(stderr, "mdctl: cannot find device size for %s: %s\n", + dev, strerror(errno)); + return 1; + case 2: +/* fprintf(stderr, "mdctl: %s is too small for md: size is %ld sectors\n", + dev, size); +*/ + fprintf(stderr, "mdctl: %s is too small for md\n", + dev); + return 1; + case 3: + fprintf(stderr, "mdctl: Cannot seek to superblock on %s: %s\n", + dev, strerror(errno)); + return 1; + case 4: + fprintf(stderr, "mdctl: Cannot read superblock on %s\n", + dev); + return 1; + case 5: + fprintf(stderr, "mdctl: No super block found on %s (Expected magic %08x, got %08x)\n", + dev, MD_SB_MAGIC, super.md_magic); + return 1; + case 6: + fprintf(stderr, "mdctl: Cannot interpret superblock on %s - version is %d\n", + dev, super.major_version); + return 1; + } + + /* Ok, its good enough to try, though the checksum could be wrong */ + printf("%s:\n",dev); + printf(" Magic : %08x\n", super.md_magic); + printf(" Version : %02d.%02d.%02d\n", super.major_version, super.minor_version, + super.patch_version); + 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); + + atime = super.ctime; + printf(" Creation Time : %.24s\n", ctime(&atime)); + printf(" Raid Level : %d\n", super.level); + printf(" Size : %d\n", super.size); + printf(" Raid Disks : %d\n", super.raid_disks); + printf(" Total Disks : %d\n", super.nr_disks); + printf("Preferred Minor : %d\n", super.md_minor); + printf("\n"); + atime = super.utime; + printf(" Update Time : %.24s\n", ctime(&atime)); + printf(" State : %s, %serrors\n", + (super.state&(1<=0) dp = &super.disks[d]; + else dp = &super.this_disk; + sprintf(nb, "%4d", d); + printf("%4s %5d %5d %5d %5d ", d < 0 ? "this" : nb, + dp->number, dp->major, dp->minor, dp->raid_disk); + if (dp->state & (1<state & (1<state & (1<state & (1< +# +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Author: Neil Brown +# Email: +# Paper: Neil Brown +# School of Computer Science and Engineering +# The University of New South Wales +# Sydney, 2052 +# Australia +# + +CFLAGS = -Wall,error + +OBJS = mdctl.o config.o ReadMe.o util.o Manage.o Assemble.o Build.o Create.o Detail.o Examine.o +all : mdctl + +mdctl : $(OBJS) + $(CC) -o mdctl $^ + +$(OBJS) : mdctl.h + +clean : + rm -f mdctl $(OBJS) + +dist : clean + ./makedist diff --git a/Manage.c b/Manage.c new file mode 100644 index 00000000..302a425b --- /dev/null +++ b/Manage.c @@ -0,0 +1,43 @@ +/* + * mdctl - manage Linux "md" devices aka RAID arrays. + * + * Copyright (C) 2001 Neil Brown + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#include "mdctl.h" + +int Manage_ro(char *devname, int fd, int readonly) +{ +} + +int Manage_runstop(char *devname, int fd, int runstop) +{ +} + +int Manage_subdevs(char *devname, int fd, + int devcnt, char *devnames[], int devmodes[]) +{ +} diff --git a/ReadMe.c b/ReadMe.c new file mode 100644 index 00000000..e07cc42d --- /dev/null +++ b/ReadMe.c @@ -0,0 +1,246 @@ +/* + * mdctl - manage Linux "md" devices aka RAID arrays. + * + * Copyright (C) 2001 Neil Brown + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#include "mdctl.h" + +char Version[] = "mdctl - v0.2 - 06 June 2001\n"; +/* + * File: ReadMe.c + * + * This file contains general comments about the implementation + * and the various usage messages that can be displayed by mdctl + * + * mdctl is a single program that can be used to control Linux md devices. + * It is intended to provide all the functionality of the mdtools and + * raidtools but with a very different interface. + * mdctl can perform all functions without a configuration file. + * There is the option of using a configuration file, but not in the same + * way that raidtools uses one + * raidtools uses a configuration file to describe how to create a RAID + * array, and also uses this file partially to start a previously + * created RAID array. Further, raidtools requires the configuration + * file for such things as stopping a raid array which needs to know + * nothing about the array. + * + * The configuration file that can be used by mdctl lists two + * different things: + * 1/ a mapping from uuid to md device to identify which arrays are + * expect and what names (numbers) they should be given + * 2/ a list of devices that should be scanned for md sub-devices + * + * + */ + +/* + * mdctl has 4 major modes of operation: + * 1/ Create + * This mode is used to create a new array with a superbock + * It can progress in several step create-add-add-run + * or it can all happen with one command + * 2/ Assemble + * This mode is used to assemble the parts of a previously created + * array into an active array. Components can be explicitly given + * or can be searched for. mdctl (optionally) check that the components + * do form a bonafide array, and can, on request, fiddle superblock + * version numbers so as to assemble a faulty array. + * 3/ Build + * This is for building legacy arrays without superblocks + * 4/ Manage + * This is for odd bits an pieces like hotadd, hotremove, setfaulty, + * stop, readonly,readwrite + * If an array is only partially setup by the Create/Assemble/Build + * command, subsequent Manage commands can finish the job. + */ + +char short_options[]="-ABCDEhVvc:l:p:n:x:u:c:sarfRSow"; +struct option long_options[] = { + {"manage", 0, 0, '@'}, + {"assemble", 0, 0, 'A'}, + {"build", 0, 0, 'B'}, + {"create", 0, 0, 'C'}, + {"detail", 0, 0, 'D'}, + {"examine", 0, 0, 'E'}, + /* after those will normally come the name of the md device */ + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'V'}, + {"verbose", 0, 0, 'v'}, + + /* For create or build: */ + {"chunk", 1, 0, 'c'}, + {"rounding", 1, 0, 'c'}, /* for linear, chunk is really a rounding number */ + {"level", 1, 0, 'l'}, /* 0,1,4,5,linear */ + {"parity", 1, 0, 'p'}, /* {left,right}-{a,}symetric */ + {"layout", 1, 0, 'p'}, + {"raid-disks",1, 0, 'n'}, + {"spare-disks",1,0, 'x'}, + + /* For assemble */ + {"uuid", 1, 0, 'u'}, + {"config", 1, 0, 'c'}, + {"scan", 0, 0, 's'}, + {"force", 0, 0, 'f'}, + /* Management */ + {"add", 0, 0, 'a'}, + {"remove", 0, 0, 'r'}, + {"fail", 0, 0, 'f'}, + {"set-faulty",0, 0, 'f'}, + {"run", 0, 0, 'R'}, + {"stop", 0, 0, 'S'}, + {"readonly", 0, 0, 'o'}, + {"readwrite", 0, 0, 'w'}, + + {0, 0, 0, 0} +}; + +char Usage[] = +"Usage: mdctl --help\n" +" for help\n" +; + +char Help[] = +"Usage: mdctl --create device options...\n" +" mdctl --assemble device options...\n" +" mdctl --build device options...\n" +" mdctl --detail device\n" +" mdctl --examine device\n" +" mdctl device options...\n" +" mdctl is used for controlling Linux md devices (aka RAID arrays)\n" +" For detail help on major modes use, e.g.\n" +" mdctl --assemble --help\n" +"\n" +"Any parameter that does not start with '-' is treated as a device name\n" +"The first such name is normally the name of an md device. Subsequent\n" +"names are names of component devices." +"\n" +"Available options are:\n" +" --create -C : Create a new array\n" +" --assemble -A : Assemble an existing array\n" +" --build -B : Build a legacy array without superblock\n" +" --detail -D : Print detail of a given md array\n" +" --examine -E : Print content of md superblock on device\n" +" --help -h : This help message or, after above option,\n" +" mode specific help message\n" +" --version -V : Print version information for mdctl\n" +" --verbose -v : Be more verbose about what is happening\n" +"\n" +" For create or build:\n" +" --chunk= -c : chunk size of kibibytes\n" +" --rounding= : rounding factor for linear array (==chunck size)\n" +" --level= -l : raid level: 0,1,4,5,linear. 0 or linear for build\n" +" --paritiy= -p : raid5 parity algorith: {left,right}-{,a}symmetric\n" +" --layout= : same as --parity\n" +" --raid-disks= -n : number of active devices in array\n" +" --spare-disks= -x : number of spares (eXtras) to allow space for\n" +"\n" +" For assemble:\n" +" --uuid= -u : uuid of array to assemble. Devices which don't\n" +" have this uuid are excluded\n" +" --config= -c : config file\n" +" --scan -s : scan config file for missing information\n" +" --force -f : Assemble the array even if some superblocks appear out-of-date\n" +"\n" +" General management:\n" +" --add -a : add, or hotadd subsequent devices\n" +" --remove -r : remove subsequent devices\n" +" --fail -f : mark subsequent devices a faulty\n" +" --set-faulty : same as --fail\n" +" --run -R : start a partially built array\n" +" --stop -S : deactive array, releasing all resources\n" +" --readonly -o : mark array as readonly\n" +" --readwrite -w : mark array as readwrite\n" +; + + +char Help_create[] = +"Usage: mdctl --create device -chunk=X --level=Y --raid-disks=Z devices\n" +"\n" +" This usage will initialise a new md array and possibly associate some\n" +" devices with it. If enough devices are given to complete the array,\n" +" the array will be activated. Otherwise it will be left inactive\n" +" to be competed and activated by subsequent management commands.\n" +"\n" +" As devices are added, they are checked to see if they contain\n" +" raid superblock or filesystems. They are also check to see if\n" +" the variance in device size exceeds 1%.\n" +" If any discrepancy is found, the array will not automatically\n" +" be run, though the presence of a '--run' can override this\n" +" caution.\n" +"\n" +" The General management options that are valid with --create are:\n" +" --run : insist of running the array even if not all devices\n" +" are present or some look odd.\n" +" --readonly: start the array readonly - not supported yet.\n" +"\n" +; + +char Help_build[] = +"Usage: mdctl --build device -chunk=X --level=Y --raid-disks=Z devices\n" +"\n" +" This usage is similar to --create. The difference is that it creates\n" +" a legacy array with a superblock. With these arrays there is no\n" +" different between initially creating the array and subsequently\n" +" assembling the array, except that hopefully there is useful data\n" +" there in the second case.\n" +"\n" +" The level may only be 0 or linear.\n" +" All devices must be listed and the array will be started once complete.\n" +; + +char Help_assemble[] = +"Usage: mdctl --assemble device options...\n" +" mdctl --assemble --scan options...\n" +"\n" +"This usage assembles one or more raid arrays from pre-existing\n" +"components.\n" +"For each array, mdctl needs to know the md device, the uuid, and\n" +"a number of sub devices. These can be found in a number of ways.\n" +"\n" +"The md device is either given before --scan or is found from the\n" +"config file. In the latter case, multiple md devices can be started\n" +"with a single mdctl command.\n" +"\n" +"The uuid can be given with the --uuid option, or can be found in\n" +"in the config file, or will be taken from the super block on the first\n" +"subdevice listed on the command line or in a subsequent --add command.\n" +"\n" +"Devices can be given on the --assemble command line, on subsequent\n" +"'mdctl --add' command lines, or from the config file. Only devices\n" +"which have an md superblock which contains the right uuid will be\n" +"considered for any device.\n" +"\n" +"The config file is only used if explicitly named with --config or\n" +"requested with --scan. In the later case, '/etc/md.conf' is used.\n" +"\n" +"If --scan is not given, then the config file will only be used\n" +"to find uuids for md arrays.\n" +"\n" +"The format of the config file is:\n" +" not yet documented\n" +"\n" +; diff --git a/TODO b/TODO new file mode 100644 index 00000000..62dab1c9 --- /dev/null +++ b/TODO @@ -0,0 +1,7 @@ + +- check superblock checksum in examine +- report "chunk" or "rounding" depending on raid level +- report "linear" instead of "-1" for raid level +- decode ayout depending on raid level +- get Assemble to upgrade devices if force flag. +- --verbose and --force flags. \ No newline at end of file diff --git a/config.c b/config.c new file mode 100644 index 00000000..b4d235cc --- /dev/null +++ b/config.c @@ -0,0 +1,54 @@ +/* + * mdctl - manage Linux "md" devices aka RAID arrays. + * + * Copyright (C) 2001 Neil Brown + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#include "mdctl.h" + +/* + * Read the config file + * + * conf_get_uuids gets a list of devicename+uuid pairs + * conf_get_devs gets device names after expanding wildcards + * + * Each keeps the returned list and frees it when asked to make + * a new list. + * + */ + +char DefaultConfFile[] = "/etc/mdctl.conf"; + +mddev_uuid_t conf_get_uuids(char *conffile) +{ + return NULL; +} + +mddev_dev_t conf_get_devs(char *conffile) +{ + return NULL; +} + diff --git a/makedist b/makedist new file mode 100755 index 00000000..5b6db54d --- /dev/null +++ b/makedist @@ -0,0 +1,20 @@ +#!/bin/sh + +target=${1-~/public_html/source/mdctl} +if [ -d $target ] +then : +else echo $target is not a directory + exit 2 +fi +set `grep '^char Version' ReadMe.c ` +echo version = $6 +base=mdctl-$6.tgz +if [ -f $target/$base ] +then + echo $target/$base exists. + exit 1 +fi +trap "rm $target/$base; exit" 1 2 3 +( cd .. ; tar czvf - mdctl ) > $target/$base +chmod a+r $target/$base +ls -l $target/$base \ No newline at end of file diff --git a/md_p.h b/md_p.h new file mode 100644 index 00000000..d6bb37a8 --- /dev/null +++ b/md_p.h @@ -0,0 +1,172 @@ +/* + md_p.h : physical layout of Linux RAID devices + Copyright (C) 1996-98 Ingo Molnar, Gadi Oxman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + You should have received a copy of the GNU General Public License + (for example /usr/src/linux/COPYING); if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _MD_P_H +#define _MD_P_H + +/* + * RAID superblock. + * + * The RAID superblock maintains some statistics on each RAID configuration. + * Each real device in the RAID set contains it near the end of the device. + * Some of the ideas are copied from the ext2fs implementation. + * + * We currently use 4096 bytes as follows: + * + * word offset function + * + * 0 - 31 Constant generic RAID device information. + * 32 - 63 Generic state information. + * 64 - 127 Personality specific information. + * 128 - 511 12 32-words descriptors of the disks in the raid set. + * 512 - 911 Reserved. + * 912 - 1023 Disk specific descriptor. + */ + +/* + * If x is the real device size in bytes, we return an apparent size of: + * + * y = (x & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES + * + * and place the 4kB superblock at offset y. + */ +#define MD_RESERVED_BYTES (64 * 1024) +#define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512) +#define MD_RESERVED_BLOCKS (MD_RESERVED_BYTES / BLOCK_SIZE) + +#define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS) +#define MD_NEW_SIZE_BLOCKS(x) ((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS) + +#define MD_SB_BYTES 4096 +#define MD_SB_WORDS (MD_SB_BYTES / 4) +#define MD_SB_BLOCKS (MD_SB_BYTES / BLOCK_SIZE) +#define MD_SB_SECTORS (MD_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define MD_SB_GENERIC_OFFSET 0 +#define MD_SB_PERSONALITY_OFFSET 64 +#define MD_SB_DISKS_OFFSET 128 +#define MD_SB_DESCRIPTOR_OFFSET 992 + +#define MD_SB_GENERIC_CONSTANT_WORDS 32 +#define MD_SB_GENERIC_STATE_WORDS 32 +#define MD_SB_GENERIC_WORDS (MD_SB_GENERIC_CONSTANT_WORDS + MD_SB_GENERIC_STATE_WORDS) +#define MD_SB_PERSONALITY_WORDS 64 +#define MD_SB_DESCRIPTOR_WORDS 32 +#define MD_SB_DISKS 27 +#define MD_SB_DISKS_WORDS (MD_SB_DISKS*MD_SB_DESCRIPTOR_WORDS) +#define MD_SB_RESERVED_WORDS (1024 - MD_SB_GENERIC_WORDS - MD_SB_PERSONALITY_WORDS - MD_SB_DISKS_WORDS - MD_SB_DESCRIPTOR_WORDS) +#define MD_SB_EQUAL_WORDS (MD_SB_GENERIC_WORDS + MD_SB_PERSONALITY_WORDS + MD_SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define MD_DISK_FAULTY 0 /* disk is faulty / operational */ +#define MD_DISK_ACTIVE 1 /* disk is running or spare disk */ +#define MD_DISK_SYNC 2 /* disk is in sync with the raid set */ +#define MD_DISK_REMOVED 3 /* disk is in sync with the raid set */ + +typedef struct mdp_device_descriptor_s { + __u32 number; /* 0 Device number in the entire set */ + __u32 major; /* 1 Device major number */ + __u32 minor; /* 2 Device minor number */ + __u32 raid_disk; /* 3 The role of the device in the raid set */ + __u32 state; /* 4 Operational state */ + __u32 reserved[MD_SB_DESCRIPTOR_WORDS - 5]; +} mdp_disk_t; + +#define MD_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define MD_SB_CLEAN 0 +#define MD_SB_ERRORS 1 + +typedef struct mdp_superblock_s { + /* + * Constant generic information + */ + __u32 md_magic; /* 0 MD identifier */ + __u32 major_version; /* 1 major version to which the set conforms */ + __u32 minor_version; /* 2 minor version ... */ + __u32 patch_version; /* 3 patchlevel version ... */ + __u32 gvalid_words; /* 4 Number of used words in this section */ + __u32 set_uuid0; /* 5 Raid set identifier */ + __u32 ctime; /* 6 Creation time */ + __u32 level; /* 7 Raid personality */ + __u32 size; /* 8 Apparent size of each individual disk */ + __u32 nr_disks; /* 9 total disks in the raid set */ + __u32 raid_disks; /* 10 disks in a fully functional raid set */ + __u32 md_minor; /* 11 preferred MD minor device number */ + __u32 not_persistent; /* 12 does it have a persistent superblock */ + __u32 set_uuid1; /* 13 Raid set identifier #2 */ + __u32 set_uuid2; /* 14 Raid set identifier #3 */ + __u32 set_uuid3; /* 15 Raid set identifier #4 */ + __u32 gstate_creserved[MD_SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + __u32 utime; /* 0 Superblock update time */ + __u32 state; /* 1 State bits (clean, ...) */ + __u32 active_disks; /* 2 Number of currently active disks */ + __u32 working_disks; /* 3 Number of working disks */ + __u32 failed_disks; /* 4 Number of failed disks */ + __u32 spare_disks; /* 5 Number of spare disks */ + __u32 sb_csum; /* 6 checksum of the whole superblock */ +#ifdef __BIG_ENDIAN + __u32 events_hi; /* 7 high-order of superblock update count */ + __u32 events_lo; /* 8 low-order of superblock update count */ +#else + __u32 events_lo; /* 7 low-order of superblock update count */ + __u32 events_hi; /* 8 high-order of superblock update count */ +#endif + __u32 gstate_sreserved[MD_SB_GENERIC_STATE_WORDS - 9]; + + /* + * Personality information + */ + __u32 layout; /* 0 the array's physical layout */ + __u32 chunk_size; /* 1 chunk size in bytes */ + __u32 root_pv; /* 2 LV root PV */ + __u32 root_block; /* 3 LV root block */ + __u32 pstate_reserved[MD_SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + mdp_disk_t disks[MD_SB_DISKS]; + + /* + * Reserved + */ + __u32 reserved[MD_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + mdp_disk_t this_disk; + +} mdp_super_t; + +static inline __u64 md_event(mdp_super_t *sb) { + __u64 ev = sb->events_hi; + return (ev<<32)| sb->events_lo; +} + +#endif + diff --git a/md_u.h b/md_u.h new file mode 100644 index 00000000..22a15438 --- /dev/null +++ b/md_u.h @@ -0,0 +1,116 @@ +/* + md_u.h : user <=> kernel API between Linux raidtools and RAID drivers + Copyright (C) 1998 Ingo Molnar + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + You should have received a copy of the GNU General Public License + (for example /usr/src/linux/COPYING); if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _MD_U_H +#define _MD_U_H + +/* ioctls */ + +/* status */ +#define RAID_VERSION _IOR (MD_MAJOR, 0x10, mdu_version_t) +#define GET_ARRAY_INFO _IOR (MD_MAJOR, 0x11, mdu_array_info_t) +#define GET_DISK_INFO _IOR (MD_MAJOR, 0x12, mdu_disk_info_t) +#define PRINT_RAID_DEBUG _IO (MD_MAJOR, 0x13) +#define RAID_AUTORUN _IO (MD_MAJOR, 0x14) + +/* configuration */ +#define CLEAR_ARRAY _IO (MD_MAJOR, 0x20) +#define ADD_NEW_DISK _IOW (MD_MAJOR, 0x21, mdu_disk_info_t) +#define HOT_REMOVE_DISK _IO (MD_MAJOR, 0x22) +#define SET_ARRAY_INFO _IOW (MD_MAJOR, 0x23, mdu_array_info_t) +#define SET_DISK_INFO _IO (MD_MAJOR, 0x24) +#define WRITE_RAID_INFO _IO (MD_MAJOR, 0x25) +#define UNPROTECT_ARRAY _IO (MD_MAJOR, 0x26) +#define PROTECT_ARRAY _IO (MD_MAJOR, 0x27) +#define HOT_ADD_DISK _IO (MD_MAJOR, 0x28) +#define SET_DISK_FAULTY _IO (MD_MAJOR, 0x29) + +/* usage */ +#define RUN_ARRAY _IOW (MD_MAJOR, 0x30, mdu_param_t) +#define START_ARRAY _IO (MD_MAJOR, 0x31) +#define STOP_ARRAY _IO (MD_MAJOR, 0x32) +#define STOP_ARRAY_RO _IO (MD_MAJOR, 0x33) +#define RESTART_ARRAY_RW _IO (MD_MAJOR, 0x34) + +typedef struct mdu_version_s { + int major; + int minor; + int patchlevel; +} mdu_version_t; + +typedef struct mdu_array_info_s { + /* + * Generic constant information + */ + int major_version; + int minor_version; + int patch_version; + int ctime; + int level; + int size; + int nr_disks; + int raid_disks; + int md_minor; + int not_persistent; + + /* + * Generic state information + */ + int utime; /* 0 Superblock update time */ + int state; /* 1 State bits (clean, ...) */ + int active_disks; /* 2 Number of currently active disks */ + int working_disks; /* 3 Number of working disks */ + int failed_disks; /* 4 Number of failed disks */ + int spare_disks; /* 5 Number of spare disks */ + + /* + * Personality information + */ + int layout; /* 0 the array's physical layout */ + int chunk_size; /* 1 chunk size in bytes */ + +} mdu_array_info_t; + +typedef struct mdu_disk_info_s { + /* + * configuration/status of one particular disk + */ + int number; + int major; + int minor; + int raid_disk; + int state; + +} mdu_disk_info_t; + +typedef struct mdu_start_info_s { + /* + * configuration/status of one particular disk + */ + int major; + int minor; + int raid_disk; + int state; + +} mdu_start_info_t; + +typedef struct mdu_param_s +{ + int personality; /* 1,2,3,4 */ + int chunk_size; /* in bytes */ + int max_fault; /* unused for now */ +} mdu_param_t; + +#endif + diff --git a/mdctl.c b/mdctl.c new file mode 100644 index 00000000..a2ece910 --- /dev/null +++ b/mdctl.c @@ -0,0 +1,395 @@ +/* + * mdctl - manage Linux "md" devices aka RAID arrays. + * + * Copyright (C) 2001 Neil Brown + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#include "mdctl.h" +#include "md_p.h" + +int main(int argc, char *argv[]) +{ + char mode = '\0'; + int opt; + char *help_text; + char *c; + int rv; + int i; + + int chunk = 0; + int level = -10; + int layout = -1; + int raiddisks = 0; + int sparedisks = 0; + int uuid[4]; + int uuidset = 0; + char *configfile = NULL; + int scan = 0; + char devmode = 0; + int runstop = 0; + int readonly = 0; + char *mddev = NULL; + char *subdev[MD_SB_DISKS]; + int devmodes[MD_SB_DISKS]; + int subdevs = 0; + int verbose = 0; + int force = 0; + + int mdfd = -1; + + while ((opt=getopt_long(argc, argv, + short_options, long_options, + NULL)) != -1) { + + switch(opt) { + case '@': /* just incase they say --manage */ + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + /* setting mode - only once */ + if (mode) { + fprintf(stderr, "mdctl: --%s/-%c not allowed, mode already set to %s\n", + long_options[opt-'A'+1].name, + long_options[opt-'A'+1].val, + long_options[mode-'A'+1].name); + exit(2); + } + mode = opt; + continue; + + case 'h': + help_text = Help; + switch (mode) { + case 'C': help_text = Help_create; break; + case 'B': help_text = Help_build; break; + case 'A': help_text = Help_assemble; break; + } + fputs(help_text,stderr); + exit(0); + + case 'V': + fputs(Version, stderr); + exit(0); + + case 'v': verbose = 1; + continue; + + case 1: /* an undecorated option - must be a device name. + * The first device is the "md" device unless scan + * has been set or mode is Examine or Detail + */ + if (mddev == NULL && !scan && mode != 'E' && mode != 'D') + mddev = optarg; + else { + if (subdevs +1 >= MD_SB_DISKS) { + fprintf(stderr, "mdctl: too many devices at %s - current limit -s %d\n", + optarg, MD_SB_DISKS); + exit(2); + } + subdev[subdevs] = optarg; + devmodes[subdevs] = devmode; + subdevs++; + } + continue; + + case ':': + case '?': + fputs(Usage, stderr); + exit(2); + default: + /* force mode setting - @==manage if nothing else */ + if (!mode) mode = '@'; + } + + /* We've got a mode, and opt is now something else which + * could depend on the mode */ +#define O(a,b) ((a<<8)|b) + switch (O(mode,opt)) { + case O('C','c'): + case O('B','c'): /* chunk or rounding */ + if (chunk) { + fprintf(stderr, "mdctl: chunk/rounding may only be specified once. " + "Second value is %s.\n", optarg); + exit(2); + } + chunk = strtol(optarg, &c, 10); + if (!optarg[0] || *c) { + fprintf(stderr, "mdctl: invalid chunk/rounding value: %s\n", + optarg); + exit(2); + } + continue; + + case O('C','l'): + case O('B','l'): /* set raid level*/ + if (level != -10) { + fprintf(stderr, "mdctl: raid level may only be set once. " + "Second value is %s.\n", optarg); + exit(2); + } + if (strcmp(optarg,"linear")==0) + level = -1; + else if (strlen(optarg)==1 && strchr("01245", optarg[0])) + level = optarg[0]-'0'; + else { + fprintf(stderr, "mdctl: invalid raid level: %s\n", + optarg); + exit(2); + } + if (level > 0 && mode == 'B') { + fprintf(stderr, "mdctl: Raid level %s not permitted with --build.\n", + optarg); + exit(2); + } + if (layout >=0 && level < 4) { + fprintf(stderr, "mdctl: raid level %s is incompatible with layout setting\n", + optarg); + exit(2); + } + if (sparedisks > 0 && level < 1) { + fprintf(stderr, "mdctl: raid level %s is incompatible with spare-disks setting.\n", + optarg); + exit(2); + } + continue; + + case O('C','p'): /* raid5 layout */ + if (layout >= 0) { + fprintf(stderr,"mdctl: layout may only be sent once. " + "Second value was %s\n", optarg); + exit(2); + } + if (level > -10 && level < 4) { + fprintf(stderr,"mdctl: layout is incompatible with raid levels below 4.\n"); + exit(2); + } + if (strcmp(optarg, "left-symmetric")==0 || strcmp(optarg,"ls")==0) + layout = ALGORITHM_LEFT_SYMMETRIC; + else if (strcmp(optarg, "left-asymmetric")==0 || strcmp(optarg,"la")==0) + layout = ALGORITHM_LEFT_ASYMMETRIC; + else if (strcmp(optarg, "right-symmetric")==0 || strcmp(optarg,"rs")==0) + layout = ALGORITHM_RIGHT_SYMMETRIC; + else if (strcmp(optarg, "right-asymmetric")==0 || strcmp(optarg,"ra")==0) + layout = ALGORITHM_RIGHT_ASYMMETRIC; + else { + fprintf(stderr,"mdctl: %s is not a valid layout value\n", + optarg); + exit(2); + } + continue; + + case O('C','n'): + case O('B','n'): /* number of raid disks */ + if (raiddisks) { + fprintf(stderr, "mdctl: raid-disks set twice: %d and %s\n", + raiddisks, optarg); + exit(2); + } + raiddisks = strtol(optarg, &c, 10); + if (!optarg[0] || *c || raiddisks<=0 || raiddisks > MD_SB_DISKS) { + fprintf(stderr, "mdctl: invalid number of raid disks: %s\n", + optarg); + exit(2); + } + continue; + + case O('C','x'): /* number of spare (eXtra) discs */ + if (sparedisks) { + fprintf(stderr,"mdctl: spare-disks set twice: %d and %s\n", + sparedisks, optarg); + exit(2); + } + if (level > -10 && level < 1) { + fprintf(stderr, "mdctl: spare-disks setting is incompatible with raid level %d\n", + level); + exit(2); + } + sparedisks = strtol(optarg, &c, 10); + if (!optarg[0] || *c || sparedisks < 0 || sparedisks > MD_SB_DISKS - raiddisks) { + fprintf(stderr, "mdctl: invalid number of spare disks: %s\n", + optarg); + exit(2); + } + continue; + + /* now for the Assemble options */ + case O('A','f'): /* force assembly */ + force = 1; + continue; + case O('A','u'): /* uuid of array */ + if (uuidset) { + fprintf(stderr, "mdctl: uuid cannot bet set twice. " + "Second value %s.\n", optarg); + exit(2); + } + if (parse_uuid(optarg, uuid)) + uuidset = 1; + else { + fprintf(stderr,"mdctl: Bad uuid: %s\n", optarg); + exit(2); + } + continue; + + case O('A','c'): /* config file */ + if (configfile) { + fprintf(stderr, "mdctl: configfile cannot be set twice. " + "Second value is %s.\n", optarg); + exit(2); + } + configfile = optarg; + /* FIXME possibly check that config file exists. Even parse it */ + continue; + case O('A','s'): /* scan */ + scan = 1; + continue; + + /* now the general management options. Some are applicable + * to other modes. None have arguments. + */ + case O('@','a'): + case O('C','a'): + case O('B','a'): + case O('A','a'): /* add a drive */ + devmode = 'a'; + continue; + case O('@','r'): /* remove a drive */ + devmode = 'r'; + continue; + case O('@','f'): /* set faulty */ + case O('C','f'): + devmode = 'f'; + continue; + case O('@','R'): + case O('A','R'): + case O('B','R'): + case O('C','R'): /* Run the array */ + if (runstop < 0) { + fprintf(stderr, "mdctl: Cannot both Stop and Run an array\n"); + exit(2); + } + runstop = 1; + continue; + case O('@','S'): + if (runstop > 0) { + fprintf(stderr, "mdctl: Cannot both Run and Stop an array\n"); + exit(2); + } + runstop = -1; + continue; + + case O('@','o'): + if (readonly < 0) { + fprintf(stderr, "mdctl: Cannot have both readonly and readwrite\n"); + exit(2); + } + readonly = 1; + continue; + case O('@','w'): + if (readonly > 0) { + fprintf(stderr, "mkdctl: Cannot have both readwrite and readonly.\n"); + exit(2); + } + readonly = -1; + continue; + } + /* We have now processed all the valid options. Anything else is + * an error + */ + fprintf(stderr, "mdctl: option %c not valid in mode %c\n", + opt, mode); + exit(2); + + } + + if (!mode) { + fputs(Usage, stderr); + exit(2); + } + /* Ok, got the option parsing out of the way + * hopefully it's mostly right but there might be some stuff + * missing + * + * That is mosty checked in ther per-mode stuff but... + * + * 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, "mdctl: an md device must be given in this mode\n"); + exit(2); + } + mdfd = open(mddev, O_RDWR, 0); + if (mdfd < 0) { + fprintf(stderr,"mdctl: error opening %s: %s\n", + mddev, strerror(errno)); + exit(1); + } + if (md_get_version(mdfd) <= 0) { + fprintf(stderr, "mdctl: %s does not appear to be an md device\n", + mddev); + close(mdfd); + exit(1); + } + } + + rv =0; + switch(mode) { + case '@':/* Management */ + /* readonly, add/remove, readwrite, runstop */ + if (readonly>1) + rv = Manage_ro(mddev, mdfd, readonly); + if (!rv && subdevs) + rv = Manage_subdevs(mddev, mdfd, subdevs, subdev, devmodes); + if (!rv && readonly < 1) + rv = Manage_ro(mddev, mdfd, readonly); + if (!rv && runstop) + rv = Manage_runstop(mddev, mdfd, runstop); + break; + case 'A': /* Assemble */ + rv = Assemble(mddev, mdfd, uuid, uuidset, configfile, scan, subdevs, subdev, readonly, runstop, verbose, force); + break; + case 'B': /* Build */ + rv = Build(mddev, mdfd, chunk, level, raiddisks, subdevs,subdev); + break; + case 'C': /* Create */ + rv = Create(mddev, mdfd, chunk, level, layout, raiddisks, sparedisks, + subdevs,subdev,runstop); + break; + case 'D': /* Detail */ + for (i=0; i + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#define __USE_LARGEFILE64 +#include +extern __off64_t lseek64 __P ((int __fd, __off64_t __offset, int __whence)); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#define MD_MAJOR 9 + + +#include "md_u.h" + +extern char short_options[]; +extern struct option long_options[]; +extern char Version[], Usage[], Help[], Help_create[], Help_build[], Help_assemble[]; + +/* structures read from config file */ +/* List of mddevice names and uuids */ +typedef struct mddev_uuid_s { + char *devname; + __u32 uuid[4]; + struct mddev_uuid_s *next; +} *mddev_uuid_t; + +/* List of device names - wildcards expanded */ +typedef struct mddev_dev_s { + char *devname; + struct mddev_dev_s *next; +} *mddev_dev_t; + +/* + * RAID5 supported algorithms + */ +#define ALGORITHM_LEFT_ASYMMETRIC 0 +#define ALGORITHM_RIGHT_ASYMMETRIC 1 +#define ALGORITHM_LEFT_SYMMETRIC 2 +#define ALGORITHM_RIGHT_SYMMETRIC 3 + + +extern int Manage_ro(char *devname, int fd, int readonly); +extern int Manage_runstop(char *devname, int fd, int runstop); +extern int Manage_subdevs(char *devname, int fd, + int devcnt, char *devnames[], int devmodes[]); + + +extern int Assemble(char *mddev, int mdfd, + int uuid[4], int uuidset, + char *conffile, int scan, + int subdevs, char *subdev[], + int readonly, int runstop, + int verbose, int force); + +extern int Build(char *mddev, int mdfd, int chunk, int level, + int raiddisks, + int subdevs, char *subdev[]); + + +extern int Create(char *mddev, int mdfd, + int chunk, int level, int layout, int raiddisks, int sparedisks, + int subdevs, char *subdev[], + int runstop); + +extern int Detail(char *dev); +extern int Examine(char *dev); + +extern int md_get_version(int fd); +extern int get_linux_version(); +extern int parse_uuid(char *str, int uuid[4]); + +extern mddev_uuid_t conf_get_uuids(char *); +extern mddev_dev_t conf_get_devs(char *); diff --git a/util.c b/util.c new file mode 100644 index 00000000..bd7f1d8f --- /dev/null +++ b/util.c @@ -0,0 +1,224 @@ +/* + * mdctl - manage Linux "md" devices aka RAID arrays. + * + * Copyright (C) 2001 Neil Brown + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#include "mdctl.h" +#include "md_p.h" +#include + +/* + * Parse a 128 bit uuid in 4 integers + * format is 32 hexx nibbles with options :. separator + * If not exactly 32 hex digits are found, return 0 + * else return 1 + */ +int parse_uuid(char *str, int uuid[4]) +{ + int hit = 0; /* number of Hex digIT */ + int i; + char c; + for (i=0; i<4; i++) uuid[i]=0; + + while ((c= *str++)) { + int n; + if (c>='0' && c<='9') + n = c-'0'; + else if (c>='a' && c <= 'f') + n = 10 + c - 'a'; + else if (c>='A' && c <= 'F') + n = 10 + c - 'A'; + else if (strchr(":. -", c)) + continue; + else return 0; + + uuid[hit/4] <<= 4; + uuid[hit/4] += n; + hit++; + } + if (hit == 32) + return 1; + return 0; + +} + + +/* + * Get the md version number. + * We use the RAID_VERSION ioctl if it is supported + * If not, but we have a block device with major '9', we assume + * 0.36.0 + * + * Return version number as 24 but number - assume version parts + * always < 255 + */ + +int md_get_version(int fd) +{ + struct stat stb; + mdu_version_t vers; + + if (fstat(fd, &stb)<0) + return -1; + if ((S_IFMT&stb.st_mode) != S_IFBLK) + return -1; + + if (ioctl(fd, RAID_VERSION, &vers) == 0) + return (vers.major<<16) | (vers.minor<<8) | vers.patchlevel; + + if (MAJOR(stb.st_rdev) == MD_MAJOR) + return (36<<8); + return -1; +} + + +int get_linux_version() +{ + struct utsname name; + int a,b,c; + if (uname(&name) <0) + return -1; + + if (sscanf(name.release, "%d.%d.%d", &a,&b,&c)!= 3) + return -1; + return (a<<16)+(b<<8)+c; +} + +int enough(int level, int raid_disks, int avail_disks) +{ + switch (level) { + case -1: + case 0: + return avail_disks == raid_disks; + case 1: + return avail_disks >= 1; + case 4: + case 5: + return avail_disks >= raid_disks-1; + default: + return 0; + } +} + +int same_uuid(int a[4], int b[4]) +{ + if (a[0]==b[0] && + a[1]==b[1] && + a[2]==b[2] && + a[3]==b[3]) + return 1; + return 0; +} + +void uuid_from_super(int uuid[4], mdp_super_t *super) +{ + uuid[0] = super->set_uuid0; + if (super->minor_version >= 90) { + uuid[1] = super->set_uuid1; + uuid[2] = super->set_uuid2; + uuid[3] = super->set_uuid3; + } else { + uuid[1] = 0; + uuid[2] = 0; + uuid[3] = 0; + } +} + +int compare_super(mdp_super_t *first, mdp_super_t *second) +{ + /* + * return: + * 0 same, or first was empty, and second was copied + * 1 second had wrong number + * 2 wrong uuid + * 3 wrong other info + */ + int uuid1[4], uuid2[4]; + if (second->md_magic != MD_SB_MAGIC) + return 1; + if (first-> md_magic != MD_SB_MAGIC) { + memcpy(first, second, sizeof(*first)); + return 0; + } + + uuid_from_super(uuid1, first); + uuid_from_super(uuid2, second); + if (!same_uuid(uuid1, uuid2)) + return 2; + if (first->major_version != second->major_version || + first->minor_version != second->minor_version || + first->patch_version != second->patch_version || + first->gvalid_words != second->gvalid_words || + first->ctime != second->ctime || + first->level != second->level || + first->size != second->size || + first->raid_disks != second->raid_disks ) + return 3; + + return 0; +} + +int load_super(int fd, mdp_super_t *super) +{ + /* try to read in the superblock + * + * return + * 0 - success + * 1 - no block size + * 2 - too small + * 3 - no seek + * 4 - no read + * 5 - no magic + * 6 - wrong major version + */ + 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 (read(fd, &super, sizeof(super)) != sizeof(super)) + return 4; + + if (super->md_magic != MD_SB_MAGIC) + return 5; + + if (super->major_version != 0) + return 6; + return 0; +} + -- 2.39.2