2 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
10 char *sysfs_devno_attribute_path(dev_t devno, char *buf,
11 size_t bufsiz, const char *attr)
16 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d/%s",
17 major(devno), minor(devno), attr);
19 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d",
20 major(devno), minor(devno));
22 return (len < 0 || (size_t) len + 1 > bufsiz) ? NULL : buf;
25 int sysfs_devno_has_attribute(dev_t devno, const char *attr)
30 if (!sysfs_devno_attribute_path(devno, path, sizeof(path), attr))
32 if (stat(path, &info) == 0)
37 char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz)
39 return sysfs_devno_attribute_path(devno, buf, bufsiz, NULL);
42 dev_t sysfs_devname_to_devno(const char *name, const char *parent)
44 char buf[PATH_MAX], *path = NULL;
47 if (strncmp("/dev/", name, 5) == 0) {
53 if (stat(name, &st) == 0)
56 name += 5; /* unaccesible, or not node in /dev */
61 * Create path to /sys/block/<parent>/<name>/dev
63 int len = snprintf(buf, sizeof(buf),
64 _PATH_SYS_BLOCK "/%s/%s/dev", parent, name);
65 if (len < 0 || (size_t) len + 1 > sizeof(buf))
71 * Create path to /sys/block/<name>/dev
73 int len = snprintf(buf, sizeof(buf),
74 _PATH_SYS_BLOCK "/%s/dev", name);
75 if (len < 0 || (size_t) len + 1 > sizeof(buf))
82 * read devno from sysfs
91 if (fscanf(f, "%u:%u", &maj, &min) == 2)
92 dev = makedev(maj, min);
99 * Returns devname (e.g. "/dev/sda1") for the given devno.
101 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
104 * Please, use more robust blkid_devno_to_devname() in your applications.
106 char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz)
108 struct sysfs_cxt cxt;
113 if (sysfs_init(&cxt, devno, NULL))
116 name = sysfs_get_devname(&cxt, buf, bufsiz);
124 if (sz + sizeof("/dev/") > bufsiz)
127 /* create the final "/dev/<name>" string */
128 memmove(buf + 5, name, sz + 1);
129 memcpy(buf, "/dev/", 5);
131 if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno)
137 int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
142 memset(cxt, 0, sizeof(*cxt));
145 if (!sysfs_devno_path(devno, path, sizeof(path)))
148 fd = open(path, O_RDONLY);
151 cxt->dir_path = strdup(path);
156 cxt->parent = parent;
164 void sysfs_deinit(struct sysfs_cxt *cxt)
169 if (cxt->dir_fd >= 0)
176 cxt->dir_path = NULL;
179 int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st)
181 int rc = fstat_at(cxt->dir_fd, cxt->dir_path, attr, st, 0);
183 if (rc != 0 && errno == ENOENT &&
184 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
186 /* Exception for "queue/<attr>". These attributes are available
187 * for parental devices only
189 return fstat_at(cxt->parent->dir_fd,
190 cxt->parent->dir_path, attr, st, 0);
195 int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr)
199 return sysfs_stat(cxt, attr, &st) == 0;
202 static int sysfs_open(struct sysfs_cxt *cxt, const char *attr)
204 int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, O_RDONLY);
206 if (fd == -1 && errno == ENOENT &&
207 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
209 /* Exception for "queue/<attr>". These attributes are available
210 * for parental devices only
212 fd = open_at(cxt->parent->dir_fd, cxt->dir_path, attr, O_RDONLY);
217 ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
218 char *buf, size_t bufsiz)
221 return readlink_at(cxt->dir_fd, cxt->dir_path, attr, buf, bufsiz);
223 /* read /sys/dev/block/<maj:min> link */
224 return readlink(cxt->dir_path, buf, bufsiz);
227 DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr)
233 fd = sysfs_open(cxt, attr);
235 /* request to open root of device in sysfs (/sys/block/<dev>)
236 * -- we cannot use cxt->sysfs_fd directly, because closedir()
237 * will close this our persistent file descriptor.
239 fd = dup(cxt->dir_fd);
255 static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr)
257 int fd = sysfs_open(cxt, attr);
259 return fd < 0 ? NULL : fdopen(fd, "r");
263 static struct dirent *xreaddir(DIR *dp)
267 while ((d = readdir(dp))) {
268 if (!strcmp(d->d_name, ".") ||
269 !strcmp(d->d_name, ".."))
272 /* blacklist here? */
278 int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
282 #ifdef _DIRENT_HAVE_D_TYPE
283 if (d->d_type != DT_DIR)
286 if (strncmp(parent_name, d->d_name, strlen(parent_name)))
289 /* Cannot use /partition file, not supported on old sysfs */
290 snprintf(path, sizeof(path), "%s/start", d->d_name);
292 return faccessat(dirfd(dir), path, R_OK, 0) == 0;
295 int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr, const char *fmt, ...)
297 FILE *f = sysfs_fopen(cxt, attr);
304 rc = vfscanf(f, fmt, ap);
312 int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res)
316 if (sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1) {
324 int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res)
328 if (sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1) {
336 int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res)
340 if (sysfs_scanf(cxt, attr, "%d", &x) == 1) {
348 char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr)
351 return sysfs_scanf(cxt, attr, "%1024[^\n]", buf) == 1 ?
355 int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr)
360 if (!(dir = sysfs_opendir(cxt, attr)))
363 while (xreaddir(dir)) r++;
369 int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname)
375 if (!(dir = sysfs_opendir(cxt, NULL)))
378 while ((d = xreaddir(dir))) {
379 if (sysfs_is_partition_dirent(dir, d, devname))
388 * Returns slave name if there is only one slave, otherwise returns NULL.
389 * The result should be deallocated by free().
391 char *sysfs_get_slave(struct sysfs_cxt *cxt)
397 if (!(dir = sysfs_opendir(cxt, "slaves")))
400 while ((d = xreaddir(dir))) {
402 goto err; /* more slaves */
404 name = strdup(d->d_name);
415 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
418 char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz)
423 sz = sysfs_readlink(cxt, NULL, buf, bufsiz - 1);
428 name = strrchr(buf, '/');
435 memmove(buf, name, sz + 1);
439 #ifdef TEST_PROGRAM_SYSFS
444 int main(int argc, char *argv[])
446 struct sysfs_cxt cxt;
455 errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
458 devno = sysfs_devname_to_devno(devname, NULL);
461 err(EXIT_FAILURE, "failed to read devno");
463 printf("NAME: %s\n", devname);
464 printf("DEVNO: %u\n", (unsigned int) devno);
465 printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path)));
466 printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
467 printf("PARTITION: %s\n",
468 sysfs_devno_has_attribute(devno, "partition") ? "YES" : "NOT");
470 sysfs_init(&cxt, devno, NULL);
472 len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1);
475 printf("DEVNOLINK: %s\n", path);
478 printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves"));
480 if (sysfs_read_u64(&cxt, "size", &u64))
481 printf("read SIZE failed\n");
483 printf("SIZE: %jd\n", u64);
485 if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i))
486 printf("read SECTOR failed\n");
488 printf("SECTOR: %d\n", i);
490 printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path)));