/* * sysfs - extract md related information from sysfs. Part of: * mdadm - manage Linux "md" devices aka RAID arrays. * * Copyright (C) 2006 Neil Brown * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Neil Brown * Email: */ #include "mdadm.h" #include int load_sys(char *path, char *buf) { int fd = open(path, O_RDONLY); int n; if (fd < 0) return -1; n = read(fd, buf, 1024); close(fd); if (n <=0 || n >= 1024) return -1; buf[n] = 0; if (buf[n-1] == '\n') buf[n-1] = 0; return 0; } struct sysarray *sysfs_read(int fd, int devnum, unsigned long options) { /* Longest possible name in sysfs, mounted at /sys, is * /sys/block/md_dXXX/md/dev-XXXXX/block/dev * /sys/block/md_dXXX/md/metadata_version * which is about 41 characters. 50 should do for now */ char fname[50]; char buf[1024]; char *base; char *dbase; struct sysarray *sra; struct sysdev *dev; DIR *dir; struct dirent *de; sra = malloc(sizeof(*sra)); if (sra == NULL) return sra; if (fd >= 0) { struct stat stb; if (fstat(fd, &stb)) return NULL; if (major(stb.st_rdev)==9) sprintf(sra->name, "md%d", minor(stb.st_rdev)); else sprintf(sra->name, "md_d%d", minor(stb.st_rdev)/16); } else { if (devnum >= 0) sprintf(sra->name, "md%d", devnum); else sprintf(sra->name, "md_d%d", -1-devnum); } sprintf(fname, "/sys/block/%s/md/", sra->name); base = fname + strlen(fname); sra->devs = NULL; if (options & GET_LEVEL) { strcpy(base, "level"); if (load_sys(fname, buf)) goto abort; sra->level = map_name(pers, buf); } if (options & GET_LAYOUT) { strcpy(base, "layout"); if (load_sys(fname, buf)) goto abort; sra->layout = strtoul(buf, NULL, 0); } if (options & GET_COMPONENT) { strcpy(base, "component_size"); if (load_sys(fname, buf)) goto abort; sra->component_size = strtoull(buf, NULL, 0); /* sysfs reports "K", but we want sectors */ sra->component_size *= 2; } if (options & GET_CHUNK) { strcpy(base, "chunk_size"); if (load_sys(fname, buf)) goto abort; sra->chunk = strtoul(buf, NULL, 0); } if (! (options & GET_DEVS)) return sra; /* Get all the devices as well */ *base = 0; dir = opendir(fname); if (!dir) goto abort; sra->spares = 0; while ((de = readdir(dir)) != NULL) { char *ep; if (de->d_ino == 0 || strncmp(de->d_name, "dev-", 4) != 0) continue; strcpy(base, de->d_name); dbase = base + strlen(base); *dbase++ = '/'; dev = malloc(sizeof(*dev)); if (!dev) goto abort; dev->next = sra->devs; sra->devs = dev; /* Always get slot, major, minor */ strcpy(dbase, "slot"); if (load_sys(fname, buf)) goto abort; dev->role = strtoul(buf, &ep, 10); if (*ep) dev->role = -1; strcpy(dbase, "block/dev"); if (load_sys(fname, buf)) goto abort; sscanf(buf, "%d:%d", &dev->major, &dev->minor); if (options & GET_OFFSET) { strcpy(dbase, "offset"); if (load_sys(fname, buf)) goto abort; dev->offset = strtoull(buf, NULL, 0); } if (options & GET_SIZE) { strcpy(dbase, "size"); if (load_sys(fname, buf)) goto abort; dev->size = strtoull(buf, NULL, 0); } if (options & GET_STATE) { dev->state = 0; strcpy(dbase, "state"); if (load_sys(fname, buf)) goto abort; if (strstr(buf, "in_sync")) dev->state |= (1<state |= (1<state == 0) sra->spares++; } if (options & GET_ERROR) { strcpy(buf, "errors"); if (load_sys(fname, buf)) goto abort; dev->errors = strtoul(buf, NULL, 0); } } return sra; abort: while (sra && sra->devs) { dev = sra->devs; sra->devs = dev->next; free(dev); } if(sra) free(sra); return NULL; } unsigned long long get_component_size(int fd) { /* Find out the component size of the array. * We cannot trust GET_ARRAY_INFO ioctl as it's * size field is only 32bits. * So look in /sys/block/mdXXX/md/component_size * * WARNING: this returns in units of Kilobytes. */ struct stat stb; char fname[50]; int n; 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)); else sprintf(fname, "/sys/block/md_d%d/md/component_size", minor(stb.st_rdev)/16); fd = open(fname, O_RDONLY); if (fd < 0) return 0; n = read(fd, fname, sizeof(fname)); close(fd); if (n == sizeof(fname)) return 0; fname[n] = 0; return strtoull(fname, NULL, 10); } int sysfs_set_str(struct sysarray *sra, struct sysdev *dev, char *name, char *val) { char fname[50]; int n; int fd; sprintf(fname, "/sys/block/%s/md/%s/%s", sra->name, dev?dev->name:"", name); fd = open(fname, O_WRONLY); if (fd < 0) return -1; n = write(fd, val, strlen(val)); close(fd); if (n != strlen(val)) return -1; return 0; } int sysfs_set_num(struct sysarray *sra, struct sysdev *dev, char *name, unsigned long long val) { char valstr[50]; sprintf(valstr, "%llu", val); return sysfs_set_str(sra, dev, name, valstr); } int sysfs_get_ll(struct sysarray *sra, struct sysdev *dev, char *name, unsigned long long *val) { char fname[50]; char buf[50]; int n; int fd; char *ep; sprintf(fname, "/sys/block/%s/md/%s/%s", sra->name, dev?dev->name:"", name); fd = open(fname, O_RDONLY); if (fd < 0) return -1; n = read(fd, buf, sizeof(buf)); close(fd); if (n <= 0) return -1; buf[n] = 0; *val = strtoull(buf, &ep, 0); if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' ')) return -1; return 0; }