]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - sysfs.c
Add some comments to explain some of the bits of superswitch.
[thirdparty/mdadm.git] / sysfs.c
diff --git a/sysfs.c b/sysfs.c
index 34840f7698c7d4c0998d7f705b24c28d03b3ffc6..ae10b1e96c9b186d81a7b9b233cacbcce572ca4b 100644 (file)
--- a/sysfs.c
+++ b/sysfs.c
@@ -74,7 +74,7 @@ int sysfs_open(int devnum, char *devname, char *attr)
        }
        strcat(fname, attr);
        fd = open(fname, O_RDWR);
-       if (fd < 0 && errno == -EACCES)
+       if (fd < 0 && errno == EACCES)
                fd = open(fname, O_RDONLY);
        return fd;
 }
@@ -92,7 +92,7 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
        char *dbase;
        struct mdinfo *sra;
        struct mdinfo *dev;
-       DIR *dir;
+       DIR *dir = NULL;
        struct dirent *de;
 
        sra = malloc(sizeof(*sra));
@@ -134,10 +134,12 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
                        sra->array.major_version = -1;
                        sra->array.minor_version = -2;
                        strcpy(sra->text_version, buf+9);
-               } else
+               } else {
                        sscanf(buf, "%d.%d",
                               &sra->array.major_version,
                               &sra->array.minor_version);
+                       strcpy(sra->text_version, buf);
+               }
        }
        if (options & GET_LEVEL) {
                strcpy(base, "level");
@@ -253,9 +255,12 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
                        dev->errors = strtoul(buf, NULL, 0);
                }
        }
+       closedir(dir);
        return sra;
 
  abort:
+       if (dir)
+               closedir(dir);
        sysfs_free(sra);
        return NULL;
 }
@@ -296,6 +301,7 @@ int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev,
        char fname[50];
        int n;
        int fd;
+
        sprintf(fname, "/sys/block/%s/md/%s/%s",
                sra->sys_name, dev?dev->sys_name:"", name);
        fd = open(fname, O_WRONLY);
@@ -359,7 +365,7 @@ int sysfs_set_array(struct mdinfo *sra,
        return rv;
 }
 
-int sysfs_add_disk(struct mdinfo *sra, int fd, struct mdinfo *sd)
+int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd)
 {
        char dv[100];
        char nm[100];
@@ -387,10 +393,125 @@ int sysfs_add_disk(struct mdinfo *sra, int fd, struct mdinfo *sd)
                rv |= sysfs_set_num(sra, sd, "slot", sd->disk.raid_disk);
 //             rv |= sysfs_set_str(sra, sd, "state", "in_sync");
        }
-       sd2 = malloc(sizeof(*sd2));
-       *sd2 = *sd;
-       sd2->next = sra->devs;
-       sra->devs = sd2;
-
+       if (! rv) {
+               sd2 = malloc(sizeof(*sd2));
+               *sd2 = *sd;
+               sd2->next = sra->devs;
+               sra->devs = sd2;
+       }
        return rv;
 }
+
+int sysfs_disk_to_sg(int fd)
+{
+       /* from an open block device, try find and open its corresponding
+        * scsi_generic interface
+        */
+       struct stat st;
+       char path[256];
+       char sg_path[256];
+       char sg_major_minor[8];
+       char *c;
+       DIR *dir;
+       struct dirent *de;
+       int major, minor, rv;
+
+       if (fstat(fd, &st))
+               return -1;
+
+       snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/device",
+                major(st.st_rdev), minor(st.st_rdev));
+
+       dir = opendir(path);
+       if (!dir)
+               return -1;
+
+       de = readdir(dir);
+       while (de) {
+               if (strncmp("scsi_generic:", de->d_name,
+                           strlen("scsi_generic:")) == 0)
+                       break;
+               de = readdir(dir);
+       }
+       closedir(dir);
+
+       if (!de)
+               return -1;
+
+       snprintf(sg_path, sizeof(sg_path), "%s/%s/dev", path, de->d_name);
+       fd = open(sg_path, O_RDONLY);
+       if (fd < 0)
+               return fd;
+
+       rv = read(fd, sg_major_minor, sizeof(sg_major_minor));
+       close(fd);
+       if (rv < 0)
+               return -1;
+       else
+               sg_major_minor[rv - 1] = '\0';
+
+       c = strchr(sg_major_minor, ':');
+       *c = '\0';
+       c++;
+       major = strtol(sg_major_minor, NULL, 10);
+       minor = strtol(c, NULL, 10);
+       snprintf(path, sizeof(path), "/dev/.tmp.md.%d:%d:%d",
+                (int) getpid(), major, minor);
+       if (mknod(path, S_IFCHR|0600, makedev(major, minor))==0) {
+                       fd = open(path, O_RDONLY);
+                       unlink(path);
+                       return fd;
+       }
+
+       return -1;
+}
+
+int sysfs_disk_to_scsi_id(int fd, __u32 *id)
+{
+       /* from an open block device, try to retrieve it scsi_id */
+       struct stat st;
+       char path[256];
+       char *c1, *c2;
+       DIR *dir;
+       struct dirent *de;
+
+       if (fstat(fd, &st))
+               return 1;
+
+       snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/device",
+                major(st.st_rdev), minor(st.st_rdev));
+
+       dir = opendir(path);
+       if (!dir)
+               return 1;
+
+       de = readdir(dir);
+       while (de) {
+               if (strncmp("scsi_disk:", de->d_name,
+                           strlen("scsi_disk:")) == 0)
+                       break;
+               de = readdir(dir);
+       }
+       closedir(dir);
+
+       if (!de)
+               return 1;
+
+       c1 = strchr(de->d_name, ':');
+       c1++;
+       c2 = strchr(c1, ':');
+       *c2 = '\0';
+       *id = strtol(c1, NULL, 10) << 24; /* host */
+       c1 = c2 + 1;
+       c2 = strchr(c1, ':');
+       *c2 = '\0';
+       *id |= strtol(c1, NULL, 10) << 16; /* channel */
+       c1 = c2 + 1;
+       c2 = strchr(c1, ':');
+       *c2 = '\0';
+       *id |= strtol(c1, NULL, 10) << 8; /* lun */
+       c1 = c2 + 1;
+       *id |= strtol(c1, NULL, 10); /* id */
+
+       return 0;
+}