]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - sysfs.c
Merge branch 'master' into devel-3.0
[thirdparty/mdadm.git] / sysfs.c
diff --git a/sysfs.c b/sysfs.c
index 182028cea6efef992bce97e8c12f67857daa2e76..58c84e26339e8014027bcea6d722bf87b73b4030 100644 (file)
--- a/sysfs.c
+++ b/sysfs.c
@@ -61,8 +61,12 @@ int sysfs_open(int devnum, char *devname, char *attr)
 {
        char fname[50];
        int fd;
+       char *mdname = devnum2devname(devnum);
 
-       sprintf(fname, "/sys/block/%s/md/", devnum2devname(devnum));
+       if (!mdname)
+               return -1;
+
+       sprintf(fname, "/sys/block/%s/md/", mdname);
        if (devname) {
                strcat(fname, devname);
                strcat(fname, "/");
@@ -71,9 +75,33 @@ int sysfs_open(int devnum, char *devname, char *attr)
        fd = open(fname, O_RDWR);
        if (fd < 0 && errno == EACCES)
                fd = open(fname, O_RDONLY);
+       free(mdname);
        return fd;
 }
 
+void sysfs_init(struct mdinfo *mdi, int fd, int devnum)
+{
+       if (fd >= 0) {
+               struct stat stb;
+               mdu_version_t vers;
+               if (fstat(fd, &stb))
+                       return;
+               if (ioctl(fd, RAID_VERSION, &vers) != 0)
+                       return;
+               if (major(stb.st_rdev)==9)
+                       sprintf(mdi->sys_name, "md%d", (int)minor(stb.st_rdev));
+               else
+                       sprintf(mdi->sys_name, "md_d%d",
+                               (int)minor(stb.st_rdev)>>MdpMinorShift);
+       } else {
+               if (devnum >= 0)
+                       sprintf(mdi->sys_name, "md%d", devnum);
+               else
+                       sprintf(mdi->sys_name, "md_d%d",
+                               -1-devnum);
+       }
+}
+
 struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
 {
        /* Longest possible name in sysfs, mounted at /sys, is
@@ -93,26 +121,9 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
        sra = malloc(sizeof(*sra));
        if (sra == NULL)
                return sra;
-       sra->next = NULL;
+       memset(sra, 0, sizeof(*sra));
+       sysfs_init(sra, fd, devnum);
 
-       if (fd >= 0) {
-               struct stat stb;
-               mdu_version_t vers;
-               if (fstat(fd, &stb)) return NULL;
-               if (ioctl(fd, RAID_VERSION, &vers) != 0)
-                       return NULL;
-               if (major(stb.st_rdev)==9)
-                       sprintf(sra->sys_name, "md%d", (int)minor(stb.st_rdev));
-               else
-                       sprintf(sra->sys_name, "md_d%d",
-                               (int)minor(stb.st_rdev)>>MdpMinorShift);
-       } else {
-               if (devnum >= 0)
-                       sprintf(sra->sys_name, "md%d", devnum);
-               else
-                       sprintf(sra->sys_name, "md_d%d",
-                               -1-devnum);
-       }
        sprintf(fname, "/sys/block/%s/md/", sra->sys_name);
        base = fname + strlen(fname);
 
@@ -238,14 +249,33 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
                dev = malloc(sizeof(*dev));
                if (!dev)
                        goto abort;
-               dev->next = sra->devs;
-               sra->devs = dev;
-               strcpy(dev->sys_name, de->d_name);
 
                /* Always get slot, major, minor */
                strcpy(dbase, "slot");
-               if (load_sys(fname, buf))
-                       goto abort;
+               if (load_sys(fname, buf)) {
+                       /* hmm... unable to read 'slot' maybe the device
+                        * is going away?
+                        */
+                       strcpy(dbase, "block");
+                       if (readlink(fname, buf, sizeof(buf)) < 0 &&
+                           errno != ENAMETOOLONG) {
+                               /* ...yup device is gone */
+                               free(dev);
+                               continue;
+                       } else {
+                               /* slot is unreadable but 'block' link
+                                * still intact... something bad is happening
+                                * so abort
+                                */
+                               free(dev);
+                               goto abort;
+                       }
+                       
+               }
+               dev->next = sra->devs;
+               sra->devs = dev;
+
+               strcpy(dev->sys_name, de->d_name);
                dev->disk.raid_disk = strtoul(buf, &ep, 10);
                if (*ep) dev->disk.raid_disk = -1;
 
@@ -364,8 +394,11 @@ int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev,
                return -1;
        n = write(fd, val, strlen(val));
        close(fd);
-       if (n != strlen(val))
+       if (n != strlen(val)) {
+               dprintf(Name ": failed to write '%s' to '%s' (%s)\n",
+                       val, fname, strerror(errno));
                return -1;
+       }
        return 0;
 }
 
@@ -408,28 +441,41 @@ int sysfs_set_safemode(struct mdinfo *sra, unsigned long ms)
        char delay[30];
 
        sec = ms / 1000;
-       msec = ms - (sec * 1000);
+       msec = ms % 1000;
 
-       sprintf(delay, "%ld.%ld", sec, msec);
+       sprintf(delay, "%ld.%03ld\n", sec, msec);
+       /*             this '\n' ^ needed for kernels older than 2.6.28 */
        return sysfs_set_str(sra, NULL, "safe_mode_delay", delay);
 }
 
-int sysfs_set_array(struct mdinfo *sra,
-                   struct mdinfo *info)
+int sysfs_set_array(struct mdinfo *info, int vers)
 {
        int rv = 0;
-       sra->array = info->array;
-
+       char ver[100];
+
+       ver[0] = 0;
+       if (info->array.major_version == -1 &&
+           info->array.minor_version == -2) {
+               strcat(strcpy(ver, "external:"), info->text_version);
+
+               if ((vers % 100) < 2 ||
+                   sysfs_set_str(info, NULL, "metadata_version",
+                                 ver) < 0) {
+                       fprintf(stderr, Name ": This kernel does not "
+                               "support external metadata.\n");
+                       return 1;
+               }
+       }
        if (info->array.level < 0)
                return 0; /* FIXME */
-       rv |= sysfs_set_str(sra, NULL, "level",
+       rv |= sysfs_set_str(info, NULL, "level",
                            map_num(pers, info->array.level));
-       rv |= sysfs_set_num(sra, NULL, "raid_disks", info->array.raid_disks);
-       rv |= sysfs_set_num(sra, NULL, "chunk_size", info->array.chunk_size);
-       rv |= sysfs_set_num(sra, NULL, "layout", info->array.layout);
-       rv |= sysfs_set_num(sra, NULL, "component_size", info->component_size/2);
-       rv |= sysfs_set_num(sra, NULL, "resync_start", info->resync_start);
-       sra->array = info->array;
+       rv |= sysfs_set_num(info, NULL, "raid_disks", info->array.raid_disks);
+       rv |= sysfs_set_num(info, NULL, "chunk_size", info->array.chunk_size);
+       rv |= sysfs_set_num(info, NULL, "layout", info->array.layout);
+       rv |= sysfs_set_num(info, NULL, "component_size", info->component_size/2);
+       if (info->array.level > 0)
+               rv |= sysfs_set_num(info, NULL, "resync_start", info->resync_start);
        return rv;
 }
 
@@ -437,7 +483,6 @@ int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd)
 {
        char dv[100];
        char nm[100];
-       struct mdinfo *sd2;
        char *dname;
        int rv;
 
@@ -463,12 +508,6 @@ int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd)
                rv |= sysfs_set_num(sra, sd, "slot", sd->disk.raid_disk);
 //             rv |= sysfs_set_str(sra, sd, "state", "in_sync");
        }
-       if (! rv) {
-               sd2 = malloc(sizeof(*sd2));
-               *sd2 = *sd;
-               sd2->next = sra->devs;
-               sra->devs = sd2;
-       }
        return rv;
 }