コード例 #1
0
ファイル: resizepart.c プロジェクト: stancheff/util-linux
static int get_partition_start(int fd, int partno, uint64_t *start)
{
	struct stat st;
	struct sysfs_cxt disk = UL_SYSFSCXT_EMPTY,
			 part = UL_SYSFSCXT_EMPTY;
	dev_t devno = 0;
	int rc = -1;

	/*
	 * wholedisk
	 */
	if (fstat(fd, &st) || !S_ISBLK(st.st_mode))
		goto done;
	devno = st.st_rdev;
	if (sysfs_init(&disk, devno, NULL))
		goto done;
	/*
	 * partition
	 */
	devno = sysfs_partno_to_devno(&disk, partno);
	if (!devno)
		goto done;
	if (sysfs_init(&part, devno, &disk))
		goto done;
	if (sysfs_read_u64(&part, "start", start))
		goto done;

	rc = 0;
done:
	sysfs_deinit(&part);
	sysfs_deinit(&disk);
	return rc;
}
コード例 #2
0
ファイル: fstrim.c プロジェクト: apprisi/util-linux
static int has_discard(const char *devname, struct sysfs_cxt *wholedisk)
{
	struct sysfs_cxt cxt, *parent = NULL;
	uint64_t dg = 0;
	dev_t disk = 0, dev;
	int rc;

	dev = sysfs_devname_to_devno(devname, NULL);
	if (!dev)
		return 1;
	/*
	 * This is tricky to read the info from sys/, because the queue
	 * atrributes are provided for whole devices (disk) only. We're trying
	 * to reuse the whole-disk sysfs context to optimize this stuff (as
	 * system usually have just one disk only).
	 */
	if (sysfs_devno_to_wholedisk(dev, NULL, 0, &disk) || !disk)
		return 1;
	if (dev != disk) {
		if (wholedisk->devno != disk) {
			sysfs_deinit(wholedisk);
			if (sysfs_init(wholedisk, disk, NULL))
				return 1;
		}
		parent = wholedisk;
	}

	rc = sysfs_init(&cxt, dev, parent);
	if (!rc)
		rc = sysfs_read_u64(&cxt, "queue/discard_granularity", &dg);

	sysfs_deinit(&cxt);
	return rc == 0 && dg > 0;
}
コード例 #3
0
ファイル: sysfs.c プロジェクト: huawenyu/util-linux
/*
 * Returns 1 if the device is private LVM device.
 */
int sysfs_devno_is_lvm_private(dev_t devno)
{
    struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
    char *uuid = NULL;
    int rc = 0;

    if (sysfs_init(&cxt, devno, NULL) != 0)
        return 0;

    uuid = sysfs_strdup(&cxt, "dm/uuid");

    /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
     * is the "LVM" prefix and "-<name>" postfix).
     */
    if (uuid && strncmp(uuid, "LVM-", 4) == 0) {
        char *p = strrchr(uuid + 4, '-');

        if (p && *(p + 1))
            rc = 1;
    }

    sysfs_deinit(&cxt);
    free(uuid);
    return rc;
}
コード例 #4
0
ファイル: sysfs.c プロジェクト: huawenyu/util-linux
/*
 * Returns devname (e.g. "/dev/sda1") for the given devno.
 *
 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
 * symlinks.
 *
 * Please, use more robust blkid_devno_to_devname() in your applications.
 */
char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz)
{
    struct sysfs_cxt cxt;
    char *name;
    size_t sz;
    struct stat st;

    if (sysfs_init(&cxt, devno, NULL))
        return NULL;

    name = sysfs_get_devname(&cxt, buf, bufsiz);
    sysfs_deinit(&cxt);

    if (!name)
        return NULL;

    sz = strlen(name);

    if (sz + sizeof("/dev/") > bufsiz)
        return NULL;

    /* create the final "/dev/<name>" string */
    memmove(buf + 5, name, sz + 1);
    memcpy(buf, "/dev/", 5);

    if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno)
        return buf;

    return NULL;
}
コード例 #5
0
ファイル: Monitor.c プロジェクト: Distrotech/mdadm
/* Not really Monitor but ... */
int Wait(char *dev)
{
	struct stat stb;
	char devnm[32];
	int rv = 1;
	int frozen_remaining = 3;

	if (stat(dev, &stb) != 0) {
		pr_err("Cannot find %s: %s\n", dev,
			strerror(errno));
		return 2;
	}
	strcpy(devnm, stat2devnm(&stb));

	while(1) {
		struct mdstat_ent *ms = mdstat_read(1, 0);
		struct mdstat_ent *e;

		for (e=ms ; e; e=e->next)
			if (strcmp(e->devnm, devnm) == 0)
				break;

		if (e && e->percent == RESYNC_NONE) {
			/* We could be in the brief pause before something
			 * starts. /proc/mdstat doesn't show that, but
			 * sync_action does.
			 */
			struct mdinfo mdi;
			char buf[21];
			sysfs_init(&mdi, -1, devnm);
			if (sysfs_get_str(&mdi, NULL, "sync_action",
					  buf, 20) > 0 &&
			    strcmp(buf,"idle\n") != 0) {
				e->percent = RESYNC_UNKNOWN;
				if (strcmp(buf, "frozen\n") == 0) {
					if (frozen_remaining == 0)
						e->percent = RESYNC_NONE;
					else
						frozen_remaining -= 1;
				}
			}
		}
		if (!e || e->percent == RESYNC_NONE) {
			if (e && e->metadata_version &&
			    strncmp(e->metadata_version, "external:", 9) == 0) {
				if (is_subarray(&e->metadata_version[9]))
					ping_monitor(&e->metadata_version[9]);
				else
					ping_monitor(devnm);
			}
			free_mdstat(ms);
			return rv;
		}
		free_mdstat(ms);
		rv = 0;
		mdstat_wait(5);
	}
}
コード例 #6
0
ファイル: sysfs.c プロジェクト: rseabra/util-linux
int main(int argc, char *argv[])
{
    struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
    char *devname;
    dev_t devno;
    char path[PATH_MAX];
    int i;
    uint64_t u64;
    ssize_t len;

    if (argc != 2)
        errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);

    devname = argv[1];
    devno = sysfs_devname_to_devno(devname, NULL);

    if (!devno)
        err(EXIT_FAILURE, "failed to read devno");

    printf("NAME: %s\n", devname);
    printf("DEVNO: %u\n", (unsigned int) devno);
    printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path)));
    printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
    printf("PARTITION: %s\n",
           sysfs_devno_has_attribute(devno, "partition") ? "YES" : "NOT");

    if (sysfs_init(&cxt, devno, NULL))
        return EXIT_FAILURE;

    len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1);
    if (len > 0) {
        path[len] = '\0';
        printf("DEVNOLINK: %s\n", path);
    }

    printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves"));

    if (sysfs_read_u64(&cxt, "size", &u64))
        printf("read SIZE failed\n");
    else
        printf("SIZE: %jd\n", u64);

    if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i))
        printf("read SECTOR failed\n");
    else
        printf("SECTOR: %d\n", i);

    printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path)));

    sysfs_deinit(&cxt);
    return EXIT_SUCCESS;
}
コード例 #7
0
ファイル: eject.c プロジェクト: ArakniD/util-linux
static int is_hotpluggable(const struct eject_control *ctl)
{
	struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
	dev_t devno;
	int rc = 0;

	devno = sysfs_devname_to_devno(ctl->device, NULL);
	if (sysfs_init(&cxt, devno, NULL) != 0)
		return 0;

	rc = sysfs_is_hotpluggable(&cxt);

	sysfs_deinit(&cxt);
	return rc;
}
コード例 #8
0
ファイル: eject.c プロジェクト: Berrrry/util-linux
static int is_hotpluggable(const char* device)
{
	struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
	char devchain[PATH_MAX];
	char subbuf[PATH_MAX];
	dev_t devno;
	int rc = 0;
	ssize_t sz;
	char *sub;

	devno = sysfs_devname_to_devno(device, NULL);
	if (sysfs_init(&cxt, devno, NULL) != 0)
		return 0;

	/* check /sys/dev/block/<maj>:<min>/removable attribute */
	if (sysfs_read_int(&cxt, "removable", &rc) == 0 && rc == 1) {
		verbose(_("%s: is removable device"), device);
		goto done;
	}

	/* read /sys/dev/block/<maj>:<min> symlink */
	sz = sysfs_readlink(&cxt, NULL, devchain, sizeof(devchain));
	if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > sizeof(devchain))
		goto done;

	devchain[sz++] = '\0';

	/* create absolute patch from the link */
	memmove(devchain + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, devchain, sz);
	memcpy(devchain, _PATH_SYS_DEVBLOCK "/",
	       sizeof(_PATH_SYS_DEVBLOCK "/") - 1);

	while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) {
		rc = is_hotpluggable_subsystem(sub);
		if (rc) {
			verbose(_("%s: connected by hotplug subsystem: %s"),
				device, sub);
			break;
		}
	}

done:
	sysfs_deinit(&cxt);
	return rc;
}
コード例 #9
0
ファイル: eject.c プロジェクト: Berrrry/util-linux
static int umount_partitions(const char *disk, int checkonly)
{
	struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
	dev_t devno;
	DIR *dir = NULL;
	struct dirent *d;
	int count = 0;

	devno = sysfs_devname_to_devno(disk, NULL);
	if (sysfs_init(&cxt, devno, NULL) != 0)
		return 0;

	/* open /sys/block/<wholedisk> */
	if (!(dir = sysfs_opendir(&cxt, NULL)))
		goto done;

	/* scan for partition subdirs */
	while ((d = readdir(dir))) {
		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
			continue;

		if (sysfs_is_partition_dirent(dir, d, disk)) {
			char *mnt = NULL;
			char *dev = find_device(d->d_name);

			if (dev && device_get_mountpoint(&dev, &mnt) == 0) {
				verbose(_("%s: mounted on %s"), dev, mnt);
				if (!checkonly)
					umount_one(mnt);
				count++;
			}
			free(dev);
			free(mnt);
		}
	}

done:
	if (dir)
		closedir(dir);
	sysfs_deinit(&cxt);

	return count;
}
コード例 #10
0
ファイル: loopdev.c プロジェクト: SpoilForHawkeye/mount
/*
 * @lc: context
 *
 * Returns pointer to the sysfs context (see lib/sysfs.c)
 */
struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc)
{
	if (!lc || !*lc->device || (lc->flags & LOOPDEV_FL_NOSYSFS))
		return NULL;

	if (!lc->sysfs.devno) {
		dev_t devno = sysfs_devname_to_devno(lc->device, NULL);
		if (!devno) {
			DBG(lc, loopdev_debug("sysfs: failed devname to devno"));
			return NULL;
		}
		if (sysfs_init(&lc->sysfs, devno, NULL)) {
			DBG(lc, loopdev_debug("sysfs: init failed"));
			return NULL;
		}
	}

	return &lc->sysfs;
}
コード例 #11
0
ファイル: sysfs.c プロジェクト: huawenyu/util-linux
/*
 * Returns by @diskdevno whole disk device devno and (optionaly) by
 * @diskname the whole disk device name.
 */
int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
                             size_t len, dev_t *diskdevno)
{
    struct sysfs_cxt cxt;
    int is_part = 0;

    if (!dev || sysfs_init(&cxt, dev, NULL) != 0)
        return -1;

    is_part = sysfs_has_attribute(&cxt, "partition");
    if (!is_part) {
        /*
         * Extra case for partitions mapped by device-mapper.
         *
         * All regualar partitions (added by BLKPG ioctl or kernel PT
         * parser) have the /sys/.../partition file. The partitions
         * mapped by DM don't have such file, but they have "part"
         * prefix in DM UUID.
         */
        char *uuid = sysfs_strdup(&cxt, "dm/uuid");
        char *tmp = uuid;
        char *prefix = uuid ? strsep(&tmp, "-") : NULL;

        if (prefix && strncasecmp(prefix, "part", 4) == 0)
            is_part = 1;
        free(uuid);

        if (is_part &&
                get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0)
            /*
             * partitioned device, mapped by DM
             */
            goto done;

        is_part = 0;
    }

    if (!is_part) {
        /*
         * unpartitioned device
         */
        if (diskname && len) {
            if (!sysfs_get_devname(&cxt, diskname, len))
                goto err;
        }
        if (diskdevno)
            *diskdevno = dev;

    } else {
        /*
         * partitioned device
         *  - readlink /sys/dev/block/8:1   = ../../block/sda/sda1
         *  - dirname  ../../block/sda/sda1 = ../../block/sda
         *  - basename ../../block/sda      = sda
         */
        char linkpath[PATH_MAX];
        char *name;
        int linklen;

        linklen = sysfs_readlink(&cxt, NULL,
                                 linkpath, sizeof(linkpath) - 1);
        if (linklen < 0)
            goto err;
        linkpath[linklen] = '\0';

        stripoff_last_component(linkpath);      /* dirname */
        name = stripoff_last_component(linkpath);   /* basename */
        if (!name)
            goto err;

        if (diskname && len) {
            strncpy(diskname, name, len);
            diskname[len - 1] = '\0';
        }

        if (diskdevno) {
            *diskdevno = sysfs_devname_to_devno(name, NULL);
            if (!*diskdevno)
                goto err;
        }
    }

done:
    sysfs_deinit(&cxt);
    return 0;
err:
    sysfs_deinit(&cxt);
    return -1;
}
コード例 #12
0
ファイル: udevsettle.c プロジェクト: AllardJ/Tomato
int main(int argc, char *argv[], char *envp[])
{
	char queuename[PATH_SIZE];
	char filename[PATH_SIZE];
	unsigned long long seq_kernel;
	unsigned long long seq_udev;
	char seqnum[32];
	int fd;
	ssize_t len;
	int timeout = DEFAULT_TIMEOUT;
	int loop;
	static const struct option options[] = {
		{ "timeout", 1, NULL, 't' },
		{ "help", 0, NULL, 'h' },
		{}
	};
	int option;
	int rc = 1;
	int seconds;

	logging_init("udevsettle");
	udev_config_init();
	dbg("version %s", UDEV_VERSION);
	sysfs_init();

	while (1) {
		option = getopt_long(argc, argv, "t:h", options, NULL);
		if (option == -1)
			break;

		switch (option) {
		case 't':
			seconds = atoi(optarg);
			if (seconds > 0)
				timeout = seconds;
			else
				fprintf(stderr, "invalid timeout value\n");
			dbg("timeout=%i", timeout);
			break;
		case 'h':
			printf("Usage: udevsettle [--help] [--timeout=<seconds>]\n\n");
			goto exit;
		}
	}

	strlcpy(queuename, udev_root, sizeof(queuename));
	strlcat(queuename, "/" EVENT_QUEUE_DIR, sizeof(queuename));

	loop = timeout * LOOP_PER_SECOND;
	while (loop--) {
		/* wait for events in queue to finish */
		while (loop--) {
			struct stat statbuf;

			if (stat(queuename, &statbuf) < 0) {
				info("queue is empty");
				break;
			}
			usleep(1000 * 1000 / LOOP_PER_SECOND);
		}
		if (loop <= 0) {
			info("timeout waiting for queue");
			goto exit;
		}

		/* read current udev seqnum */
		strlcpy(filename, udev_root, sizeof(filename));
		strlcat(filename, "/" EVENT_SEQNUM, sizeof(filename));
		fd = open(filename, O_RDONLY);
		if (fd < 0)
			goto exit;
		len = read(fd, seqnum, sizeof(seqnum)-1);
		close(fd);
		if (len <= 0)
			goto exit;
		seqnum[len] = '\0';
		seq_udev = strtoull(seqnum, NULL, 10);
		info("udev seqnum = %llu", seq_udev);

		/* read current kernel seqnum */
		strlcpy(filename, sysfs_path, sizeof(filename));
		strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename));
		fd = open(filename, O_RDONLY);
		if (fd < 0)
			goto exit;
		len = read(fd, seqnum, sizeof(seqnum)-1);
		close(fd);
		if (len <= 0)
			goto exit;
		seqnum[len] = '\0';
		seq_kernel = strtoull(seqnum, NULL, 10);
		info("kernel seqnum = %llu", seq_kernel);

		/* make sure all kernel events have arrived in the queue */
		if (seq_udev >= seq_kernel) {
			info("queue is empty and no pending events left");
			rc = 0;
			goto exit;
		}
		usleep(1000 * 1000 / LOOP_PER_SECOND);
		info("queue is empty, but events still pending");
	}

exit:
	sysfs_cleanup();
	logging_close();
	return rc;
}
コード例 #13
0
ファイル: sysfs.c プロジェクト: neilbrown/mdadm
struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
{
    char fname[PATH_MAX];
    char buf[PATH_MAX];
    char *base;
    char *dbase;
    struct mdinfo *sra;
    struct mdinfo *dev, **devp;
    DIR *dir = NULL;
    struct dirent *de;

    sra = xcalloc(1, sizeof(*sra));
    sysfs_init(sra, fd, devnm);
    if (sra->sys_name[0] == 0) {
        free(sra);
        return NULL;
    }

    sprintf(fname, "/sys/block/%s/md/", sra->sys_name);
    base = fname + strlen(fname);

    sra->devs = NULL;
    if (options & GET_VERSION) {
        strcpy(base, "metadata_version");
        if (load_sys(fname, buf, sizeof(buf)))
            goto abort;
        if (strncmp(buf, "none", 4) == 0) {
            sra->array.major_version =
                sra->array.minor_version = -1;
            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");
        if (load_sys(fname, buf, sizeof(buf)))
            goto abort;
        sra->array.level = map_name(pers, buf);
    }
    if (options & GET_LAYOUT) {
        strcpy(base, "layout");
        if (load_sys(fname, buf, sizeof(buf)))
            goto abort;
        sra->array.layout = strtoul(buf, NULL, 0);
    }
    if (options & GET_DISKS) {
        strcpy(base, "raid_disks");
        if (load_sys(fname, buf, sizeof(buf)))
            goto abort;
        sra->array.raid_disks = strtoul(buf, NULL, 0);
    }
    if (options & GET_DEGRADED) {
        strcpy(base, "degraded");
        if (load_sys(fname, buf, sizeof(buf)))
            goto abort;
        sra->array.failed_disks = strtoul(buf, NULL, 0);
    }
    if (options & GET_COMPONENT) {
        strcpy(base, "component_size");
        if (load_sys(fname, buf, sizeof(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, sizeof(buf)))
            goto abort;
        sra->array.chunk_size = strtoul(buf, NULL, 0);
    }
    if (options & GET_CACHE) {
        strcpy(base, "stripe_cache_size");
        if (load_sys(fname, buf, sizeof(buf)))
            /* Probably level doesn't support it */
            sra->cache_size = 0;
        else
            sra->cache_size = strtoul(buf, NULL, 0);
    }
    if (options & GET_MISMATCH) {
        strcpy(base, "mismatch_cnt");
        if (load_sys(fname, buf, sizeof(buf)))
            goto abort;
        sra->mismatch_cnt = strtoul(buf, NULL, 0);
    }
    if (options & GET_SAFEMODE) {
        int scale = 1;
        int dot = 0;
        unsigned i;
        unsigned long msec;
        size_t len;

        strcpy(base, "safe_mode_delay");
        if (load_sys(fname, buf, sizeof(buf)))
            goto abort;

        /* remove a period, and count digits after it */
        len = strlen(buf);
        for (i = 0; i < len; i++) {
            if (dot) {
                if (isdigit(buf[i])) {
                    buf[i-1] = buf[i];
                    scale *= 10;
                }
                buf[i] = 0;
            } else if (buf[i] == '.') {
                dot=1;
                buf[i] = 0;
            }
        }
        msec = strtoul(buf, NULL, 10);
        msec = (msec * 1000) / scale;
        sra->safe_mode_delay = msec;
    }
    if (options & GET_BITMAP_LOCATION) {
        strcpy(base, "bitmap/location");
        if (load_sys(fname, buf, sizeof(buf)))
            goto abort;
        if (strncmp(buf, "file", 4) == 0)
            sra->bitmap_offset = 1;
        else if (strncmp(buf, "none", 4) == 0)
            sra->bitmap_offset = 0;
        else if (buf[0] == '+')
            sra->bitmap_offset = strtol(buf+1, NULL, 10);
        else
            goto abort;
    }

    if (options & GET_ARRAY_STATE) {
        strcpy(base, "array_state");
        if (load_sys(fname, sra->sysfs_array_state,
                     sizeof(sra->sysfs_array_state)))
            goto abort;
    } else
        sra->sysfs_array_state[0] = 0;

    if (! (options & GET_DEVS))
        return sra;

    /* Get all the devices as well */
    *base = 0;
    dir = opendir(fname);
    if (!dir)
        goto abort;
    sra->array.spare_disks = 0;

    devp = &sra->devs;
    sra->devs = NULL;
    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 = xcalloc(1, sizeof(*dev));

        /* Always get slot, major, minor */
        strcpy(dbase, "slot");
        if (load_sys(fname, buf, sizeof(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;
            }

        }
        strcpy(dev->sys_name, de->d_name);
        dev->disk.raid_disk = strtoul(buf, &ep, 10);
        if (*ep) dev->disk.raid_disk = -1;

        strcpy(dbase, "block/dev");
        if (load_sys(fname, buf, sizeof(buf))) {
            /* assume this is a stale reference to a hot
             * removed device
             */
            free(dev);
            continue;
        }
        sra->array.nr_disks++;
        sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor);

        /* special case check for block devices that can go 'offline' */
        strcpy(dbase, "block/device/state");
        if (load_sys(fname, buf, sizeof(buf)) == 0 &&
                strncmp(buf, "offline", 7) == 0) {
            free(dev);
            continue;
        }

        /* finally add this disk to the array */
        *devp = dev;
        devp = & dev->next;
        dev->next = NULL;

        if (options & GET_OFFSET) {
            strcpy(dbase, "offset");
            if (load_sys(fname, buf, sizeof(buf)))
                goto abort;
            dev->data_offset = strtoull(buf, NULL, 0);
            strcpy(dbase, "new_offset");
            if (load_sys(fname, buf, sizeof(buf)) == 0)
                dev->new_data_offset = strtoull(buf, NULL, 0);
            else
                dev->new_data_offset = dev->data_offset;
        }
        if (options & GET_SIZE) {
            strcpy(dbase, "size");
            if (load_sys(fname, buf, sizeof(buf)))
                goto abort;
            dev->component_size = strtoull(buf, NULL, 0) * 2;
        }
        if (options & GET_STATE) {
            dev->disk.state = 0;
            strcpy(dbase, "state");
            if (load_sys(fname, buf, sizeof(buf)))
                goto abort;
            if (strstr(buf, "in_sync"))
                dev->disk.state |= (1<<MD_DISK_SYNC);
            if (strstr(buf, "faulty"))
                dev->disk.state |= (1<<MD_DISK_FAULTY);
            if (dev->disk.state == 0)
                sra->array.spare_disks++;
        }
        if (options & GET_ERROR) {
            strcpy(buf, "errors");
            if (load_sys(fname, buf, sizeof(buf)))
                goto abort;
            dev->errors = strtoul(buf, NULL, 0);
        }
    }
    closedir(dir);
    return sra;

abort:
    if (dir)
        closedir(dir);
    sysfs_free(sra);
    return NULL;
}