diff options
Diffstat (limited to 'Manage.c')
-rw-r--r-- | Manage.c | 105 |
1 files changed, 76 insertions, 29 deletions
@@ -346,6 +346,9 @@ int Manage_subdevs(char *devname, int fd, mdu_disk_info_t disc; unsigned long long array_size; mddev_dev_t dv, next = NULL; + struct mdinfo *mdi = NULL; + struct mdinfo *dev = NULL; + char sys_name[20] = "dev-"; struct stat stb; int j, jnext = 0; int tfd; @@ -443,16 +446,43 @@ int Manage_subdevs(char *devname, int fd, if (jnext == 0) continue; } else { + /* + * For fail/remove operations, allow the disk + * to be completely missing, use name matching + * to a device in our sysfs entries to + * suffice. For add we need a valid block device. + * Leave this loop one of three ways: + * 1) tfd < 0 and dev is set to our device + * 2) tfd >= 0 and dev is NULL + * 3) failed to find suitable device and return + */ 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 && dv->disposition != 'a') { + strcpy(&sys_name[4], + strrchr(dv->devname, '/') + 1); + mdi = sysfs_read(fd, 0, + GET_DEVS | KEEP_GONE_DEVS); + if (!mdi) { + fprintf(stderr, Name ": can't open %s " + "and can't read sysfs info\n", + dv->devname); + return 1; + } + for (dev = mdi->devs; dev; dev = dev->next) { + if (strcmp(sys_name, dev->sys_name)) + continue; + break; + } + if (!dev) { + fprintf(stderr, Name ": can't open %s " + "and %s not listed in sysfs\n", + dv->devname, sys_name); + sysfs_free(mdi); + return 1; + } + } else { if (tfd < 0 || fstat(tfd, &stb) != 0) { fprintf(stderr, Name ": cannot find %s: %s\n", dv->devname, strerror(errno)); @@ -461,12 +491,12 @@ int Manage_subdevs(char *devname, int fd, return 1; } close(tfd); - } - if ((stb.st_mode & S_IFMT) != S_IFBLK) { - fprintf(stderr, Name ": %s is not a " - "block device.\n", - dv->devname); - return 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){ @@ -790,26 +820,36 @@ int Manage_subdevs(char *devname, int fd, return 1; } } - /* FIXME check that it is a current member */ - err = ioctl(fd, HOT_REMOVE_DISK, (unsigned long)stb.st_rdev); - if (err && errno == ENODEV) { + /* stb.st_rdev is only valid if we have a tfd that + * does not indicate an error on attempt to open + * the devname + */ + if (tfd >= 0) + err = ioctl(fd, HOT_REMOVE_DISK, + (unsigned long)stb.st_rdev); + if (tfd < 0 || (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 == major(stb.st_rdev) && - dv->disk.minor == minor(stb.st_rdev)) + if (!mdi) { + strcpy(&sys_name[4], + strrchr(dv->devname, '/') + 1); + mdi = sysfs_read(fd, 0, GET_DEVS | + KEEP_GONE_DEVS); + if (mdi) + dev = mdi->devs; + for ( ; dev ; dev=dev->next) { + if (strcmp(sys_name, dev->sys_name)) + continue; break; - if (dv) - err = sysfs_set_str(sra, dv, + } + } + if (dev) + err = sysfs_set_str(mdi, dev, "state", "remove"); else err = -1; - if (sra) - sysfs_free(sra); + if (mdi) + sysfs_free(mdi); } if (err) { fprintf(stderr, Name ": hot remove failed " @@ -844,11 +884,18 @@ int Manage_subdevs(char *devname, int fd, case 'f': /* set faulty */ /* FIXME check current member */ - if (ioctl(fd, SET_DISK_FAULTY, (unsigned long) stb.st_rdev)) { + if ((tfd >= 0 && ioctl(fd, SET_DISK_FAULTY, + (unsigned long) stb.st_rdev)) || + (tfd < 0 && sysfs_set_str(mdi, dev, "state", + "faulty"))) { fprintf(stderr, Name ": set device faulty failed for %s: %s\n", dnprintable, strerror(errno)); + if (mdi) + sysfs_free(mdi); return 1; - } + } + if (mdi) + sysfs_free(mdi); if (verbose >= 0) fprintf(stderr, Name ": set %s faulty in %s\n", dnprintable, devname); |