From 5387c01517fef32144a17a52e284f8c97d773847 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 11 May 2018 14:28:03 +0200 Subject: lib/sysfs: new implementation * reuse ul_path_* API * allow to use prefix for sysfs paths, so we can use snapshots from sysfs for regression tests Signed-off-by: Karel Zak --- lib/Makemodule.am | 7 +- lib/sysfs.c | 972 +++++++++++++++++++++++++----------------------------- 2 files changed, 448 insertions(+), 531 deletions(-) (limited to 'lib') diff --git a/lib/Makemodule.am b/lib/Makemodule.am index 2081ac927..13b19f151 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -143,9 +143,12 @@ if LINUX test_cpuset_SOURCES = lib/cpuset.c test_cpuset_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_CPUSET -test_sysfs_SOURCES = lib/sysfs.c +test_sysfs_SOURCES = lib/sysfs.c lib/path.c lib/fileutils.c +if HAVE_CPU_SET_T +test_sysfs_SOURCES += lib/cpuset.c +endif test_sysfs_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_SYSFS -test_sysfs_LDADD = $(LDADD) libcommon.la +test_sysfs_LDADD = $(LDADD) test_pager_SOURCES = lib/pager.c test_pager_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_PAGER diff --git a/lib/sysfs.c b/lib/sysfs.c index e5437f43a..5b59952eb 100644 --- a/lib/sysfs.c +++ b/lib/sysfs.c @@ -16,291 +16,127 @@ #include "fileutils.h" #include "all-io.h" -char *sysfs_devno_attribute_path(dev_t devno, char *buf, - size_t bufsiz, const char *attr) -{ - int len; - - if (attr) - len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d/%s", - major(devno), minor(devno), attr); - else - len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d", - major(devno), minor(devno)); - - return (len < 0 || (size_t) len >= bufsiz) ? NULL : buf; -} - -int sysfs_devno_has_attribute(dev_t devno, const char *attr) -{ - char path[PATH_MAX]; - struct stat info; - - if (!sysfs_devno_attribute_path(devno, path, sizeof(path), attr)) - return 0; - if (stat(path, &info) == 0) - return 1; - return 0; -} - -char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz) -{ - return sysfs_devno_attribute_path(devno, buf, bufsiz, NULL); -} - -static dev_t read_devno(const char *path) -{ - FILE *f; - int maj = 0, min = 0; - dev_t dev = 0; - - f = fopen(path, "r" UL_CLOEXECSTR); - if (!f) - return 0; - - if (fscanf(f, "%d:%d", &maj, &min) == 2) - dev = makedev(maj, min); - fclose(f); - return dev; -} - -dev_t sysfs_devname_to_devno(const char *name, const char *parent) -{ - char buf[PATH_MAX]; - char *_name = NULL; /* name as encoded in sysfs */ - dev_t dev = 0; - int len; - - if (strncmp("/dev/", name, 5) == 0) { - /* - * Read from /dev - */ - struct stat st; - - if (stat(name, &st) == 0) { - dev = st.st_rdev; - goto done; - } - name += 5; /* unaccesible, or not node in /dev */ - } - - _name = strdup(name); - if (!_name) - goto done; - sysfs_devname_dev_to_sys(_name); - - if (parent && strncmp("dm-", name, 3)) { - /* - * Create path to /sys/block///dev - */ - char *_parent = strdup(parent); - - if (!_parent) { - free(_parent); - goto done; - } - sysfs_devname_dev_to_sys(_parent); - - len = snprintf(buf, sizeof(buf), - _PATH_SYS_BLOCK "/%s/%s/dev", _parent, _name); - free(_parent); - if (len < 0 || (size_t) len >= sizeof(buf)) - goto done; - - /* don't try anything else for dm-* */ - dev = read_devno(buf); - goto done; - } - - /* - * Read from /sys/block//dev - */ - len = snprintf(buf, sizeof(buf), - _PATH_SYS_BLOCK "/%s/dev", _name); - if (len < 0 || (size_t) len >= sizeof(buf)) - goto done; - dev = read_devno(buf); - - if (!dev) { - /* - * Read from /sys/block//device/dev - */ - len = snprintf(buf, sizeof(buf), - _PATH_SYS_BLOCK "/%s/device/dev", _name); - if (len < 0 || (size_t) len >= sizeof(buf)) - goto done; - dev = read_devno(buf); - } -done: - free(_name); - return dev; -} +static void sysfs_blkdev_deinit_path(struct path_cxt *pc); +static int sysfs_blkdev_enoent_redirect(struct path_cxt *pc, const char *path, int *dirfd); +static dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char *parent); /* - * Returns devname (e.g. "/dev/sda1") for the given devno. + * sysfs_blkdev_* is sysfs extension to ul_path_* API for block devices. * - * Please, use more robust blkid_devno_to_devname() in your applications. */ -char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz) +int sysfs_blkdev_init_path(struct path_cxt *pc, dev_t devno, struct path_cxt *parent) { - 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/" string */ - memmove(buf + 5, name, sz + 1); - memcpy(buf, "/dev/", 5); + struct sysfs_blkdev *blk; + int rc; + char buf[sizeof(_PATH_SYS_DEVBLOCK) + + sizeof(stringify_value(UINT32_MAX)) * 2 + + 3]; - if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno) - return buf; + /* define path to devno stuff */ + snprintf(buf, sizeof(buf), _PATH_SYS_DEVBLOCK "/%d:%d", major(devno), minor(devno)); + rc = ul_path_set_dir(pc, buf); + if (rc) + return rc; - return NULL; -} + /* make sure path exists */ + rc = ul_path_get_dirfd(pc); + if (rc < 0) + return rc; -int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent) -{ - char path[PATH_MAX]; - int fd, rc; + /* initialize sysfs blkdev specific stuff */ + blk = calloc(1, sizeof(struct sysfs_blkdev)); + if (!blk) + return -ENOMEM; - memset(cxt, 0, sizeof(*cxt)); - cxt->dir_fd = -1; + blk->devno = devno; + ul_path_set_dialect(pc, blk, sysfs_blkdev_deinit_path); - if (!sysfs_devno_path(devno, path, sizeof(path))) - goto err; + sysfs_blkdev_set_parent(pc, parent); - fd = open(path, O_RDONLY|O_CLOEXEC); - if (fd < 0) - goto err; - cxt->dir_fd = fd; - - cxt->dir_path = strdup(path); - if (!cxt->dir_path) - goto err; - cxt->devno = devno; - cxt->parent = parent; + ul_path_set_enoent_redirect(pc, sysfs_blkdev_enoent_redirect); return 0; -err: - rc = errno > 0 ? -errno : -1; - sysfs_deinit(cxt); - return rc; } -void sysfs_deinit(struct sysfs_cxt *cxt) +static void sysfs_blkdev_deinit_path(struct path_cxt *pc) { - if (!cxt) - return; + struct sysfs_blkdev *blk; - if (cxt->dir_fd >= 0) - close(cxt->dir_fd); - free(cxt->dir_path); - - memset(cxt, 0, sizeof(*cxt)); + if (!pc) + return; + blk = ul_path_get_dialect(pc); + if (!blk) + return; - cxt->dir_fd = -1; + ul_ref_path(blk->parent); + free(blk); } -int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st) +int sysfs_blkdev_set_parent(struct path_cxt *pc, struct path_cxt *parent) { - int rc = fstatat(cxt->dir_fd, attr, st, 0); + struct sysfs_blkdev *blk = ul_path_get_dialect(pc); - if (rc != 0 && errno == ENOENT && - strncmp(attr, "queue/", 6) == 0 && cxt->parent) { + if (!pc || !blk) + return -EINVAL; - /* Exception for "queue/". These attributes are available - * for parental devices only - */ - return fstatat(cxt->parent->dir_fd, attr, st, 0); + if (blk->parent) { + ul_unref_path(blk->parent); + blk->parent = NULL; } - return rc; -} -int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr) -{ - struct stat st; + if (parent) { + ul_ref_path(parent); + blk->parent = parent; + } else + blk->parent = NULL; - return sysfs_stat(cxt, attr, &st) == 0; + return 0; } -static int sysfs_open(struct sysfs_cxt *cxt, const char *attr, int flags) +/* + * Redirects ENOENT errors to the parent, if the path is to the queue/ + * sysfs directory. For example + * + * /sys/dev/block/8:1/queue/logical_block_size redirects to + * /sys/dev/block/8:0/queue/logical_block_size + */ +static int sysfs_blkdev_enoent_redirect(struct path_cxt *pc, const char *path, int *dirfd) { - int fd = openat(cxt->dir_fd, attr, flags); - - if (fd == -1 && errno == ENOENT && - strncmp(attr, "queue/", 6) == 0 && cxt->parent) { + struct sysfs_blkdev *blk = ul_path_get_dialect(pc); - /* Exception for "queue/". These attributes are available - * for parental devices only - */ - fd = openat(cxt->parent->dir_fd, attr, flags); + if (blk && blk->parent && strncmp(path, "queue/", 6) == 0) { + *dirfd = ul_path_get_dirfd(blk->parent); + if (*dirfd >= 0) + return 0; } - return fd; -} - -ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr, - char *buf, size_t bufsiz) -{ - if (!cxt->dir_path) - return -1; - - if (attr) - return readlinkat(cxt->dir_fd, attr, buf, bufsiz); - - /* read /sys/dev/block/ link */ - return readlink(cxt->dir_path, buf, bufsiz); + return 1; /* no redirect */ } -DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr) +char *sysfs_blkdev_get_name(struct path_cxt *pc, char *buf, size_t bufsiz) { - DIR *dir; - int fd = -1; - - if (attr) - fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC); - - else if (cxt->dir_fd >= 0) - /* request to open root of device in sysfs (/sys/block/) - * -- we cannot use cxt->sysfs_fd directly, because closedir() - * will close this our persistent file descriptor. - */ - fd = dup_fd_cloexec(cxt->dir_fd, STDERR_FILENO + 1); + char link[PATH_MAX]; + char *name; + ssize_t sz; - if (fd < 0) + /* read /sys/dev/block/ link */ + sz = ul_path_readlink(pc, link, sizeof(link) - 1, NULL); + if (sz < 0) return NULL; + link[sz] = '\0'; - dir = fdopendir(fd); - if (!dir) { - close(fd); + name = strrchr(link, '/'); + if (!name) return NULL; - } - if (!attr) - rewinddir(dir); - return dir; -} - -static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr) -{ - int fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC); + name++; + sz = strlen(name); + if ((size_t) sz + 1 > bufsiz) + return NULL; - return fd < 0 ? NULL : fdopen(fd, "r" UL_CLOEXECSTR); + memcpy(buf, name, sz + 1); + sysfs_devname_sys_to_dev(buf); + return buf; } - static struct dirent *xreaddir(DIR *dp) { struct dirent *d; @@ -316,7 +152,7 @@ static struct dirent *xreaddir(DIR *dp) return d; } -int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name) +int sysfs_blkdev_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name) { char path[NAME_MAX + 6 + 1]; @@ -356,39 +192,54 @@ int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_nam return faccessat(dirfd(dir), path, R_OK, 0) == 0; } +int sysfs_blkdev_count_partitions(struct path_cxt *pc, const char *devname) +{ + DIR *dir; + struct dirent *d; + int r = 0; + + dir = ul_path_opendir(pc, NULL); + if (!dir) + return 0; + + while ((d = xreaddir(dir))) { + if (sysfs_blkdev_is_partition_dirent(dir, d, devname)) + r++; + } + + closedir(dir); + return r; +} + /* * Converts @partno (partition number) to devno of the partition. - * The @cxt handles wholedisk device. + * The @pc handles wholedisk device. * * Note that this code does not expect any special format of the * partitions devnames. */ -dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno) +dev_t sysfs_blkdev_partno_to_devno(struct path_cxt *pc, int partno) { DIR *dir; struct dirent *d; - char path[NAME_MAX + 10 + 1]; dev_t devno = 0; - dir = sysfs_opendir(cxt, NULL); + dir = ul_path_opendir(pc, NULL); if (!dir) return 0; while ((d = xreaddir(dir))) { - int n, maj, min; + int n; - if (!sysfs_is_partition_dirent(dir, d, NULL)) + if (!sysfs_blkdev_is_partition_dirent(dir, d, NULL)) continue; - snprintf(path, sizeof(path), "%s/partition", d->d_name); - if (sysfs_read_int(cxt, path, &n)) + if (ul_path_readf_s32(pc, &n, "%s/partition", d->d_name)) continue; if (n == partno) { - snprintf(path, sizeof(path), "%s/dev", d->d_name); - if (sysfs_scanf(cxt, path, "%d:%d", &maj, &min) == 2) - devno = makedev(maj, min); - break; + if (ul_path_readf_majmin(pc, &devno, "%s/dev", d->d_name) == 0) + break; } } @@ -397,152 +248,23 @@ dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno) } -int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr, const char *fmt, ...) -{ - FILE *f = sysfs_fopen(cxt, attr); - va_list ap; - int rc; - - if (!f) - return -EINVAL; - va_start(ap, fmt); - rc = vfscanf(f, fmt, ap); - va_end(ap); - - fclose(f); - return rc; -} - - -int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res) -{ - int64_t x = 0; - - if (sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1) { - if (res) - *res = x; - return 0; - } - return -1; -} - -int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res) -{ - uint64_t x = 0; - - if (sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1) { - if (res) - *res = x; - return 0; - } - return -1; -} - -int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res) -{ - int x = 0; - - if (sysfs_scanf(cxt, attr, "%d", &x) == 1) { - if (res) - *res = x; - return 0; - } - return -1; -} - -int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str) -{ - int fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC); - int rc, errsv; - - if (fd < 0) - return -errno; - rc = write_all(fd, str, strlen(str)); - - errsv = errno; - close(fd); - errno = errsv; - return rc; -} - -int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num) -{ - char buf[sizeof(stringify_value(ULLONG_MAX))]; - int fd, rc = 0, len, errsv; - - fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC); - if (fd < 0) - return -errno; - - len = snprintf(buf, sizeof(buf), "%" PRIu64, num); - if (len < 0 || (size_t) len >= sizeof(buf)) - rc = len < 0 ? -errno : -E2BIG; - else - rc = write_all(fd, buf, len); - - errsv = errno; - close(fd); - errno = errsv; - return rc; -} - -char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr) -{ - char buf[BUFSIZ]; - return sysfs_scanf(cxt, attr, "%1023[^\n]", buf) == 1 ? - strdup(buf) : NULL; -} - - -int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr) -{ - DIR *dir; - int r = 0; - - if (!(dir = sysfs_opendir(cxt, attr))) - return 0; - - while (xreaddir(dir)) r++; - - closedir(dir); - return r; -} - -int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname) -{ - DIR *dir; - struct dirent *d; - int r = 0; - - if (!(dir = sysfs_opendir(cxt, NULL))) - return 0; - - while ((d = xreaddir(dir))) { - if (sysfs_is_partition_dirent(dir, d, devname)) - r++; - } - - closedir(dir); - return r; -} - /* * Returns slave name if there is only one slave, otherwise returns NULL. * The result should be deallocated by free(). */ -char *sysfs_get_slave(struct sysfs_cxt *cxt) +char *sysfs_blkdev_get_slave(struct path_cxt *pc) { DIR *dir; struct dirent *d; char *name = NULL; - if (!(dir = sysfs_opendir(cxt, "slaves"))) + dir = ul_path_opendir(pc, "slaves"); + if (!dir) return NULL; while ((d = xreaddir(dir))) { if (name) goto err; /* more slaves */ - name = strdup(d->d_name); } @@ -554,32 +276,6 @@ err: return NULL; } -char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz) -{ - char linkpath[PATH_MAX]; - char *name; - ssize_t sz; - - sz = sysfs_readlink(cxt, NULL, linkpath, sizeof(linkpath) - 1); - if (sz < 0) - return NULL; - linkpath[sz] = '\0'; - - name = strrchr(linkpath, '/'); - if (!name) - return NULL; - - name++; - sz = strlen(name); - - if ((size_t) sz + 1 > bufsiz) - return NULL; - - memcpy(buf, name, sz + 1); - sysfs_devname_sys_to_dev(buf); - - return buf; -} #define SUBSYSTEM_LINKNAME "/subsystem" @@ -637,19 +333,27 @@ static char *get_subsystem(char *chain, char *buf, size_t bufsz) * Returns complete path to the device, the patch contains all subsystems * used for the device. */ -char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz) +char *sysfs_blkdev_get_devchain(struct path_cxt *pc, char *buf, size_t bufsz) { /* read /sys/dev/block/: symlink */ - ssize_t sz = sysfs_readlink(cxt, NULL, buf, bufsz); + ssize_t sz = ul_path_readlink(pc, buf, bufsz, NULL); + const char *prefix; + size_t psz = 0; + if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > bufsz) return NULL; buf[sz++] = '\0'; + prefix = ul_path_get_prefix(pc); + if (prefix) + psz = strlen(prefix); /* create absolute patch from the link */ - memmove(buf + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, buf, sz); - memcpy(buf, _PATH_SYS_DEVBLOCK "/", sizeof(_PATH_SYS_DEVBLOCK "/") - 1); + memmove(buf + psz + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, buf, sz); + if (prefix) + memcpy(buf, prefix, psz); + memcpy(buf + psz, _PATH_SYS_DEVBLOCK "/", sizeof(_PATH_SYS_DEVBLOCK "/") - 1); return buf; } @@ -659,7 +363,7 @@ char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz) * * Returns: 0 in success, <0 on error, 1 on end of chain */ -int sysfs_next_subsystem(struct sysfs_cxt *cxt __attribute__((unused)), +int sysfs_blkdev_next_subsystem(struct path_cxt *pc __attribute__((unused)), char *devchain, char **subsys) { char subbuf[PATH_MAX]; @@ -699,19 +403,19 @@ static int is_hotpluggable_subsystem(const char *name) return 0; } -int sysfs_is_hotpluggable(struct sysfs_cxt *cxt) +int sysfs_blkdev_is_hotpluggable(struct path_cxt *pc) { char buf[PATH_MAX], *chain, *sub; int rc = 0; /* check /sys/dev/block/:/removable attribute */ - if (sysfs_read_int(cxt, "removable", &rc) == 0 && rc == 1) + if (ul_path_read_s32(pc, &rc, "removable") == 0 && rc == 1) return 1; - chain = sysfs_get_devchain(cxt, buf, sizeof(buf)); + chain = sysfs_blkdev_get_devchain(pc, buf, sizeof(buf)); - while (chain && sysfs_next_subsystem(cxt, chain, &sub) == 0) { + while (chain && sysfs_blkdev_next_subsystem(pc, chain, &sub) == 0) { rc = is_hotpluggable_subsystem(sub); if (rc) { free(sub); @@ -723,16 +427,16 @@ int sysfs_is_hotpluggable(struct sysfs_cxt *cxt) return rc; } -static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname, +static int get_dm_wholedisk(struct path_cxt *pc, char *diskname, size_t len, dev_t *diskdevno) { int rc = 0; char *name; - /* Note, sysfs_get_slave() returns the first slave only, + /* Note, sysfs_blkdev_get_slave() returns the first slave only, * if there is more slaves, then return NULL */ - name = sysfs_get_slave(cxt); + name = sysfs_blkdev_get_slave(pc); if (!name) return -1; @@ -742,7 +446,7 @@ static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname, } if (diskdevno) { - *diskdevno = sysfs_devname_to_devno(name, NULL); + *diskdevno = __sysfs_devname_to_devno(ul_path_get_prefix(pc), name, NULL); if (!*diskdevno) rc = -1; } @@ -755,16 +459,17 @@ static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname, * Returns by @diskdevno whole disk device devno and (optionally) by * @diskname the whole disk device name. */ -int sysfs_devno_to_wholedisk(dev_t dev, char *diskname, - size_t len, dev_t *diskdevno) +int sysfs_blkdev_get_wholedisk( struct path_cxt *pc, + char *diskname, + size_t len, + dev_t *diskdevno) { - struct sysfs_cxt cxt; int is_part = 0; - if (!dev || sysfs_init(&cxt, dev, NULL) != 0) + if (!pc) return -1; - is_part = sysfs_has_attribute(&cxt, "partition"); + is_part = ul_path_access(pc, F_OK, "partition") == 0; if (!is_part) { /* * Extra case for partitions mapped by device-mapper. @@ -774,16 +479,18 @@ int sysfs_devno_to_wholedisk(dev_t dev, char *diskname, * 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; + char *uuid = NULL, *tmp, *prefix; + + ul_path_read_string(pc, &uuid, "dm/uuid"); + tmp = uuid; + 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) + get_dm_wholedisk(pc, diskname, len, diskdevno) == 0) /* * partitioned device, mapped by DM */ @@ -796,10 +503,10 @@ int sysfs_devno_to_wholedisk(dev_t dev, char *diskname, /* * unpartitioned device */ - if (diskname && len && !sysfs_get_devname(&cxt, diskname, len)) + if (diskname && !sysfs_blkdev_get_name(pc, diskname, len)) goto err; if (diskdevno) - *diskdevno = dev; + *diskdevno = sysfs_blkdev_get_devno(pc); } else { /* @@ -812,7 +519,7 @@ int sysfs_devno_to_wholedisk(dev_t dev, char *diskname, char *name; ssize_t linklen; - linklen = sysfs_readlink(&cxt, NULL, linkpath, sizeof(linkpath) - 1); + linklen = ul_path_readlink(pc, linkpath, sizeof(linkpath) - 1, NULL); if (linklen < 0) goto err; linkpath[linklen] = '\0'; @@ -829,52 +536,71 @@ int sysfs_devno_to_wholedisk(dev_t dev, char *diskname, } if (diskdevno) { - *diskdevno = sysfs_devname_to_devno(name, NULL); + *diskdevno = __sysfs_devname_to_devno(ul_path_get_prefix(pc), diskname, NULL); if (!*diskdevno) goto err; } } done: - sysfs_deinit(&cxt); return 0; err: - sysfs_deinit(&cxt); return -1; } +int sysfs_devno_to_wholedisk(dev_t devno, char *diskname, + size_t len, dev_t *diskdevno) +{ + struct path_cxt *pc; + int rc = 0; + + if (!devno) + return -EINVAL; + pc = ul_new_path(NULL); + if (!pc) + return -ENOMEM; + + rc = sysfs_blkdev_init_path(pc, devno, NULL); + if (!rc) + rc = sysfs_blkdev_get_wholedisk(pc, diskname, len, diskdevno); + ul_unref_path(pc); + return rc; +} + /* * Returns 1 if the device is private device mapper device. The @uuid * (if not NULL) returns DM device UUID, use free() to deallocate. */ int sysfs_devno_is_dm_private(dev_t devno, char **uuid) { - struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; + struct path_cxt *pc = NULL; char *id = NULL; int rc = 0; - if (sysfs_init(&cxt, devno, NULL) != 0) - return 0; - - id = sysfs_strdup(&cxt, "dm/uuid"); - if (id) { - /* Private LVM devices use "LVM--" uuid format (important - * is the "LVM" prefix and "-" postfix). - */ - if (strncmp(id, "LVM-", 4) == 0) { - char *p = strrchr(id + 4, '-'); + pc = ul_new_path(NULL); + if (!pc) + goto done; + if (sysfs_blkdev_init_path(pc, devno, NULL) != 0) + goto done; + if (ul_path_read_string(pc, &id, "dm/uuid") <= 0 || !id) + goto done; - if (p && *(p + 1)) - rc = 1; + /* Private LVM devices use "LVM--" uuid format (important + * is the "LVM" prefix and "-" postfix). + */ + if (strncmp(id, "LVM-", 4) == 0) { + char *p = strrchr(id + 4, '-'); - /* Private Stratis devices prefix the UUID with "stratis-1-private" - */ - } else if (strncmp(id, "stratis-1-private", 17) == 0) { + if (p && *(p + 1)) rc = 1; - } - } - sysfs_deinit(&cxt); + /* Private Stratis devices prefix the UUID with "stratis-1-private" + */ + } else if (strncmp(id, "stratis-1-private", 17) == 0) { + rc = 1; + } +done: + ul_unref_path(pc); if (uuid) *uuid = id; else @@ -896,18 +622,21 @@ int sysfs_devno_is_wholedisk(dev_t devno) } -int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, int *c, int *t, int *l) +int sysfs_blkdev_scsi_get_hctl(struct path_cxt *pc, int *h, int *c, int *t, int *l) { char buf[PATH_MAX], *hctl; + struct sysfs_blkdev *blk; ssize_t len; - if (!cxt || cxt->hctl_error) + blk = ul_path_get_dialect(pc); + + if (!blk || blk->hctl_error) return -EINVAL; - if (cxt->has_hctl) + if (blk->has_hctl) goto done; - cxt->hctl_error = 1; - len = sysfs_readlink(cxt, "device", buf, sizeof(buf) - 1); + blk->hctl_error = 1; + len = ul_path_readlink(pc, buf, sizeof(buf) - 1, "device"); if (len < 0) return len; @@ -917,54 +646,63 @@ int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, int *c, int *t, int *l) return -1; hctl++; - if (sscanf(hctl, "%u:%u:%u:%u", &cxt->scsi_host, &cxt->scsi_channel, - &cxt->scsi_target, &cxt->scsi_lun) != 4) + if (sscanf(hctl, "%u:%u:%u:%u", &blk->scsi_host, &blk->scsi_channel, + &blk->scsi_target, &blk->scsi_lun) != 4) return -1; - cxt->has_hctl = 1; + blk->has_hctl = 1; done: if (h) - *h = cxt->scsi_host; + *h = blk->scsi_host; if (c) - *c = cxt->scsi_channel; + *c = blk->scsi_channel; if (t) - *t = cxt->scsi_target; + *t = blk->scsi_target; if (l) - *l = cxt->scsi_lun; + *l = blk->scsi_lun; - cxt->hctl_error = 0; + blk->hctl_error = 0; return 0; } -static char *sysfs_scsi_host_attribute_path(struct sysfs_cxt *cxt, - const char *type, char *buf, size_t bufsz, const char *attr) +static char *scsi_host_attribute_path( + struct path_cxt *pc, + const char *type, + char *buf, + size_t bufsz, + const char *attr) { int len; int host; + const char *prefix; - if (sysfs_scsi_get_hctl(cxt, &host, NULL, NULL, NULL)) + if (sysfs_blkdev_scsi_get_hctl(pc, &host, NULL, NULL, NULL)) return NULL; + prefix = ul_path_get_prefix(pc); + if (!prefix) + prefix = ""; + if (attr) - len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d/%s", - type, host, attr); + len = snprintf(buf, bufsz, "%s%s/%s_host/host%d/%s", + prefix, _PATH_SYS_CLASS, type, host, attr); else - len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d", - type, host); + len = snprintf(buf, bufsz, "%s%s/%s_host/host%d", + prefix, _PATH_SYS_CLASS, type, host); return (len < 0 || (size_t) len >= bufsz) ? NULL : buf; } -char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt, - const char *type, const char *attr) +char *sysfs_blkdev_scsi_host_strdup_attribute(struct path_cxt *pc, + const char *type, const char *attr) { char buf[1024]; int rc; FILE *f; if (!attr || !type || - !sysfs_scsi_host_attribute_path(cxt, type, buf, sizeof(buf), attr)) + !scsi_host_attribute_path(pc, type, buf, sizeof(buf), attr)) return NULL; if (!(f = fopen(buf, "r" UL_CLOEXECSTR))) @@ -976,53 +714,60 @@ char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt, return rc == 1 ? strdup(buf) : NULL; } -int sysfs_scsi_host_is(struct sysfs_cxt *cxt, const char *type) +int sysfs_blkdev_scsi_host_is(struct path_cxt *pc, const char *type) { char buf[PATH_MAX]; struct stat st; - if (!type || !sysfs_scsi_host_attribute_path(cxt, type, + if (!type || !scsi_host_attribute_path(pc, type, buf, sizeof(buf), NULL)) return 0; return stat(buf, &st) == 0 && S_ISDIR(st.st_mode); } -static char *sysfs_scsi_attribute_path(struct sysfs_cxt *cxt, +static char *scsi_attribute_path(struct path_cxt *pc, char *buf, size_t bufsz, const char *attr) { int len, h, c, t, l; + const char *prefix; - if (sysfs_scsi_get_hctl(cxt, &h, &c, &t, &l) != 0) + if (sysfs_blkdev_scsi_get_hctl(pc, &h, &c, &t, &l) != 0) return NULL; + prefix = ul_path_get_prefix(pc); + if (!prefix) + prefix = ""; + if (attr) - len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d/%s", + len = snprintf(buf, bufsz, "%s%s/devices/%d:%d:%d:%d/%s", + prefix, _PATH_SYS_SCSI, h,c,t,l, attr); else - len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d", + len = snprintf(buf, bufsz, "%s%s/devices/%d:%d:%d:%d", + prefix, _PATH_SYS_SCSI, h,c,t,l); return (len < 0 || (size_t) len >= bufsz) ? NULL : buf; } -int sysfs_scsi_has_attribute(struct sysfs_cxt *cxt, const char *attr) +int sysfs_scsi_has_attribute(struct path_cxt *pc, const char *attr) { char path[PATH_MAX]; struct stat st; - if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), attr)) + if (!scsi_attribute_path(pc, path, sizeof(path), attr)) return 0; return stat(path, &st) == 0; } -int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern) +int sysfs_scsi_path_contains(struct path_cxt *pc, const char *pattern) { char path[PATH_MAX], linkc[PATH_MAX]; struct stat st; ssize_t len; - if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), NULL)) + if (!scsi_attribute_path(pc, path, sizeof(path), NULL)) return 0; if (stat(path, &st) != 0) @@ -1036,6 +781,164 @@ int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern) return strstr(linkc, pattern) != NULL; } +static dev_t read_devno(const char *path) +{ + FILE *f; + int maj = 0, min = 0; + dev_t dev = 0; + + f = fopen(path, "r" UL_CLOEXECSTR); + if (!f) + return 0; + + if (fscanf(f, "%d:%d", &maj, &min) == 2) + dev = makedev(maj, min); + fclose(f); + return dev; +} + +static dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char *parent) +{ + char buf[PATH_MAX]; + char *_name = NULL; /* name as encoded in sysfs */ + dev_t dev = 0; + int len; + + if (!prefix) + prefix = ""; + + if (strncmp("/dev/", name, 5) == 0) { + /* + * Read from /dev + */ + struct stat st; + + if (stat(name, &st) == 0) { + dev = st.st_rdev; + goto done; + } + name += 5; /* unaccesible, or not node in /dev */ + } + + _name = strdup(name); + if (!_name) + goto done; + sysfs_devname_dev_to_sys(_name); + + if (parent && strncmp("dm-", name, 3)) { + /* + * Create path to /sys/block///dev + */ + char *_parent = strdup(parent); + + if (!_parent) { + free(_parent); + goto done; + } + sysfs_devname_dev_to_sys(_parent); + len = snprintf(buf, sizeof(buf), + "%s" _PATH_SYS_BLOCK "/%s/%s/dev", + prefix, _parent, _name); + free(_parent); + if (len < 0 || (size_t) len >= sizeof(buf)) + goto done; + + /* don't try anything else for dm-* */ + dev = read_devno(buf); + goto done; + } + + /* + * Read from /sys/block//dev + */ + len = snprintf(buf, sizeof(buf), + "%s" _PATH_SYS_BLOCK "/%s/dev", + prefix, _name); + if (len < 0 || (size_t) len >= sizeof(buf)) + goto done; + dev = read_devno(buf); + + if (!dev) { + /* + * Read from /sys/block//device/dev + */ + len = snprintf(buf, sizeof(buf), + "%s" _PATH_SYS_BLOCK "/%s/device/dev", + prefix, _name); + if (len < 0 || (size_t) len >= sizeof(buf)) + goto done; + dev = read_devno(buf); + } +done: + free(_name); + return dev; +} + +dev_t sysfs_devname_to_devno(const char *name) +{ + return __sysfs_devname_to_devno(NULL, name, NULL); +} + +char *sysfs_blkdev_get_path(struct path_cxt *pc, char *buf, size_t bufsiz) +{ + const char *name = sysfs_blkdev_get_name(pc, buf, bufsiz); + char *res = NULL; + size_t sz; + struct stat st; + + if (!name) + goto done; + + sz = strlen(name); + if (sz + sizeof("/dev/") > bufsiz) + goto done; + + /* create the final "/dev/" string */ + memmove(buf + 5, name, sz + 1); + memcpy(buf, "/dev/", 5); + + if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == sysfs_blkdev_get_devno(pc)) + res = buf; +done: + return res; +} + +dev_t sysfs_blkdev_get_devno(struct path_cxt *pc) +{ + return ((struct sysfs_blkdev *) ul_path_get_dialect(pc))->devno; +} + +/* + * Returns devname (e.g. "/dev/sda1") for the given devno. + * + * 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 path_cxt *pc = ul_new_path(NULL); + char *res = NULL; + + if (sysfs_blkdev_init_path(pc, devno, NULL) == 0) + res = sysfs_blkdev_get_path(pc, buf, bufsiz); + + ul_unref_path(pc); + return res; +} + +char *sysfs_devno_to_devname(dev_t devno, char *buf, size_t bufsiz) +{ + struct path_cxt *pc = ul_new_path(NULL); + char *res = NULL; + + if (sysfs_blkdev_init_path(pc, devno, NULL) == 0) + res = sysfs_blkdev_get_name(pc, buf, bufsiz); + + ul_unref_path(pc); + return res; +} + + + #ifdef TEST_PROGRAM_SYSFS #include #include @@ -1043,79 +946,90 @@ int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern) int main(int argc, char *argv[]) { - struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; + struct path_cxt *pc; char *devname; dev_t devno, disk_devno; char path[PATH_MAX], *sub, *chain; char diskname[32]; - int i, is_part; + int i, is_part, rc = EXIT_SUCCESS; uint64_t u64; - ssize_t len; if (argc != 2) errx(EXIT_FAILURE, "usage: %s ", argv[0]); devname = argv[1]; - devno = sysfs_devname_to_devno(devname, NULL); + devno = sysfs_devname_to_devno(devname); if (!devno) err(EXIT_FAILURE, "failed to read devno"); - if (sysfs_init(&cxt, devno, NULL)) - return EXIT_FAILURE; + printf("non-context:\n"); + printf(" DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno)); + printf(" DEVNAME: %s\n", sysfs_devno_to_devname(devno, path, sizeof(path))); + printf(" DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path))); + + sysfs_devno_to_wholedisk(devno, diskname, sizeof(diskname), &disk_devno); + printf(" WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno, major(disk_devno), minor(disk_devno)); + printf(" WHOLEDISK-DEVNAME: %s\n", diskname); + + pc = ul_new_path(NULL); + if (sysfs_blkdev_init_path(pc, devno, NULL) != 0) + goto done; - printf("NAME: %s\n", devname); - printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path))); - printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path))); - printf("DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno)); - printf("DEVNO-PATH: %s\n", sysfs_devno_path(devno, path, sizeof(path))); + printf("context based:\n"); + devno = sysfs_blkdev_get_devno(pc); + printf(" DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno)); + printf(" DEVNAME: %s\n", sysfs_blkdev_get_name(pc, path, sizeof(path))); + printf(" DEVPATH: %s\n", sysfs_blkdev_get_path(pc, path, sizeof(path))); sysfs_devno_to_wholedisk(devno, diskname, sizeof(diskname), &disk_devno); - printf("WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno, major(disk_devno), minor(disk_devno)); - printf("WHOLEDISK-DEVNAME: %s\n", diskname); + printf(" WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno, major(disk_devno), minor(disk_devno)); + printf(" WHOLEDISK-DEVNAME: %s\n", diskname); - is_part = sysfs_devno_has_attribute(devno, "partition"); - printf("PARTITION: %s\n", is_part ? "YES" : "NOT"); + is_part = ul_path_access(pc, F_OK, "partition") == 0; + printf(" PARTITION: %s\n", is_part ? "YES" : "NOT"); - printf("HOTPLUG: %s\n", sysfs_is_hotpluggable(&cxt) ? "yes" : "no"); - printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves")); + if (is_part && disk_devno) { + struct path_cxt *disk_pc = ul_new_path(NULL); - len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1); - if (len > 0) { - path[len] = '\0'; - printf("DEVNOLINK: %s\n", path); + sysfs_blkdev_init_path(disk_pc, disk_devno, NULL); + sysfs_blkdev_set_parent(pc, disk_pc); } + printf(" HOTPLUG: %s\n", sysfs_blkdev_is_hotpluggable(pc) ? "yes" : "no"); + printf(" SLAVES: %d\n", ul_path_count_dirents(pc, "slaves")); + if (!is_part) { printf("First 5 partitions:\n"); for (i = 1; i <= 5; i++) { - dev_t dev = sysfs_partno_to_devno(&cxt, i); + dev_t dev = sysfs_blkdev_partno_to_devno(pc, i); if (dev) printf("\t#%d %d:%d\n", i, major(dev), minor(dev)); } } - if (sysfs_read_u64(&cxt, "size", &u64)) - printf("read SIZE failed\n"); + if (ul_path_read_u64(pc, &u64, "size") != 0) + printf(" (!) read SIZE failed\n"); else - printf("SIZE: %jd\n", u64); + printf(" SIZE: %jd\n", u64); - if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i)) - printf("read SECTOR failed\n"); + if (ul_path_read_s32(pc, &i, "queue/hw_sector_size")) + printf(" (!) read SECTOR failed\n"); else - printf("SECTOR: %d\n", i); + printf(" SECTOR: %d\n", i); - chain = sysfs_get_devchain(&cxt, path, sizeof(path)); - printf("SUBSUSTEMS:\n"); + chain = sysfs_blkdev_get_devchain(pc, path, sizeof(path)); + printf(" SUBSUSTEMS:\n"); - while (chain && sysfs_next_subsystem(&cxt, chain, &sub) == 0) { + while (chain && sysfs_blkdev_next_subsystem(pc, chain, &sub) == 0) { printf("\t%s\n", sub); free(sub); } - - sysfs_deinit(&cxt); - return EXIT_SUCCESS; + rc = EXIT_SUCCESS; +done: + ul_unref_path(pc); + return rc; } #endif /* TEST_PROGRAM_SYSFS */ -- cgit v1.2.3-55-g7522