diff --git a/Manage.c b/Manage.c index 6e9d4a0..acfec75 100644 --- a/Manage.c +++ b/Manage.c @@ -383,6 +383,7 @@ int Manage_subdevs(char *devname, int fd, char *dnprintable = dv->devname; char *add_dev = dv->devname; int err; + int re_add_failed = 0; next = dv->next; jnext = 0; @@ -662,14 +663,20 @@ int Manage_subdevs(char *devname, int fd, get_linux_version() <= 2006018) ; else if (st->sb) { + struct mdinfo mdi; + st->ss->getinfo_super(st, &mdi); st->ss->uuid_from_super(st, ouuid); - if (memcmp(duuid, ouuid, sizeof(ouuid))==0) { - /* looks close enough for now. Kernel - * will worry about whether a bitmap - * based reconstruction is possible. + if ((mdi.disk.state & (1<ss->getinfo_super(st, &mdi); + 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; @@ -684,8 +691,7 @@ int Manage_subdevs(char *devname, int fd, tfd = -1; /* don't even try if disk is marked as faulty */ errno = 0; - if ((disc.state & 1) == 0 && - ioctl(fd, ADD_NEW_DISK, &disc) == 0) { + if (ioctl(fd, ADD_NEW_DISK, &disc) == 0) { if (verbose >= 0) fprintf(stderr, Name ": re-added %s\n", add_dev); count++; @@ -698,7 +704,8 @@ int Manage_subdevs(char *devname, int fd, continue; return 1; } - /* fall back on normal-add */ + skip_re_add: + re_add_failed = 1; } } if (add_dev != dv->devname) { @@ -720,6 +727,17 @@ int Manage_subdevs(char *devname, int fd, 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. diff --git a/mdadm.h b/mdadm.h index 9ad99f0..3e22910 100644 --- a/mdadm.h +++ b/mdadm.h @@ -986,6 +986,7 @@ extern char *fname_from_uuid(struct supertype *st, extern unsigned long calc_csum(void *super, int bytes); extern int enough(int level, int raid_disks, int layout, int clean, char *avail, int avail_disks); +extern int enough_fd(int fd); extern int ask(char *mesg); extern unsigned long long get_component_size(int fd); extern void remove_partitions(int fd); diff --git a/tests/01r1fail b/tests/01r1fail index c378663..9f55632 100644 --- a/tests/01r1fail +++ b/tests/01r1fail @@ -20,6 +20,7 @@ mdadm $md0 --remove $dev2 $dev1 check nosync check state UUU_ +mdadm --zero-superblock $dev2 mdadm $md0 -a $dev2 check recovery check wait diff --git a/util.c b/util.c index 0cb251c..c997621 100644 --- a/util.c +++ b/util.c @@ -320,6 +320,36 @@ int enough(int level, int raid_disks, int layout, int clean, } } +int enough_fd(int fd) +{ + struct mdu_array_info_s array; + struct mdu_disk_info_s disk; + int avail_disks = 0; + int i; + char *avail; + + if (ioctl(fd, GET_ARRAY_INFO, &array) != 0 || + array.raid_disks <= 0) + return 0; + avail = calloc(array.raid_disks, 1); + for (i=0; i= array.raid_disks) + continue; + avail_disks++; + avail[disk.raid_disk] = 1; + } + /* This is used on an active array, so assume it is clean */ + return enough(array.level, array.raid_disks, array.layout, + 1, + avail, avail_disks); +} + + const int uuid_match_any[4] = { ~0, ~0, ~0, ~0 }; int same_uuid(int a[4], int b[4], int swapuuid) {