summaryrefslogtreecommitdiffstats
path: root/Manage.c
diff options
context:
space:
mode:
Diffstat (limited to 'Manage.c')
-rw-r--r--Manage.c105
1 files changed, 76 insertions, 29 deletions
diff --git a/Manage.c b/Manage.c
index f848d8b..6539eda 100644
--- a/Manage.c
+++ b/Manage.c
@@ -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);