- }
-
- 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 && dv->disposition == 'A') {
- /* we are re-adding a device to a
- * completely dead array - have to depend
- * on kernel to check
- */
- } else if (!tst->sb) {
- close(tfd);
- st->ss->free_super(st);
- pr_err("cannot load array metadata from %s\n", devname);
- goto abort;
- }
-
- /* Make sure device is large enough */
- if (tst->ss->avail_size(tst, ldsize/512) <
- array_size) {
- close(tfd);
- tfd = -1;
- st->ss->free_super(st);
- if (add_dev != dv->devname)
- continue;
- pr_err("%s not large enough to join array\n",
- dv->devname);
- goto abort;
- }
-
- /* 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.
- */
-
- if (st->sb) {
- struct mdinfo mdi;
- st->ss->getinfo_super(st, &mdi, NULL);
- st->ss->uuid_from_super(st, ouuid);
- if (tst->sb)
- tst->ss->uuid_from_super(tst, duuid);
- else
- /* Assume uuid matches: kernel will check */
- memcpy(duuid, ouuid, sizeof(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
- )
- 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 || dv->writemostly > 0) {
- int rv = -1;
- tfd = dev_open(dv->devname, O_RDWR);
- if (tfd < 0) {
- pr_err("failed to open %s for"
- " superblock update during re-add\n", dv->devname);
- st->ss->free_super(st);
- goto abort;
- }
-
- if (dv->writemostly == 1)
- rv = st->ss->update_super(
- st, NULL, "writemostly",
- devname, verbose, 0, NULL);
- if (dv->writemostly == 2)
- rv = st->ss->update_super(
- st, NULL, "readwrite",
- devname, verbose, 0, NULL);
- if (update)
- 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) {
- pr_err("failed to update"
- " superblock during re-add\n");
- st->ss->free_super(st);
- goto abort;
- }
- }
- /* don't even try if disk is marked as faulty */
- errno = 0;
- if (ioctl(fd, ADD_NEW_DISK, &disc) == 0) {
- if (verbose >= 0)
- pr_err("re-added %s\n", add_dev);
- count++;
- st->ss->free_super(st);
- continue;
- }
- if (errno == ENOMEM || errno == EROFS) {
- pr_err("add new device failed for %s: %s\n",
- add_dev, strerror(errno));
- st->ss->free_super(st);
- if (add_dev != dv->devname)
- continue;
- goto abort;
- }
- }
- skip_re_add:
- st->ss->free_super(st);
- }
- if (add_dev != dv->devname) {
- if (verbose > 0)
- pr_err("--re-add for %s to %s is not possible\n",
- add_dev, devname);
- if (tfd >= 0) {
- close(tfd);
- tfd = -1;
- }
- continue;
- }
- if (dv->disposition == 'A') {
- if (tfd >= 0)
- close(tfd);
- pr_err("--re-add for %s to %s is not possible\n",
- dv->devname, devname);
- goto abort;
- }
- if (array.active_disks < array.raid_disks) {
- char *avail = xcalloc(array.raid_disks, 1);
- int d;
- int found = 0;
-
- for (d = 0; d < MAX_DISKS && found < array.active_disks; d++) {
- disc.number = d;
- if (ioctl(fd, GET_DISK_INFO, &disc))
- continue;
- if (disc.major == 0 && disc.minor == 0)
- continue;
- if (!(disc.state & (1<<MD_DISK_SYNC)))
- continue;
- avail[disc.raid_disk] = 1;
- found++;
- }
- array_failed = !enough(array.level, array.raid_disks,
- array.layout, 1, avail);
- } else
- array_failed = 0;
- if (array_failed) {
- pr_err("%s has failed so using --add cannot work and might destroy\n",
- devname);
- pr_err("data on %s. You should stop the array and re-assemble it.\n",
- dv->devname);
- if (tfd >= 0)
- close(tfd);
- goto abort;
- }
- } else {
- /* non-persistent. Must ensure that new drive
- * is at least array.size big.
- */
- if (ldsize/512 < array_size) {
- pr_err("%s not large enough to join array\n",
- dv->devname);
- if (tfd >= 0)
- close(tfd);
- goto abort;
- }
- }
- /* 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);
- goto abort;
- }
- if (tst->ss->write_init_super(tst)) {
- close(dfd);
- goto abort;
- }
- } else if (dv->disposition == 'A') {
- /* this had better be raid1.
- * As we are "--re-add"ing we must find a spare slot
- * to fill.
- */
- char *used = xcalloc(array.raid_disks, 1);
- 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) {
- pr_err("add failed for %s:"
- " could not get exclusive access to container\n",
- dv->devname);
- tst->ss->free_super(tst);
- goto abort;
- }
-
- 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);
- goto abort;
- }
- if (tst->update_tail)
- flush_metadata_updates(tst);
- else
- tst->ss->sync_metadata(tst);
-
- sra = sysfs_read(container_fd, -1, 0);
- if (!sra) {
- pr_err("add failed for %s: sysfs_read failed\n",
- dv->devname);
- close(container_fd);
- tst->ss->free_super(tst);
- goto abort;
- }
- 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) {
- pr_err("add new device to external metadata"
- " failed for %s\n", dv->devname);
- close(container_fd);
- sysfs_free(sra);
- goto abort;
- }
- ping_monitor_by_id(devnum);
- sysfs_free(sra);
- close(container_fd);
- } else {
- tst->ss->free_super(tst);
- if (ioctl(fd, ADD_NEW_DISK, &disc)) {
- pr_err("add new device failed for %s as %d: %s\n",
- dv->devname, j, strerror(errno));
- goto abort;
- }
- }
- if (verbose >= 0)
- pr_err("added %s\n", dv->devname);