From: Neil Brown Date: Fri, 8 Jun 2001 02:36:23 +0000 (+0000) Subject: mdctl-v0.2 X-Git-Tag: mdctl-v0.2 X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=commitdiff_plain;h=64c4757e27fe7d688685e9a86ff87a3331a88979 mdctl-v0.2 --- 64c4757e27fe7d688685e9a86ff87a3331a88979 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; +} +