]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/sysfs: new implementation
authorKarel Zak <kzak@redhat.com>
Fri, 11 May 2018 12:28:03 +0000 (14:28 +0200)
committerKarel Zak <kzak@redhat.com>
Thu, 21 Jun 2018 10:56:16 +0000 (12:56 +0200)
* 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 <kzak@redhat.com>
include/sysfs.h
lib/Makemodule.am
lib/sysfs.c

index 9a72a2009c976a788e084efae571629031608b11..c36eab0356dfce311440461b2138ddb43d150a8b 100644 (file)
 #include <inttypes.h>
 #include <dirent.h>
 
-struct sysfs_cxt {
-       dev_t   devno;
-       int     dir_fd;         /* /sys/block/<name> */
-       char    *dir_path;
-       struct sysfs_cxt *parent;
-
-       unsigned int    scsi_host,
-                       scsi_channel,
-                       scsi_target,
-                       scsi_lun;
-
-       unsigned int    has_hctl   : 1,
-                       hctl_error : 1 ;
-};
-
-#define UL_SYSFSCXT_EMPTY { 0, -1, NULL, NULL, 0, 0, 0, 0, 0 }
-
-extern char *sysfs_devno_attribute_path(dev_t devno, char *buf,
-                                 size_t bufsiz, const char *attr);
-extern int sysfs_devno_has_attribute(dev_t devno, const char *attr);
-extern char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz);
-extern char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz);
-extern dev_t sysfs_devname_to_devno(const char *name, const char *parent);
-
-extern int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
-                                       __attribute__ ((warn_unused_result));
-extern void sysfs_deinit(struct sysfs_cxt *cxt);
-
-extern DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr);
-
-extern int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st);
-extern ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
-                          char *buf, size_t bufsiz);
-extern int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr);
-
-extern int sysfs_scanf(struct sysfs_cxt *cxt,  const char *attr,
-                      const char *fmt, ...)
-                       __attribute__ ((format (scanf, 3, 4)));
-
-extern int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res);
-extern int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res);
-extern int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res);
-
-extern int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str);
-extern int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num);
-
-extern char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz);
-
-extern char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr);
-
-extern int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr);
-extern int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname);
-extern dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno);
-extern char *sysfs_get_slave(struct sysfs_cxt *cxt);
-
-extern char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz);
-extern int sysfs_next_subsystem(struct sysfs_cxt *cxt, char *devchain, char **subsys);
-extern int sysfs_is_hotpluggable(struct sysfs_cxt *cxt);
-
-extern int sysfs_is_partition_dirent(DIR *dir, struct dirent *d,
-                       const char *parent_name);
-
-extern int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
-            size_t len, dev_t *diskdevno);
-
-extern int sysfs_devno_is_dm_private(dev_t devno, char **uuid);
-extern int sysfs_devno_is_wholedisk(dev_t devno);
-
-extern int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h,
-                              int *c, int *t, int *l);
-extern char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt,
-                const char *type, const char *attr);
-extern int sysfs_scsi_host_is(struct sysfs_cxt *cxt, const char *type);
-extern int sysfs_scsi_has_attribute(struct sysfs_cxt *cxt, const char *attr);
-extern int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern);
+#include "path.h"
 
 /**
  * sysfs_devname_sys_to_dev:
@@ -125,4 +51,53 @@ static inline void sysfs_devname_dev_to_sys(char *name)
                        c[0] = '!';
 }
 
+struct sysfs_blkdev {
+       dev_t   devno;
+       struct path_cxt *parent;
+
+       unsigned int    scsi_host,
+                       scsi_channel,
+                       scsi_target,
+                       scsi_lun;
+
+       unsigned int    has_hctl   : 1,
+                       hctl_error : 1 ;
+};
+
+int sysfs_blkdev_init_path(struct path_cxt *pc, dev_t devno, struct path_cxt *parent);
+int sysfs_blkdev_set_parent(struct path_cxt *pc, struct path_cxt *parent);
+
+char *sysfs_blkdev_get_name(struct path_cxt *pc, char *buf, size_t bufsiz);
+int sysfs_blkdev_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name);
+int sysfs_blkdev_count_partitions(struct path_cxt *pc, const char *devname);
+dev_t sysfs_blkdev_partno_to_devno(struct path_cxt *pc, int partno);
+char *sysfs_blkdev_get_slave(struct path_cxt *pc);
+char *sysfs_blkdev_get_path(struct path_cxt *pc, char *buf, size_t bufsiz);
+dev_t sysfs_blkdev_get_devno(struct path_cxt *pc);
+
+char *sysfs_blkdev_get_devchain(struct path_cxt *pc, char *buf, size_t bufsz);
+int sysfs_blkdev_next_subsystem(struct path_cxt *pc __attribute__((unused)), char *devchain, char **subsys);
+
+int sysfs_blkdev_is_hotpluggable(struct path_cxt *pc);
+int sysfs_blkdev_get_wholedisk( struct path_cxt *pc,
+                                char *diskname,
+                                size_t len,
+                                dev_t *diskdevno);
+
+int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
+                             size_t len, dev_t *diskdevno);
+int sysfs_devno_is_dm_private(dev_t devno, char **uuid);
+int sysfs_devno_is_wholedisk(dev_t devno);
+dev_t sysfs_devname_to_devno(const char *name);
+char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz);
+char *sysfs_devno_to_devname(dev_t devno, char *buf, size_t bufsiz);
+
+int sysfs_blkdev_scsi_get_hctl(struct path_cxt *pc, int *h, int *c, int *t, int *l);
+char *sysfs_blkdev_scsi_host_strdup_attribute(struct path_cxt *pc,
+                        const char *type, const char *attr);
+int sysfs_blkdev_scsi_host_is(struct path_cxt *pc, const char *type);
+int sysfs_scsi_has_attribute(struct path_cxt *pc, const char *attr);
+int sysfs_scsi_path_contains(struct path_cxt *pc, const char *pattern);
+
+
 #endif /* UTIL_LINUX_SYSFS_H */
index 2081ac927367ee130a0fccc9cfffede39422b104..13b19f1512ce90cb03e84b59d855ddf9ceee59c7 100644 (file)
@@ -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
index e5437f43a4ada7172ae1034413a60ae2ba3182e7..5b59952ebc96595905b16fb0c164ea4f4e535ffe 100644 (file)
 #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/<parent>/<name>/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/<sysname>/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/<sysname>/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/<name>" 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/<attr>". 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/<attr>". 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/<maj:min> 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/<dev>)
-                * -- 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/<maj:min> 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/<maj>:<min> 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/<maj>:<min>/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>-<name>" uuid format (important
-                * is the "LVM" prefix and "-<name>" 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>-<name>" uuid format (important
+        * is the "LVM" prefix and "-<name>" 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/<parent>/<name>/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/<sysname>/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/<sysname>/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/<name>" 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 <errno.h>
 #include <err.h>
@@ -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 <devname>", 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 */