X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=sysfs.c;h=ae10b1e96c9b186d81a7b9b233cacbcce572ca4b;hb=6adfd3affda071d1083497d150a1150ade0e3fdb;hp=f198d1646e4930d33f70173d646d33ec900c5bfc;hpb=7e0f69790c47b21c4aa7636a4b05925687c80a6e;p=thirdparty%2Fmdadm.git diff --git a/sysfs.c b/sysfs.c index f198d164..ae10b1e9 100644 --- a/sysfs.c +++ b/sysfs.c @@ -34,7 +34,7 @@ int load_sys(char *path, char *buf) return -1; n = read(fd, buf, 1024); close(fd); - if (n <=0 || n >= 1024) + if (n <0 || n >= 1024) return -1; buf[n] = 0; if (buf[n-1] == '\n') @@ -56,6 +56,29 @@ void sysfs_free(struct mdinfo *sra) } } +int sysfs_open(int devnum, char *devname, char *attr) +{ + char fname[50]; + char sys_name[16]; + int fd; + if (devnum >= 0) + sprintf(sys_name, "md%d", devnum); + else + sprintf(sys_name, "md_d%d", + -1-devnum); + + sprintf(fname, "/sys/block/%s/md/", sys_name); + if (devname) { + strcat(fname, devname); + strcat(fname, "/"); + } + strcat(fname, attr); + fd = open(fname, O_RDWR); + if (fd < 0 && errno == EACCES) + fd = open(fname, O_RDONLY); + return fd; +} + struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options) { /* Longest possible name in sysfs, mounted at /sys, is @@ -69,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)); @@ -84,10 +107,10 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options) if (ioctl(fd, RAID_VERSION, &vers) != 0) return NULL; if (major(stb.st_rdev)==9) - sprintf(sra->sys_name, "md%d", minor(stb.st_rdev)); + sprintf(sra->sys_name, "md%d", (int)minor(stb.st_rdev)); else sprintf(sra->sys_name, "md_d%d", - minor(stb.st_rdev)>>MdpMinorShift); + (int)minor(stb.st_rdev)>>MdpMinorShift); } else { if (devnum >= 0) sprintf(sra->sys_name, "md%d", devnum); @@ -103,13 +126,20 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options) strcpy(base, "metadata_version"); if (load_sys(fname, buf)) goto abort; - if (strncmp(buf, "none", 4) == 0) + if (strncmp(buf, "none", 4) == 0) { sra->array.major_version = sra->array.minor_version = -1; - else + strcpy(sra->text_version, ""); + } else if (strncmp(buf, "external:", 9) == 0) { + sra->array.major_version = -1; + sra->array.minor_version = -2; + strcpy(sra->text_version, buf+9); + } 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"); @@ -123,6 +153,12 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options) goto abort; sra->array.layout = strtoul(buf, NULL, 0); } + if (options & GET_DISKS) { + strcpy(base, "raid_disks"); + if (load_sys(fname, buf)) + goto abort; + sra->array.raid_disks = strtoul(buf, NULL, 0); + } if (options & GET_COMPONENT) { strcpy(base, "component_size"); if (load_sys(fname, buf)) @@ -219,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; } @@ -241,10 +280,10 @@ unsigned long long get_component_size(int fd) if (fstat(fd, &stb)) return 0; if (major(stb.st_rdev) == 9) sprintf(fname, "/sys/block/md%d/md/component_size", - minor(stb.st_rdev)); + (int)minor(stb.st_rdev)); else sprintf(fname, "/sys/block/md_d%d/md/component_size", - minor(stb.st_rdev)>>MdpMinorShift); + (int)minor(stb.st_rdev)>>MdpMinorShift); fd = open(fname, O_RDONLY); if (fd < 0) return 0; @@ -262,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); @@ -305,3 +345,173 @@ int sysfs_get_ll(struct mdinfo *sra, struct mdinfo *dev, return -1; return 0; } + +int sysfs_set_array(struct mdinfo *sra, + struct mdinfo *info) +{ + int rv = 0; + sra->array = info->array; + + if (info->array.level < 0) + return 0; /* FIXME */ + rv |= sysfs_set_str(sra, 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); + rv |= sysfs_set_num(sra, NULL, "resync_start", info->resync_start); + sra->array = info->array; + return rv; +} + +int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd) +{ + char dv[100]; + char nm[100]; + struct mdinfo *sd2; + char *dname; + int rv; + + sprintf(dv, "%d:%d", sd->disk.major, sd->disk.minor); + rv = sysfs_set_str(sra, NULL, "new_dev", dv); + if (rv) + return rv; + + memset(nm, 0, sizeof(nm)); + sprintf(dv, "/sys/dev/block/%d:%d", sd->disk.major, sd->disk.minor); + if (readlink(dv, nm, sizeof(nm)) < 0) + return -1; + dname = strrchr(nm, '/'); + if (dname) dname++; + strcpy(sd->sys_name, "dev-"); + strcpy(sd->sys_name+4, dname); + + rv |= sysfs_set_num(sra, sd, "offset", sd->data_offset); + rv |= sysfs_set_num(sra, sd, "size", (sd->component_size+1) / 2); + if (sra->array.level != LEVEL_CONTAINER) { + 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; +} + +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; +}