/* from mdopen.c */ int open_mddev(char *dev, int report_errors/*unused*/) { int mdfd = open(dev, O_RDWR); if (mdfd < 0) fprintf(stderr, Name ": error opening %s: %s\n", dev, strerror(errno)); else if (md_get_version(mdfd) <= 0) { fprintf(stderr, Name ": %s does not appear to be an md device\n", dev); close(mdfd); mdfd = -1; } return mdfd; }
int Build(char *mddev, struct mddev_dev *devlist, struct shape *s, struct context *c) { /* Build a linear or raid0 arrays without superblocks * We cannot really do any checks, we just do it. * For md_version < 0.90.0, we call REGISTER_DEV * with the device numbers, and then * START_MD giving the "geometry" * geometry is 0xpp00cc * where pp is personality: 1==linear, 2=raid0 * cc = chunk size factor: 0==4k, 1==8k etc. * * For md_version >= 0.90.0 we call * SET_ARRAY_INFO, ADD_NEW_DISK, RUN_ARRAY * */ int i; int vers; struct stat stb; int subdevs = 0, missing_disks = 0; struct mddev_dev *dv; int bitmap_fd; unsigned long long bitmapsize; int mdfd; char chosen_name[1024]; int uuid[4] = {0,0,0,0}; struct map_ent *map = NULL; /* scan all devices, make sure they really are block devices */ for (dv = devlist; dv; dv=dv->next) { subdevs++; if (strcmp("missing", dv->devname) == 0) { missing_disks++; continue; } if (stat(dv->devname, &stb)) { pr_err("Cannot find %s: %s\n", dv->devname, strerror(errno)); return 1; } if ((stb.st_mode & S_IFMT) != S_IFBLK) { pr_err("%s is not a block device.\n", dv->devname); return 1; } } if (s->raiddisks != subdevs) { pr_err("requested %d devices in array but listed %d\n", s->raiddisks, subdevs); return 1; } if (s->layout == UnSet) switch(s->level) { default: /* no layout */ s->layout = 0; break; case 10: s->layout = 0x102; /* near=2, far=1 */ if (c->verbose > 0) pr_err("layout defaults to n1\n"); break; case 5: case 6: s->layout = map_name(r5layout, "default"); if (c->verbose > 0) pr_err("layout defaults to %s\n", map_num(r5layout, s->layout)); break; case LEVEL_FAULTY: s->layout = map_name(faultylayout, "default"); if (c->verbose > 0) pr_err("layout defaults to %s\n", map_num(faultylayout, s->layout)); break; } /* We need to create the device. It can have no name. */ map_lock(&map); mdfd = create_mddev(mddev, NULL, c->autof, LOCAL, chosen_name); if (mdfd < 0) { map_unlock(&map); return 1; } mddev = chosen_name; map_update(&map, fd2devnm(mdfd), "none", uuid, chosen_name); map_unlock(&map); vers = md_get_version(mdfd); /* looks Ok, go for it */ if (vers >= 9000) { mdu_array_info_t array; array.level = s->level; if (s->size == MAX_SIZE) s->size = 0; array.size = s->size; array.nr_disks = s->raiddisks; array.raid_disks = s->raiddisks; array.md_minor = 0; if (fstat(mdfd, &stb)==0) array.md_minor = minor(stb.st_rdev); array.not_persistent = 1; array.state = 0; /* not clean, but no errors */ if (s->assume_clean) array.state |= 1; array.active_disks = s->raiddisks - missing_disks; array.working_disks = s->raiddisks - missing_disks; array.spare_disks = 0; array.failed_disks = missing_disks; if (s->chunk == 0 && (s->level==0 || s->level==LEVEL_LINEAR)) s->chunk = 64; array.chunk_size = s->chunk*1024; array.layout = s->layout; if (ioctl(mdfd, SET_ARRAY_INFO, &array)) { pr_err("SET_ARRAY_INFO failed for %s: %s\n", mddev, strerror(errno)); goto abort; } } else if (s->bitmap_file) { pr_err("bitmaps not supported with this kernel\n"); goto abort; } if (s->bitmap_file && strcmp(s->bitmap_file, "none") == 0) s->bitmap_file = NULL; if (s->bitmap_file && s->level <= 0) { pr_err("bitmaps not meaningful with level %s\n", map_num(pers, s->level)?:"given"); goto abort; }
static int mdmon(char *devnm, int must_fork, int takeover) { int mdfd; struct mdinfo *mdi, *di; struct supertype *container; sigset_t set; struct sigaction act; int pfd[2]; int status; int ignore; pid_t victim = -1; int victim_sock = -1; dprintf("starting mdmon for %s\n", devnm); mdfd = open_dev(devnm); if (mdfd < 0) { pr_err("%s: %s\n", devnm, strerror(errno)); return 1; } if (md_get_version(mdfd) < 0) { pr_err("%s: Not an md device\n", devnm); return 1; } /* Fork, and have the child tell us when they are ready */ if (must_fork) { if (pipe(pfd) != 0) { pr_err("failed to create pipe\n"); return 1; } switch(fork()) { case -1: pr_err("failed to fork: %s\n", strerror(errno)); return 1; case 0: /* child */ close(pfd[0]); break; default: /* parent */ close(pfd[1]); if (read(pfd[0], &status, sizeof(status)) != sizeof(status)) { wait(&status); status = WEXITSTATUS(status); } close(pfd[0]); return status; } } else pfd[0] = pfd[1] = -1; container = xcalloc(1, sizeof(*container)); strcpy(container->devnm, devnm); container->arrays = NULL; container->sock = -1; mdi = sysfs_read(mdfd, container->devnm, GET_VERSION|GET_LEVEL|GET_DEVS); if (!mdi) { pr_err("failed to load sysfs info for %s\n", container->devnm); exit(3); } if (mdi->array.level != UnSet) { pr_err("%s is not a container - cannot monitor\n", devnm); exit(3); } if (mdi->array.major_version != -1 || mdi->array.minor_version != -2) { pr_err("%s does not use external metadata - cannot monitor\n", devnm); exit(3); } container->ss = version_to_superswitch(mdi->text_version); if (container->ss == NULL) { pr_err("%s uses unsupported metadata: %s\n", devnm, mdi->text_version); exit(3); } container->devs = NULL; for (di = mdi->devs; di; di = di->next) { struct mdinfo *cd = xmalloc(sizeof(*cd)); *cd = *di; cd->next = container->devs; container->devs = cd; } sysfs_free(mdi); /* SIGUSR is sent between parent and child. So both block it * and enable it only with pselect. */ sigemptyset(&set); sigaddset(&set, SIGUSR1); sigaddset(&set, SIGTERM); sigprocmask(SIG_BLOCK, &set, NULL); act.sa_handler = wake_me; act.sa_flags = 0; sigaction(SIGUSR1, &act, NULL); act.sa_handler = term; sigaction(SIGTERM, &act, NULL); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); victim = mdmon_pid(container->devnm); if (victim >= 0) victim_sock = connect_monitor(container->devnm); ignore = chdir("/"); if (!takeover && victim > 0 && victim_sock >= 0) { if (fping_monitor(victim_sock) == 0) { pr_err("%s already managed\n", container->devnm); exit(3); } close(victim_sock); victim_sock = -1; } if (container->ss->load_container(container, mdfd, devnm)) { pr_err("Cannot load metadata for %s\n", devnm); exit(3); } close(mdfd); /* Ok, this is close enough. We can say goodbye to our parent now. */ if (victim > 0) remove_pidfile(devnm); if (make_pidfile(devnm) < 0) { exit(3); } container->sock = make_control_sock(devnm); status = 0; if (pfd[1] >= 0) { if (write(pfd[1], &status, sizeof(status)) < 0) pr_err("failed to notify our parent: %d\n", getppid()); close(pfd[1]); } mlockall(MCL_CURRENT | MCL_FUTURE); if (clone_monitor(container) < 0) { pr_err("failed to start monitor process: %s\n", strerror(errno)); exit(2); } if (victim > 0) { try_kill_monitor(victim, container->devnm, victim_sock); if (victim_sock >= 0) close(victim_sock); } setsid(); close(0); open("/dev/null", O_RDWR); close(1); ignore = dup(0); #ifndef DEBUG close(2); ignore = dup(0); #endif /* This silliness is to stop the compiler complaining * that we ignore 'ignore' */ if (ignore) ignore++; do_manager(container); exit(0); }
int Assemble(struct supertype *st, char *mddev, int mdfd, mddev_ident_t ident, char *conffile, mddev_dev_t devlist, int readonly, int runstop, char *update, int verbose, int force) { /* * The task of Assemble is to find a collection of * devices that should (according to their superblocks) * form an array, and to give this collection to the MD driver. * In Linux-2.4 and later, this involves submitting 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 set 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 !devlist 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 superblock - discard if different * Record events, devicenum * This should give us a list of devices for the array * We should collect the most recent event number * * 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; void *first_super = NULL, *super = NULL; struct { char *devname; unsigned int major, minor; unsigned int oldmajor, oldminor; long long events; int uptodate; int state; int raid_disk; int disk_nr; } *devices; int *best = NULL; /* indexed by raid_disk */ unsigned int bestcnt = 0; int devcnt = 0; unsigned int okcnt, sparecnt; unsigned int req_cnt; unsigned int i; int most_recent = 0; int chosen_drive; int change = 0; int inargv = 0; int start_partial_ok = force || devlist==NULL; unsigned int num_devs; mddev_dev_t tmpdev; struct mdinfo info; struct mddev_ident_s ident2; char *avail; int nextspare = 0; vers = md_get_version(mdfd); if (vers <= 0) { fprintf(stderr, Name ": %s appears not to be an md device.\n", mddev); return 1; } if (vers < 9000) { fprintf(stderr, Name ": Assemble requires driver version 0.90.0 or later.\n" " Upgrade your kernel or try --build\n"); return 1; } if (get_linux_version() < 2004000) old_linux = 1; if (ioctl(mdfd, GET_ARRAY_INFO, &info.array)>=0) { fprintf(stderr, Name ": 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 */ /* * If any subdevs are listed, then any that don't * match ident are discarded. Remainder must all match and * become the array. * If no subdevs, then we scan all devices in the config file, but * there must be something in the identity */ if (!devlist && ident->uuid_set == 0 && ident->super_minor < 0 && ident->devices == NULL) { fprintf(stderr, Name ": No identity information available for %s - cannot assemble.\n", mddev); return 1; } if (devlist == NULL) devlist = conf_get_devs(conffile); else inargv = 1; tmpdev = devlist; num_devs = 0; while (tmpdev) { num_devs++; tmpdev = tmpdev->next; } devices = malloc(num_devs * sizeof(*devices)); if (!st && ident->st) st = ident->st; if (verbose>0) fprintf(stderr, Name ": looking for devices for %s\n", mddev); while ( devlist) { char *devname; int dfd; struct stat stb; struct supertype *tst = st; devname = devlist->devname; devlist = devlist->next; if (ident->devices && !match_oneof(ident->devices, devname)) { if ((inargv && verbose>=0) || verbose > 0) fprintf(stderr, Name ": %s is not one of %s\n", devname, ident->devices); continue; } if (super) { free(super); super = NULL; } dfd = dev_open(devname, O_RDONLY|O_EXCL); if (dfd < 0) { if ((inargv && verbose >= 0) || verbose > 0) fprintf(stderr, Name ": cannot open device %s: %s\n", devname, strerror(errno)); } else if (fstat(dfd, &stb)< 0) { /* Impossible! */ fprintf(stderr, Name ": fstat failed for %s: %s\n", devname, strerror(errno)); } else if ((stb.st_mode & S_IFMT) != S_IFBLK) { fprintf(stderr, Name ": %s is not a block device.\n", devname); } else if (!tst && (tst = guess_super(dfd)) == NULL) { if ((inargv && verbose >= 0) || verbose > 0) fprintf(stderr, Name ": no recogniseable superblock\n"); } else if (tst->ss->load_super(tst,dfd, &super, NULL)) { if ((inargv && verbose >= 0) || verbose > 0) fprintf( stderr, Name ": no RAID superblock on %s\n", devname); } else { tst->ss->getinfo_super(&info, &ident2, super); } if (dfd >= 0) close(dfd); if (ident->uuid_set && (!update || strcmp(update, "uuid")!= 0) && (!super || same_uuid(info.uuid, ident->uuid, tst->ss->swapuuid)==0)) { if ((inargv && verbose >= 0) || verbose > 0) fprintf(stderr, Name ": %s has wrong uuid.\n", devname); continue; } if (ident->name[0] && (!super || strncmp(ident2.name, ident->name, 32)!=0)) { if ((inargv && verbose >= 0) || verbose > 0) fprintf(stderr, Name ": %s has wrong name.\n", devname); continue; } if (ident->super_minor != UnSet && (!super || ident->super_minor != info.array.md_minor)) { if ((inargv && verbose >= 0) || verbose > 0) fprintf(stderr, Name ": %s has wrong super-minor.\n", devname); continue; } if (ident->level != UnSet && (!super|| ident->level != info.array.level)) { if ((inargv && verbose >= 0) || verbose > 0) fprintf(stderr, Name ": %s has wrong raid level.\n", devname); continue; } if (ident->raid_disks != UnSet && (!super || ident->raid_disks!= info.array.raid_disks)) { if ((inargv && verbose >= 0) || verbose > 0) fprintf(stderr, Name ": %s requires wrong number of drives.\n", devname); continue; } /* If we are this far, then we are commited to this device. * If the super_block doesn't exist, or doesn't match others, * then we cannot continue */ if (!super) { fprintf(stderr, Name ": %s has no superblock - assembly aborted\n", devname); free(first_super); return 1; } st = tst; /* commit to this format, if haven't already */ if (st->ss->compare_super(&first_super, super)) { fprintf(stderr, Name ": superblock on %s doesn't match others - assembly aborted\n", devname); free(super); free(first_super); return 1; } /* looks like a good enough match to update the super block if needed */ if (update) { /* prepare useful information in info structures */ struct stat stb2; fstat(mdfd, &stb2); info.array.md_minor = minor(stb2.st_rdev); if (strcmp(update, "uuid")==0 && !ident->uuid_set) { int rfd; if ((rfd = open("/dev/urandom", O_RDONLY)) < 0 || read(rfd, ident->uuid, 16) != 16) { *(__u32*)(ident->uuid) = random(); *(__u32*)(ident->uuid+1) = random(); *(__u32*)(ident->uuid+2) = random(); *(__u32*)(ident->uuid+3) = random(); } if (rfd >= 0) close(rfd); ident->uuid_set = 1; } memcpy(info.uuid, ident->uuid, 16); st->ss->update_super(&info, super, update, devname, verbose); dfd = dev_open(devname, O_RDWR|O_EXCL); if (dfd < 0) fprintf(stderr, Name ": Cannot open %s for superblock update\n", devname); else if (st->ss->store_super(st, dfd, super)) fprintf(stderr, Name ": Could not re-write superblock on %s.\n", devname); if (dfd >= 0) close(dfd); } if (verbose > 0) fprintf(stderr, Name ": %s is identified as a member of %s, slot %d.\n", devname, mddev, info.disk.raid_disk); devices[devcnt].devname = devname; devices[devcnt].major = major(stb.st_rdev); devices[devcnt].minor = minor(stb.st_rdev); devices[devcnt].oldmajor = info.disk.major; devices[devcnt].oldminor = info.disk.minor; devices[devcnt].events = info.events; devices[devcnt].raid_disk = info.disk.raid_disk; devices[devcnt].disk_nr = info.disk.number; devices[devcnt].uptodate = 0; devices[devcnt].state = info.disk.state; if (most_recent < devcnt) { if (devices[devcnt].events > devices[most_recent].events) most_recent = devcnt; } if (info.array.level == -4) /* with multipath, the raid_disk from the superblock is meaningless */ i = devcnt; else i = devices[devcnt].raid_disk; if (i+1 == 0) { if (nextspare < info.array.raid_disks) nextspare = info.array.raid_disks; i = nextspare++; } if (i < 10000) { if (i >= bestcnt) { unsigned int newbestcnt = i+10; int *newbest = malloc(sizeof(int)*newbestcnt); unsigned int c; for (c=0; c < newbestcnt; c++) if (c < bestcnt) newbest[c] = best[c]; else newbest[c] = -1; if (best)free(best); best = newbest; bestcnt = newbestcnt; } if (best[i] == -1 || devices[best[i]].events < devices[devcnt].events) best[i] = devcnt; } devcnt++; } if (super) free(super); super = NULL; if (update && strcmp(update, "byteorder")==0) st->minor_version = 90; if (devcnt == 0) { fprintf(stderr, Name ": no devices found for %s\n", mddev); free(first_super); return 1; } st->ss->getinfo_super(&info, &ident2, first_super); /* now we have some devices that might be suitable. * I wonder how many */ avail = malloc(info.array.raid_disks); memset(avail, 0, info.array.raid_disks); okcnt = 0; sparecnt=0; for (i=0; i< bestcnt ;i++) { int j = best[i]; int event_margin = 1; /* always allow a difference of '1' * like the kernel does */ if (j < 0) continue; /* note: we ignore error flags in multipath arrays * as they don't make sense */ if (info.array.level != -4) if (!(devices[j].state & (1<<MD_DISK_SYNC))) { if (!(devices[j].state & (1<<MD_DISK_FAULTY))) sparecnt++; continue; } if (devices[j].events+event_margin >= devices[most_recent].events) { devices[j].uptodate = 1; if (i < info.array.raid_disks) { okcnt++; avail[i]=1; } else sparecnt++; } } while (force && !enough(info.array.level, info.array.raid_disks, info.array.layout, avail, okcnt)) { /* Choose the newest best drive which is * not up-to-date, update the superblock * and add it. */ int fd; chosen_drive = -1; for (i=0; i<info.array.raid_disks && i < bestcnt; i++) { int j = best[i]; if (j>=0 && !devices[j].uptodate && devices[j].events > 0 && (chosen_drive < 0 || devices[j].events > devices[chosen_drive].events)) chosen_drive = j; } if (chosen_drive < 0) break; if (verbose >= 0) fprintf(stderr, Name ": forcing event count in %s(%d) from %d upto %d\n", devices[chosen_drive].devname, devices[chosen_drive].raid_disk, (int)(devices[chosen_drive].events), (int)(devices[most_recent].events)); fd = dev_open(devices[chosen_drive].devname, O_RDWR|O_EXCL); if (fd < 0) { fprintf(stderr, Name ": Couldn't open %s for write - not updating\n", devices[chosen_drive].devname); devices[chosen_drive].events = 0; continue; } if (st->ss->load_super(st,fd, &super, NULL)) { close(fd); fprintf(stderr, Name ": RAID superblock disappeared from %s - not updating.\n", devices[chosen_drive].devname); devices[chosen_drive].events = 0; continue; } info.events = devices[most_recent].events; st->ss->update_super(&info, super, "force", devices[chosen_drive].devname, verbose); if (st->ss->store_super(st, fd, super)) { close(fd); fprintf(stderr, Name ": Could not re-write superblock on %s\n", devices[chosen_drive].devname); devices[chosen_drive].events = 0; free(super); continue; } close(fd); devices[chosen_drive].events = devices[most_recent].events; devices[chosen_drive].uptodate = 1; avail[chosen_drive] = 1; okcnt++; free(super); } /* Now we want to look at the superblock which the kernel will base things on * and compare the devices that we think are working with the devices that the * superblock thinks are working. * If there are differences and --force is given, then update this chosen * superblock. */ chosen_drive = -1; super = NULL; for (i=0; chosen_drive < 0 && i<bestcnt; i++) { int j = best[i]; int fd; if (j<0) continue; if (!devices[j].uptodate) continue; chosen_drive = j; if ((fd=dev_open(devices[j].devname, O_RDONLY|O_EXCL))< 0) { fprintf(stderr, Name ": Cannot open %s: %s\n", devices[j].devname, strerror(errno)); return 1; } if (st->ss->load_super(st,fd, &super, NULL)) { close(fd); fprintf(stderr, Name ": RAID superblock has disappeared from %s\n", devices[j].devname); return 1; } close(fd); } if (super == NULL) { fprintf(stderr, Name ": No suitable drives found for %s\n", mddev); return 1; } st->ss->getinfo_super(&info, &ident2, super); for (i=0; i<bestcnt; i++) { int j = best[i]; unsigned int desired_state; if (i < info.array.raid_disks) desired_state = (1<<MD_DISK_ACTIVE) | (1<<MD_DISK_SYNC); else desired_state = 0; if (j<0) continue; if (!devices[j].uptodate) continue; info.disk.number = devices[j].disk_nr; info.disk.raid_disk = i; info.disk.state = desired_state; if (devices[j].uptodate && st->ss->update_super(&info, super, "assemble", NULL, verbose)) { if (force) { if (verbose >= 0) fprintf(stderr, Name ": " "clearing FAULTY flag for device %d in %s for %s\n", j, mddev, devices[j].devname); change = 1; } else { if (verbose >= -1) fprintf(stderr, Name ": " "device %d in %s has wrong state in superblock, but %s seems ok\n", i, mddev, devices[j].devname); } } #if 0 if (!devices[j].uptodate && !(super.disks[i].state & (1 << MD_DISK_FAULTY))) { fprintf(stderr, Name ": devices %d of %s is not marked FAULTY in superblock, but cannot be found\n", i, mddev); } #endif } if (force && okcnt == info.array.raid_disks-1) { /* FIXME check event count */ change += st->ss->update_super(&info, super, "force", devices[chosen_drive].devname, verbose); } if (change) { int fd; fd = dev_open(devices[chosen_drive].devname, O_RDWR|O_EXCL); if (fd < 0) { fprintf(stderr, Name ": Could open %s for write - cannot Assemble array.\n", devices[chosen_drive].devname); return 1; } if (st->ss->store_super(st, fd, super)) { close(fd); fprintf(stderr, Name ": Could not re-write superblock on %s\n", devices[chosen_drive].devname); return 1; } close(fd); } /* count number of in-sync devices according to the superblock. * We must have this number to start the array without -s or -R */ req_cnt = info.array.working_disks; /* Almost ready to actually *do* something */ if (!old_linux) { int rv; if ((vers % 100) >= 1) { /* can use different versions */ mdu_array_info_t inf; memset(&inf, 0, sizeof(inf)); inf.major_version = st->ss->major; inf.minor_version = st->minor_version; rv = ioctl(mdfd, SET_ARRAY_INFO, &inf); } else rv = ioctl(mdfd, SET_ARRAY_INFO, NULL); if (rv) { fprintf(stderr, Name ": SET_ARRAY_INFO failed for %s: %s\n", mddev, strerror(errno)); return 1; } if (ident->bitmap_fd >= 0) { if (ioctl(mdfd, SET_BITMAP_FILE, ident->bitmap_fd) != 0) { fprintf(stderr, Name ": SET_BITMAP_FILE failed.\n"); return 1; } } /* First, add the raid disks, but add the chosen one last */ for (i=0; i<= bestcnt; i++) { int j; if (i < bestcnt) { j = best[i]; if (j == chosen_drive) continue; } else j = chosen_drive; if (j >= 0 /* && devices[j].uptodate */) { mdu_disk_info_t disk; memset(&disk, 0, sizeof(disk)); disk.major = devices[j].major; disk.minor = devices[j].minor; if (ioctl(mdfd, ADD_NEW_DISK, &disk)!=0) { fprintf(stderr, Name ": failed to add %s to %s: %s\n", devices[j].devname, mddev, strerror(errno)); if (i < info.array.raid_disks || i == bestcnt) okcnt--; else sparecnt--; } else if (verbose > 0) fprintf(stderr, Name ": added %s to %s as %d\n", devices[j].devname, mddev, devices[j].raid_disk); } else if (verbose > 0 && i < info.array.raid_disks) fprintf(stderr, Name ": no uptodate device for slot %d of %s\n", i, mddev); } if (runstop == 1 || (runstop == 0 && ( enough(info.array.level, info.array.raid_disks, info.array.layout, avail, okcnt) && (okcnt >= req_cnt || start_partial_ok) ))) { if (ioctl(mdfd, RUN_ARRAY, NULL)==0) { if (verbose >= 0) { fprintf(stderr, Name ": %s has been started with %d drive%s", mddev, okcnt, okcnt==1?"":"s"); if (okcnt < info.array.raid_disks) fprintf(stderr, " (out of %d)", info.array.raid_disks); if (sparecnt) fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s"); fprintf(stderr, ".\n"); } return 0; } fprintf(stderr, Name ": failed to RUN_ARRAY %s: %s\n", mddev, strerror(errno)); return 1; } if (runstop == -1) { fprintf(stderr, Name ": %s assembled from %d drive%s, but not started.\n", mddev, okcnt, okcnt==1?"":"s"); return 0; } if (verbose >= 0) { fprintf(stderr, Name ": %s assembled from %d drive%s", mddev, okcnt, okcnt==1?"":"s"); if (sparecnt) fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s"); if (!enough(info.array.level, info.array.raid_disks, info.array.layout, avail, okcnt)) fprintf(stderr, " - not enough to start the array.\n"); else { if (req_cnt == info.array.raid_disks) fprintf(stderr, " - need all %d to start it", req_cnt); else fprintf(stderr, " - need %d of %d to start", req_cnt, info.array.raid_disks); fprintf(stderr, " (use --run to insist).\n"); } } return 1; } else { /* The "chosen_drive" is a good choice, and if necessary, the superblock has * been updated to point to the current locations of devices. * so we can just start the array */ unsigned long dev; dev = makedev(devices[chosen_drive].major, devices[chosen_drive].minor); if (ioctl(mdfd, START_ARRAY, dev)) { fprintf(stderr, Name ": Cannot start array: %s\n", strerror(errno)); } } return 0; }
int Query(char *dev) { /* Give a brief description of the device, * whether it is an md device and whether it has * a superblock */ int fd = open(dev, O_RDONLY); int vers; int ioctlerr; int superror; struct mdinfo info; mdu_array_info_t array; struct supertype *st = NULL; unsigned long long larray_size; struct stat stb; char *mddev; mdu_disk_info_t disc; char *activity; if (fd < 0){ pr_err("cannot open %s: %s\n", dev, strerror(errno)); return 1; } vers = md_get_version(fd); if (ioctl(fd, GET_ARRAY_INFO, &array)<0) ioctlerr = errno; else ioctlerr = 0; fstat(fd, &stb); if (vers>=9000 && !ioctlerr) { if (!get_dev_size(fd, NULL, &larray_size)) larray_size = 0; } if (vers < 0) printf("%s: is not an md array\n", dev); else if (vers < 9000) printf("%s: is an md device, but kernel cannot provide details\n", dev); else if (ioctlerr == ENODEV) printf("%s: is an md device which is not active\n", dev); else if (ioctlerr) printf("%s: is an md device, but gives \"%s\" when queried\n", dev, strerror(ioctlerr)); else { printf("%s: %s %s %d devices, %d spare%s. Use mdadm --detail for more detail.\n", dev, human_size_brief(larray_size), map_num(pers, array.level), array.raid_disks, array.spare_disks, array.spare_disks==1?"":"s"); } st = guess_super(fd); if (st) superror = st->ss->load_super(st, fd, dev); else superror = -1; close(fd); if (superror == 0) { /* array might be active... */ st->ss->getinfo_super(st, &info, NULL); if (st->ss == &super0) { mddev = get_md_name(info.array.md_minor); disc.number = info.disk.number; activity = "undetected"; if (mddev && (fd = open(mddev, O_RDONLY))>=0) { if (md_get_version(fd) >= 9000 && ioctl(fd, GET_ARRAY_INFO, &array)>= 0) { if (ioctl(fd, GET_DISK_INFO, &disc) >= 0 && makedev((unsigned)disc.major,(unsigned)disc.minor) == stb.st_rdev) activity = "active"; else activity = "mismatch"; } close(fd); } } else { activity = "unknown"; mddev = "array"; } printf("%s: device %d in %d device %s %s %s. Use mdadm --examine for more detail.\n", dev, info.disk.number, info.array.raid_disks, activity, map_num(pers, info.array.level), mddev); if (st->ss == &super0) put_md_name(mddev); } return 0; }
int Manage_subdevs(char *devname, int fd, struct mddev_dev *devlist, int verbose, int test, char *update) { /* do something to each dev. * devmode can be * 'a' - add the device * try HOT_ADD_DISK * If that fails EINVAL, try ADD_NEW_DISK * 'r' - remove the device HOT_REMOVE_DISK * device can be 'faulty' or 'detached' in which case all * matching devices are removed. * 'f' - set the device faulty SET_DISK_FAULTY * device can be 'detached' in which case any device that * is inaccessible will be marked faulty. * For 'f' and 'r', the device can also be a kernel-internal * name such as 'sdb'. */ struct mddev_dev *add_devlist = NULL; mdu_array_info_t array; mdu_disk_info_t disc; unsigned long long array_size; struct mddev_dev *dv, *next = NULL; struct stat stb; int j, jnext = 0; int tfd = -1; struct supertype *st, *tst; char *subarray = NULL; int duuid[4]; int ouuid[4]; int lfd = -1; int sysfd = -1; int count = 0; /* number of actions taken */ if (ioctl(fd, GET_ARRAY_INFO, &array)) { fprintf(stderr, Name ": cannot get array info for %s\n", devname); return 1; } /* array.size is only 32 bit and may be truncated. * So read from sysfs if possible, and record number of sectors */ array_size = get_component_size(fd); if (array_size <= 0) array_size = array.size * 2; tst = super_by_fd(fd, &subarray); if (!tst) { fprintf(stderr, Name ": unsupport array - version %d.%d\n", array.major_version, array.minor_version); return 1; } stb.st_rdev = 0; for (dv = devlist, j=0 ; dv; dv = next, j = jnext) { unsigned long long ldsize; char dvname[20]; char *dnprintable = dv->devname; char *add_dev = dv->devname; int err; int re_add_failed = 0; next = dv->next; jnext = 0; if (strcmp(dv->devname, "failed")==0 || strcmp(dv->devname, "faulty")==0) { int remaining_disks = array.nr_disks; if (dv->disposition != 'r') { fprintf(stderr, Name ": %s only meaningful " "with -r, not -%c\n", dv->devname, dv->disposition); return 1; } for (; j < 1024 && remaining_disks > 0; j++) { unsigned dev; disc.number = j; if (ioctl(fd, GET_DISK_INFO, &disc)) continue; if (disc.major == 0 && disc.minor == 0) continue; remaining_disks --; if ((disc.state & 1) == 0) /* faulty */ continue; dev = makedev(disc.major, disc.minor); if (stb.st_rdev == dev) /* already did that one */ continue; stb.st_rdev = dev; next = dv; /* same slot again next time - things might * have reshuffled */ jnext = j; sprintf(dvname,"%d:%d", disc.major, disc.minor); dnprintable = dvname; break; } if (next != dv) continue; } else if (strcmp(dv->devname, "detached") == 0) { int remaining_disks = array.nr_disks; if (dv->disposition != 'r' && dv->disposition != 'f') { fprintf(stderr, Name ": %s only meaningful " "with -r of -f, not -%c\n", dv->devname, dv->disposition); return 1; } for (; j < 1024 && remaining_disks > 0; j++) { int sfd; unsigned dev; disc.number = j; if (ioctl(fd, GET_DISK_INFO, &disc)) continue; if (disc.major == 0 && disc.minor == 0) continue; remaining_disks --; sprintf(dvname,"%d:%d", disc.major, disc.minor); sfd = dev_open(dvname, O_RDONLY); if (sfd >= 0) { close(sfd); continue; } if (dv->disposition == 'f' && (disc.state & 1) == 1) /* already faulty */ continue; if (errno != ENXIO) continue; dev = makedev(disc.major, disc.minor); if (stb.st_rdev == dev) /* already did that one */ continue; stb.st_rdev = dev; next = dv; /* same slot again next time - things might * have reshuffled */ jnext = j; dnprintable = dvname; break; } if (next != dv) continue; } else if (strcmp(dv->devname, "missing") == 0) { if (dv->disposition != 'a' || dv->re_add == 0) { fprintf(stderr, Name ": 'missing' only meaningful " "with --re-add\n"); return 1; } if (add_devlist == NULL) add_devlist = conf_get_devs(); if (add_devlist == NULL) { fprintf(stderr, Name ": no devices to scan for missing members."); continue; } add_dev = add_devlist->devname; add_devlist = add_devlist->next; if (add_devlist != NULL) next = dv; if (stat(add_dev, &stb) < 0) continue; } else if (strchr(dv->devname, '/') == NULL && strchr(dv->devname, ':') == NULL && strlen(dv->devname) < 50) { /* Assume this is a kernel-internal name like 'sda1' */ int found = 0; char dname[55]; if (dv->disposition != 'r' && dv->disposition != 'f') { fprintf(stderr, Name ": %s only meaningful " "with -r or -f, not -%c\n", dv->devname, dv->disposition); return 1; } sprintf(dname, "dev-%s", dv->devname); sysfd = sysfs_open(fd2devnum(fd), dname, "block/dev"); if (sysfd >= 0) { char dn[20]; int mj,mn; if (sysfs_fd_get_str(sysfd, dn, 20) > 0 && sscanf(dn, "%d:%d", &mj,&mn) == 2) { stb.st_rdev = makedev(mj,mn); found = 1; } close(sysfd); sysfd = -1; } if (!found) { sysfd = sysfs_open(fd2devnum(fd), dname, "state"); if (sysfd < 0) { fprintf(stderr, Name ": %s does not appear " "to be a component of %s\n", dv->devname, devname); return 1; } } } else { j = 0; tfd = dev_open(dv->devname, O_RDONLY); if (tfd < 0 && dv->disposition == 'r' && lstat(dv->devname, &stb) == 0) /* Be happy, the lstat worked, that is * enough for --remove */ ; else { if (tfd < 0 || fstat(tfd, &stb) != 0) { fprintf(stderr, Name ": cannot find %s: %s\n", dv->devname, strerror(errno)); if (tfd >= 0) close(tfd); return 1; } close(tfd); tfd = -1; } if ((stb.st_mode & S_IFMT) != S_IFBLK) { fprintf(stderr, Name ": %s is not a " "block device.\n", dv->devname); return 1; } } switch(dv->disposition){ default: fprintf(stderr, Name ": internal error - devmode[%s]=%d\n", dv->devname, dv->disposition); return 1; case 'a': /* add the device */ if (subarray) { fprintf(stderr, Name ": Cannot add disks to a" " \'member\' array, perform this" " operation on the parent container\n"); return 1; } /* Make sure it isn't in use (in 2.6 or later) */ tfd = dev_open(add_dev, O_RDONLY|O_EXCL|O_DIRECT); if (tfd < 0 && add_dev != dv->devname) continue; if (tfd < 0) { fprintf(stderr, Name ": Cannot open %s: %s\n", dv->devname, strerror(errno)); return 1; } st = dup_super(tst); if (array.not_persistent==0) st->ss->load_super(st, tfd, NULL); if (add_dev == dv->devname) { if (!get_dev_size(tfd, dv->devname, &ldsize)) { close(tfd); return 1; } } else if (!get_dev_size(tfd, NULL, &ldsize)) { close(tfd); tfd = -1; continue; } if (!tst->ss->external && array.major_version == 0 && md_get_version(fd)%100 < 2) { close(tfd); tfd = -1; if (ioctl(fd, HOT_ADD_DISK, (unsigned long)stb.st_rdev)==0) { if (verbose >= 0) fprintf(stderr, Name ": hot added %s\n", add_dev); continue; } fprintf(stderr, Name ": hot add failed for %s: %s\n", add_dev, strerror(errno)); return 1; } if (array.not_persistent == 0 || tst->ss->external) { /* need to find a sample superblock to copy, and * a spare slot to use. * For 'external' array (well, container based), * We can just load the metadata for the array. */ if (tst->sb) /* already loaded */; else if (tst->ss->external) { tst->ss->load_container(tst, fd, NULL); } else for (j = 0; j < tst->max_devs; j++) { char *dev; int dfd; disc.number = j; if (ioctl(fd, GET_DISK_INFO, &disc)) continue; if (disc.major==0 && disc.minor==0) continue; if ((disc.state & 4)==0) continue; /* sync */ /* Looks like a good device to try */ dev = map_dev(disc.major, disc.minor, 1); if (!dev) continue; dfd = dev_open(dev, O_RDONLY); if (dfd < 0) continue; if (tst->ss->load_super(tst, dfd, NULL)) { close(dfd); continue; } close(dfd); break; } /* FIXME this is a bad test to be using */ if (!tst->sb) { close(tfd); fprintf(stderr, Name ": cannot load array metadata from %s\n", devname); return 1; } /* Make sure device is large enough */ if (tst->ss->avail_size(tst, ldsize/512) < array_size) { close(tfd); tfd = -1; if (add_dev != dv->devname) continue; fprintf(stderr, Name ": %s not large enough to join array\n", dv->devname); return 1; } /* Possibly this device was recently part of the array * and was temporarily removed, and is now being re-added. * If so, we can simply re-add it. */ tst->ss->uuid_from_super(tst, duuid); if (st->sb) { struct mdinfo mdi; st->ss->getinfo_super(st, &mdi, NULL); st->ss->uuid_from_super(st, ouuid); if ((mdi.disk.state & (1<<MD_DISK_ACTIVE)) && !(mdi.disk.state & (1<<MD_DISK_FAULTY)) && memcmp(duuid, ouuid, sizeof(ouuid))==0) { /* look like it is worth a try. Need to * make sure kernel will accept it though. */ /* re-add doesn't work for version-1 superblocks * before 2.6.18 :-( */ if (array.major_version == 1 && get_linux_version() <= 2006018) goto skip_re_add; disc.number = mdi.disk.number; if (ioctl(fd, GET_DISK_INFO, &disc) != 0 || disc.major != 0 || disc.minor != 0 || !enough_fd(fd)) goto skip_re_add; disc.major = major(stb.st_rdev); disc.minor = minor(stb.st_rdev); disc.number = mdi.disk.number; disc.raid_disk = mdi.disk.raid_disk; disc.state = mdi.disk.state; if (dv->writemostly == 1) disc.state |= 1 << MD_DISK_WRITEMOSTLY; if (dv->writemostly == 2) disc.state &= ~(1 << MD_DISK_WRITEMOSTLY); remove_partitions(tfd); close(tfd); tfd = -1; if (update) { int rv = -1; tfd = dev_open(dv->devname, O_RDWR); if (tfd >= 0) rv = st->ss->update_super( st, NULL, update, devname, verbose, 0, NULL); if (rv == 0) rv = st->ss->store_super(st, tfd); close(tfd); tfd = -1; if (rv != 0) { fprintf(stderr, Name ": failed to update" " superblock during re-add\n"); return 1; } } /* don't even try if disk is marked as faulty */ errno = 0; if (ioctl(fd, ADD_NEW_DISK, &disc) == 0) { if (verbose >= 0) fprintf(stderr, Name ": re-added %s\n", add_dev); count++; continue; } if (errno == ENOMEM || errno == EROFS) { fprintf(stderr, Name ": add new device failed for %s: %s\n", add_dev, strerror(errno)); if (add_dev != dv->devname) continue; return 1; } skip_re_add: re_add_failed = 1; } st->ss->free_super(st); } if (add_dev != dv->devname) { if (verbose > 0) fprintf(stderr, Name ": --re-add for %s to %s is not possible\n", add_dev, devname); if (tfd >= 0) { close(tfd); tfd = -1; } continue; } if (dv->re_add) { if (tfd >= 0) close(tfd); fprintf(stderr, Name ": --re-add for %s to %s is not possible\n", dv->devname, devname); return 1; } if (re_add_failed) { fprintf(stderr, Name ": %s reports being an active member for %s, but a --re-add fails.\n", dv->devname, devname); fprintf(stderr, Name ": not performing --add as that would convert %s in to a spare.\n", dv->devname); fprintf(stderr, Name ": To make this a spare, use \"mdadm --zero-superblock %s\" first.\n", dv->devname); if (tfd >= 0) close(tfd); return 1; } } else { /* non-persistent. Must ensure that new drive * is at least array.size big. */ if (ldsize/512 < array_size) { fprintf(stderr, Name ": %s not large enough to join array\n", dv->devname); if (tfd >= 0) close(tfd); return 1; } } /* committed to really trying this device now*/ if (tfd >= 0) { remove_partitions(tfd); close(tfd); tfd = -1; } /* in 2.6.17 and earlier, version-1 superblocks won't * use the number we write, but will choose a free number. * we must choose the same free number, which requires * starting at 'raid_disks' and counting up */ for (j = array.raid_disks; j< tst->max_devs; j++) { disc.number = j; if (ioctl(fd, GET_DISK_INFO, &disc)) break; if (disc.major==0 && disc.minor==0) break; if (disc.state & 8) /* removed */ break; } disc.major = major(stb.st_rdev); disc.minor = minor(stb.st_rdev); disc.number =j; disc.state = 0; if (array.not_persistent==0) { int dfd; if (dv->writemostly == 1) disc.state |= 1 << MD_DISK_WRITEMOSTLY; dfd = dev_open(dv->devname, O_RDWR | O_EXCL|O_DIRECT); if (tst->ss->add_to_super(tst, &disc, dfd, dv->devname)) { close(dfd); return 1; } if (tst->ss->write_init_super(tst)) { close(dfd); return 1; } } else if (dv->re_add) { /* this had better be raid1. * As we are "--re-add"ing we must find a spare slot * to fill. */ char *used = malloc(array.raid_disks); memset(used, 0, array.raid_disks); for (j=0; j< tst->max_devs; j++) { mdu_disk_info_t disc2; disc2.number = j; if (ioctl(fd, GET_DISK_INFO, &disc2)) continue; if (disc2.major==0 && disc2.minor==0) continue; if (disc2.state & 8) /* removed */ continue; if (disc2.raid_disk < 0) continue; if (disc2.raid_disk > array.raid_disks) continue; used[disc2.raid_disk] = 1; } for (j=0 ; j<array.raid_disks; j++) if (!used[j]) { disc.raid_disk = j; disc.state |= (1<<MD_DISK_SYNC); break; } free(used); } if (dv->writemostly == 1) disc.state |= (1 << MD_DISK_WRITEMOSTLY); if (tst->ss->external) { /* add a disk * to an external metadata container */ struct mdinfo new_mdi; struct mdinfo *sra; int container_fd; int devnum = fd2devnum(fd); int dfd; container_fd = open_dev_excl(devnum); if (container_fd < 0) { fprintf(stderr, Name ": add failed for %s:" " could not get exclusive access to container\n", dv->devname); tst->ss->free_super(tst); return 1; } dfd = dev_open(dv->devname, O_RDWR | O_EXCL|O_DIRECT); if (mdmon_running(tst->container_dev)) tst->update_tail = &tst->updates; if (tst->ss->add_to_super(tst, &disc, dfd, dv->devname)) { close(dfd); close(container_fd); return 1; } if (tst->update_tail) flush_metadata_updates(tst); else tst->ss->sync_metadata(tst); sra = sysfs_read(container_fd, -1, 0); if (!sra) { fprintf(stderr, Name ": add failed for %s: sysfs_read failed\n", dv->devname); close(container_fd); tst->ss->free_super(tst); return 1; } sra->array.level = LEVEL_CONTAINER; /* Need to set data_offset and component_size */ tst->ss->getinfo_super(tst, &new_mdi, NULL); new_mdi.disk.major = disc.major; new_mdi.disk.minor = disc.minor; new_mdi.recovery_start = 0; /* Make sure fds are closed as they are O_EXCL which * would block add_disk */ tst->ss->free_super(tst); if (sysfs_add_disk(sra, &new_mdi, 0) != 0) { fprintf(stderr, Name ": add new device to external metadata" " failed for %s\n", dv->devname); close(container_fd); sysfs_free(sra); return 1; } ping_monitor_by_id(devnum); sysfs_free(sra); close(container_fd); } else { tst->ss->free_super(tst); if (ioctl(fd, ADD_NEW_DISK, &disc)) { fprintf(stderr, Name ": add new device failed for %s as %d: %s\n", dv->devname, j, strerror(errno)); return 1; } } if (verbose >= 0) fprintf(stderr, Name ": added %s\n", dv->devname); break; case 'r': /* hot remove */ if (subarray) { fprintf(stderr, Name ": Cannot remove disks from a" " \'member\' array, perform this" " operation on the parent container\n"); if (sysfd >= 0) close(sysfd); return 1; } if (tst->ss->external) { /* To remove a device from a container, we must * check that it isn't in use in an array. * This involves looking in the 'holders' * directory - there must be just one entry, * the container. * To ensure that it doesn't get used as a * hold spare while we are checking, we * get an O_EXCL open on the container */ int dnum = fd2devnum(fd); lfd = open_dev_excl(dnum); if (lfd < 0) { fprintf(stderr, Name ": Cannot get exclusive access " " to container - odd\n"); if (sysfd >= 0) close(sysfd); return 1; } /* in the detached case it is not possible to * check if we are the unique holder, so just * rely on the 'detached' checks */ if (strcmp(dv->devname, "detached") == 0 || sysfd >= 0 || sysfs_unique_holder(dnum, stb.st_rdev)) /* pass */; else { fprintf(stderr, Name ": %s is %s, cannot remove.\n", dnprintable, errno == EEXIST ? "still in use": "not a member"); close(lfd); return 1; } } /* FIXME check that it is a current member */ if (sysfd >= 0) { /* device has been removed and we don't know * the major:minor number */ int n = write(sysfd, "remove", 6); if (n != 6) err = -1; else err = 0; close(sysfd); sysfd = -1; } else { err = ioctl(fd, HOT_REMOVE_DISK, (unsigned long)stb.st_rdev); if (err && errno == ENODEV) { /* Old kernels rejected this if no personality * registered */ struct mdinfo *sra = sysfs_read(fd, 0, GET_DEVS); struct mdinfo *dv = NULL; if (sra) dv = sra->devs; for ( ; dv ; dv=dv->next) if (dv->disk.major == (int)major(stb.st_rdev) && dv->disk.minor == (int)minor(stb.st_rdev)) break; if (dv) err = sysfs_set_str(sra, dv, "state", "remove"); else err = -1; if (sra) sysfs_free(sra); } } if (err) { fprintf(stderr, Name ": hot remove failed " "for %s: %s\n", dnprintable, strerror(errno)); if (lfd >= 0) close(lfd); return 1; } if (tst->ss->external) { /* * Before dropping our exclusive open we make an * attempt at preventing mdmon from seeing an * 'add' event before reconciling this 'remove' * event. */ char *name = devnum2devname(fd2devnum(fd)); if (!name) { fprintf(stderr, Name ": unable to get container name\n"); return 1; } ping_manager(name); free(name); } if (lfd >= 0) close(lfd); count++; if (verbose >= 0) fprintf(stderr, Name ": hot removed %s from %s\n", dnprintable, devname); break; case 'f': /* set faulty */ /* FIXME check current member */ if ((sysfd >= 0 && write(sysfd, "faulty", 6) != 6) || (sysfd < 0 && ioctl(fd, SET_DISK_FAULTY, (unsigned long) stb.st_rdev))) { fprintf(stderr, Name ": set device faulty failed for %s: %s\n", dnprintable, strerror(errno)); if (sysfd >= 0) close(sysfd); return 1; } if (sysfd >= 0) close(sysfd); sysfd = -1; count++; if (verbose >= 0) fprintf(stderr, Name ": set %s faulty in %s\n", dnprintable, devname); break; } } if (test && count == 0) return 2; return 0; }
int Manage_ro(char *devname, int fd, int readonly) { /* switch to readonly or rw * * requires >= 0.90.0 * first check that array is runing * use RESTART_ARRAY_RW or STOP_ARRAY_RO * */ mdu_array_info_t array; #ifndef MDASSEMBLE struct mdinfo *mdi; #endif if (md_get_version(fd) < 9000) { fprintf(stderr, Name ": need md driver version 0.90.0 or later\n"); return 1; } #ifndef MDASSEMBLE /* If this is an externally-manage array, we need to modify the * metadata_version so that mdmon doesn't undo our change. */ mdi = sysfs_read(fd, -1, GET_LEVEL|GET_VERSION); if (mdi && mdi->array.major_version == -1 && is_subarray(mdi->text_version)) { char vers[64]; strcpy(vers, "external:"); strcat(vers, mdi->text_version); if (readonly > 0) { int rv; /* We set readonly ourselves. */ vers[9] = '-'; sysfs_set_str(mdi, NULL, "metadata_version", vers); close(fd); rv = sysfs_set_str(mdi, NULL, "array_state", "readonly"); if (rv < 0) { fprintf(stderr, Name ": failed to set readonly for %s: %s\n", devname, strerror(errno)); vers[9] = mdi->text_version[0]; sysfs_set_str(mdi, NULL, "metadata_version", vers); return 1; } } else { char *cp; /* We cannot set read/write - must signal mdmon */ vers[9] = '/'; sysfs_set_str(mdi, NULL, "metadata_version", vers); cp = strchr(vers+10, '/'); if (*cp) *cp = 0; ping_monitor(vers+10); if (mdi->array.level <= 0) sysfs_set_str(mdi, NULL, "array_state", "active"); } return 0; } #endif if (ioctl(fd, GET_ARRAY_INFO, &array)) { fprintf(stderr, Name ": %s does not appear to be active.\n", devname); return 1; } if (readonly>0) { if (ioctl(fd, STOP_ARRAY_RO, NULL)) { fprintf(stderr, Name ": failed to set readonly for %s: %s\n", devname, strerror(errno)); return 1; } } else if (readonly < 0) { if (ioctl(fd, RESTART_ARRAY_RW, NULL)) { fprintf(stderr, Name ": failed to set writable for %s: %s\n", devname, strerror(errno)); return 1; } } return 0; }
int Manage_runstop(char *devname, int fd, int runstop, int quiet) { /* Run or stop the array. array must already be configured * required >= 0.90.0 * Only print failure messages if quiet == 0; * quiet > 0 means really be quiet * quiet < 0 means we will try again if it fails. */ mdu_param_t param; /* unused */ if (runstop == -1 && md_get_version(fd) < 9000) { if (ioctl(fd, STOP_MD, 0)) { if (quiet == 0) fprintf(stderr, Name ": stopping device %s " "failed: %s\n", devname, strerror(errno)); return 1; } } if (md_get_version(fd) < 9000) { fprintf(stderr, Name ": need md driver version 0.90.0 or later\n"); return 1; } /* if (ioctl(fd, GET_ARRAY_INFO, &array)) { fprintf(stderr, Name ": %s does not appear to be active.\n", devname); return 1; } */ if (runstop>0) { if (ioctl(fd, RUN_ARRAY, ¶m)) { fprintf(stderr, Name ": failed to run array %s: %s\n", devname, strerror(errno)); return 1; } if (quiet <= 0) fprintf(stderr, Name ": started %s\n", devname); } else if (runstop < 0){ struct map_ent *map = NULL; struct stat stb; struct mdinfo *mdi; int devnum; int err; int count; /* If this is an mdmon managed array, just write 'inactive' * to the array state and let mdmon clear up. */ devnum = fd2devnum(fd); /* Get EXCL access first. If this fails, then attempting * to stop is probably a bad idea. */ close(fd); fd = open(devname, O_RDONLY|O_EXCL); if (fd < 0 || fd2devnum(fd) != devnum) { if (fd >= 0) close(fd); fprintf(stderr, Name ": Cannot get exclusive access to %s:" "Perhaps a running " "process, mounted filesystem " "or active volume group?\n", devname); return 1; } mdi = sysfs_read(fd, -1, GET_LEVEL|GET_VERSION); if (mdi && mdi->array.level > 0 && is_subarray(mdi->text_version)) { int err; /* This is mdmon managed. */ close(fd); count = 25; while (count && (err = sysfs_set_str(mdi, NULL, "array_state", "inactive")) < 0 && errno == EBUSY) { usleep(200000); count--; } if (err && !quiet) { fprintf(stderr, Name ": failed to stop array %s: %s\n", devname, strerror(errno)); return 1; } /* Give monitor a chance to act */ ping_monitor(mdi->text_version); fd = open_dev_excl(devnum); if (fd < 0) { fprintf(stderr, Name ": failed to completely stop %s" ": Device is busy\n", devname); return 1; } } else if (mdi && mdi->array.major_version == -1 && mdi->array.minor_version == -2 && !is_subarray(mdi->text_version)) { struct mdstat_ent *mds, *m; /* container, possibly mdmon-managed. * Make sure mdmon isn't opening it, which * would interfere with the 'stop' */ ping_monitor(mdi->sys_name); /* now check that there are no existing arrays * which are members of this array */ mds = mdstat_read(0, 0); for (m=mds; m; m=m->next) if (m->metadata_version && strncmp(m->metadata_version, "external:", 9)==0 && is_subarray(m->metadata_version+9) && devname2devnum(m->metadata_version+10) == devnum) { if (!quiet) fprintf(stderr, Name ": Cannot stop container %s: " "member %s still active\n", devname, m->dev); free_mdstat(mds); if (mdi) sysfs_free(mdi); return 1; } } /* As we have an O_EXCL open, any use of the device * which blocks STOP_ARRAY is probably a transient use, * so it is reasonable to retry for a while - 5 seconds. */ count = 25; err = 0; while (count && fd >= 0 && (err = ioctl(fd, STOP_ARRAY, NULL)) < 0 && errno == EBUSY) { usleep(200000); count --; } if (fd >= 0 && err) { if (quiet == 0) { fprintf(stderr, Name ": failed to stop array %s: %s\n", devname, strerror(errno)); if (errno == EBUSY) fprintf(stderr, "Perhaps a running " "process, mounted filesystem " "or active volume group?\n"); } if (mdi) sysfs_free(mdi); return 1; } /* prior to 2.6.28, KOBJ_CHANGE was not sent when an md array * was stopped, so We'll do it here just to be sure. Drop any * partitions as well... */ if (fd >= 0) ioctl(fd, BLKRRPART, 0); if (mdi) sysfs_uevent(mdi, "change"); if (devnum != NoMdDev && (stat("/dev/.udev", &stb) != 0 || check_env("MDADM_NO_UDEV"))) { struct map_ent *mp = map_by_devnum(&map, devnum); remove_devices(devnum, mp ? mp->path : NULL); } if (quiet <= 0) fprintf(stderr, Name ": stopped %s\n", devname); map_lock(&map); map_remove(&map, devnum); map_unlock(&map); } return 0; }