]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/sysfs.c
docs: move source-code-management.txt to README
[thirdparty/util-linux.git] / lib / sysfs.c
CommitLineData
7fe16fda 1/*
0f23ee0c
KZ
2 * No copyright is claimed. This code is in the public domain; do with
3 * it what you wish.
4 *
5 * Written by Karel Zak <kzak@redhat.com>
7fe16fda 6 */
093b20ba 7#include <ctype.h>
948d1f31 8#include <libgen.h>
2208b3cc
RM
9#include <fcntl.h>
10#include <sys/stat.h>
11#include <unistd.h>
093b20ba 12
7fe16fda 13#include "c.h"
7fe16fda
KZ
14#include "pathnames.h"
15#include "sysfs.h"
d4eaabc8 16#include "fileutils.h"
6c987ca9 17#include "all-io.h"
7fe16fda
KZ
18
19char *sysfs_devno_attribute_path(dev_t devno, char *buf,
413993fc 20 size_t bufsiz, const char *attr)
7fe16fda
KZ
21{
22 int len;
23
24 if (attr)
413993fc 25 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d/%s",
7fe16fda
KZ
26 major(devno), minor(devno), attr);
27 else
413993fc 28 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d",
7fe16fda
KZ
29 major(devno), minor(devno));
30
06fa5817 31 return (len < 0 || (size_t) len >= bufsiz) ? NULL : buf;
7fe16fda
KZ
32}
33
34int sysfs_devno_has_attribute(dev_t devno, const char *attr)
35{
36 char path[PATH_MAX];
37 struct stat info;
38
39 if (!sysfs_devno_attribute_path(devno, path, sizeof(path), attr))
40 return 0;
41 if (stat(path, &info) == 0)
42 return 1;
43 return 0;
44}
45
413993fc 46char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz)
7fe16fda 47{
413993fc 48 return sysfs_devno_attribute_path(devno, buf, bufsiz, NULL);
7fe16fda
KZ
49}
50
51dev_t sysfs_devname_to_devno(const char *name, const char *parent)
52{
53 char buf[PATH_MAX], *path = NULL;
54 dev_t dev = 0;
55
56 if (strncmp("/dev/", name, 5) == 0) {
57 /*
58 * Read from /dev
59 */
60 struct stat st;
61
62 if (stat(name, &st) == 0)
63 dev = st.st_rdev;
64 else
65 name += 5; /* unaccesible, or not node in /dev */
66 }
67
857db7f5 68 if (!dev && parent && strncmp("dm-", name, 3)) {
7fe16fda
KZ
69 /*
70 * Create path to /sys/block/<parent>/<name>/dev
71 */
6c62abc4
KZ
72 char *_name = strdup(name), *_parent = strdup(parent);
73 int len;
74
92441d5c
KZ
75 if (!_name || !_parent) {
76 free(_name);
77 free(_parent);
6c62abc4 78 return 0;
92441d5c 79 }
6c62abc4
KZ
80 sysfs_devname_dev_to_sys(_name);
81 sysfs_devname_dev_to_sys(_parent);
82
83 len = snprintf(buf, sizeof(buf),
84 _PATH_SYS_BLOCK "/%s/%s/dev", _parent, _name);
85 free(_name);
86 free(_parent);
06fa5817 87 if (len < 0 || (size_t) len >= sizeof(buf))
7fe16fda
KZ
88 return 0;
89 path = buf;
90
91 } else if (!dev) {
92 /*
759b120d 93 * Create path to /sys/block/<sysname>/dev
7fe16fda 94 */
6c62abc4
KZ
95 char *_name = strdup(name);
96 int len;
759b120d 97
6c62abc4
KZ
98 if (!_name)
99 return 0;
100
101 sysfs_devname_dev_to_sys(_name);
102 len = snprintf(buf, sizeof(buf),
103 _PATH_SYS_BLOCK "/%s/dev", _name);
104 free(_name);
06fa5817 105 if (len < 0 || (size_t) len >= sizeof(buf))
7fe16fda
KZ
106 return 0;
107 path = buf;
108 }
109
110 if (path) {
111 /*
112 * read devno from sysfs
113 */
114 FILE *f;
115 int maj = 0, min = 0;
116
b1fa3e22 117 f = fopen(path, "r" UL_CLOEXECSTR);
7fe16fda
KZ
118 if (!f)
119 return 0;
120
9d72cbc1 121 if (fscanf(f, "%d:%d", &maj, &min) == 2)
7fe16fda
KZ
122 dev = makedev(maj, min);
123 fclose(f);
124 }
125 return dev;
126}
127
413993fc
KZ
128/*
129 * Returns devname (e.g. "/dev/sda1") for the given devno.
130 *
413993fc
KZ
131 * Please, use more robust blkid_devno_to_devname() in your applications.
132 */
133char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz)
134{
135 struct sysfs_cxt cxt;
136 char *name;
137 size_t sz;
138 struct stat st;
139
140 if (sysfs_init(&cxt, devno, NULL))
141 return NULL;
142
143 name = sysfs_get_devname(&cxt, buf, bufsiz);
144 sysfs_deinit(&cxt);
145
146 if (!name)
147 return NULL;
148
149 sz = strlen(name);
150
151 if (sz + sizeof("/dev/") > bufsiz)
152 return NULL;
153
154 /* create the final "/dev/<name>" string */
155 memmove(buf + 5, name, sz + 1);
156 memcpy(buf, "/dev/", 5);
157
158 if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno)
159 return buf;
160
161 return NULL;
162}
163
7fe16fda
KZ
164int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
165{
166 char path[PATH_MAX];
4c5bbc5d 167 int fd, rc;
7fe16fda 168
413993fc 169 memset(cxt, 0, sizeof(*cxt));
3a18db62 170 cxt->dir_fd = -1;
413993fc 171
7fe16fda
KZ
172 if (!sysfs_devno_path(devno, path, sizeof(path)))
173 goto err;
174
b1fa3e22 175 fd = open(path, O_RDONLY|O_CLOEXEC);
7fe16fda
KZ
176 if (fd < 0)
177 goto err;
06ae069b
KZ
178 cxt->dir_fd = fd;
179
7fe16fda
KZ
180 cxt->dir_path = strdup(path);
181 if (!cxt->dir_path)
182 goto err;
7fe16fda 183 cxt->devno = devno;
7fe16fda
KZ
184 cxt->parent = parent;
185 return 0;
186err:
4c5bbc5d 187 rc = errno > 0 ? -errno : -1;
7fe16fda
KZ
188 sysfs_deinit(cxt);
189 return rc;
190}
191
192void sysfs_deinit(struct sysfs_cxt *cxt)
193{
194 if (!cxt)
195 return;
196
197 if (cxt->dir_fd >= 0)
198 close(cxt->dir_fd);
87e77645
KZ
199 free(cxt->dir_path);
200
d0f7e5b4
KZ
201 memset(cxt, 0, sizeof(*cxt));
202
7fe16fda 203 cxt->dir_fd = -1;
7fe16fda
KZ
204}
205
206int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st)
207{
2208b3cc 208 int rc = fstatat(cxt->dir_fd, attr, st, 0);
7fe16fda
KZ
209
210 if (rc != 0 && errno == ENOENT &&
211 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
212
213 /* Exception for "queue/<attr>". These attributes are available
214 * for parental devices only
215 */
2208b3cc 216 return fstatat(cxt->parent->dir_fd, attr, st, 0);
7fe16fda
KZ
217 }
218 return rc;
219}
220
d8a84552
KZ
221int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr)
222{
223 struct stat st;
224
225 return sysfs_stat(cxt, attr, &st) == 0;
226}
227
6c987ca9 228static int sysfs_open(struct sysfs_cxt *cxt, const char *attr, int flags)
7fe16fda 229{
2208b3cc 230 int fd = openat(cxt->dir_fd, attr, flags);
7fe16fda
KZ
231
232 if (fd == -1 && errno == ENOENT &&
233 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
234
235 /* Exception for "queue/<attr>". These attributes are available
236 * for parental devices only
237 */
2208b3cc 238 fd = openat(cxt->parent->dir_fd, attr, flags);
7fe16fda
KZ
239 }
240 return fd;
241}
242
413993fc
KZ
243ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
244 char *buf, size_t bufsiz)
245{
cffee0de
CW
246 if (!cxt->dir_path)
247 return -1;
248
413993fc 249 if (attr)
2208b3cc 250 return readlinkat(cxt->dir_fd, attr, buf, bufsiz);
413993fc
KZ
251
252 /* read /sys/dev/block/<maj:min> link */
253 return readlink(cxt->dir_path, buf, bufsiz);
254}
255
7fe16fda
KZ
256DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr)
257{
258 DIR *dir;
fee9431f 259 int fd = -1;
7fe16fda
KZ
260
261 if (attr)
6c987ca9 262 fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC);
fee9431f
KZ
263
264 else if (cxt->dir_fd >= 0)
7fe16fda
KZ
265 /* request to open root of device in sysfs (/sys/block/<dev>)
266 * -- we cannot use cxt->sysfs_fd directly, because closedir()
267 * will close this our persistent file descriptor.
268 */
fef6f84a 269 fd = dup_fd_cloexec(cxt->dir_fd, STDERR_FILENO + 1);
7fe16fda
KZ
270
271 if (fd < 0)
272 return NULL;
273
274 dir = fdopendir(fd);
275 if (!dir) {
276 close(fd);
277 return NULL;
278 }
279 if (!attr)
280 rewinddir(dir);
281 return dir;
282}
283
284
285static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr)
286{
6c987ca9 287 int fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC);
7fe16fda 288
b1fa3e22 289 return fd < 0 ? NULL : fdopen(fd, "r" UL_CLOEXECSTR);
7fe16fda
KZ
290}
291
292
293static struct dirent *xreaddir(DIR *dp)
294{
295 struct dirent *d;
296
297 while ((d = readdir(dp))) {
298 if (!strcmp(d->d_name, ".") ||
299 !strcmp(d->d_name, ".."))
300 continue;
301
302 /* blacklist here? */
303 break;
304 }
305 return d;
306}
307
308int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
309{
310 char path[256];
311
312#ifdef _DIRENT_HAVE_D_TYPE
09a71aa1 313 if (d->d_type != DT_DIR &&
ea7f012b
KZ
314 d->d_type != DT_LNK &&
315 d->d_type != DT_UNKNOWN)
7fe16fda
KZ
316 return 0;
317#endif
093b20ba
KZ
318 if (parent_name) {
319 const char *p = parent_name;
320 size_t len;
321
322 /* /dev/sda --> "sda" */
323 if (*parent_name == '/') {
324 p = strrchr(parent_name, '/');
325 if (!p)
326 return 0;
327 p++;
328 }
329
330 len = strlen(p);
331 if (strlen(d->d_name) <= len)
332 return 0;
333
a3b78053
KZ
334 /* partitions subdir name is
335 * "<parent>[:digit:]" or "<parent>p[:digit:]"
336 */
337 return strncmp(p, d->d_name, len) == 0 &&
338 ((*(d->d_name + len) == 'p' && isdigit(*(d->d_name + len + 1)))
339 || isdigit(*(d->d_name + len)));
093b20ba 340 }
7fe16fda
KZ
341
342 /* Cannot use /partition file, not supported on old sysfs */
343 snprintf(path, sizeof(path), "%s/start", d->d_name);
344
345 return faccessat(dirfd(dir), path, R_OK, 0) == 0;
346}
347
778ad369 348/*
f31322a2
BV
349 * Converts @partno (partition number) to devno of the partition.
350 * The @cxt handles wholedisk device.
778ad369
KZ
351 *
352 * Note that this code does not expect any special format of the
353 * partitions devnames.
354 */
355dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno)
356{
357 DIR *dir;
358 struct dirent *d;
359 char path[256];
360 dev_t devno = 0;
361
362 dir = sysfs_opendir(cxt, NULL);
363 if (!dir)
364 return 0;
365
366 while ((d = xreaddir(dir))) {
367 int n, maj, min;
368
369 if (!sysfs_is_partition_dirent(dir, d, NULL))
370 continue;
371
372 snprintf(path, sizeof(path), "%s/partition", d->d_name);
f31322a2 373 if (sysfs_read_int(cxt, path, &n))
778ad369
KZ
374 continue;
375
f31322a2
BV
376 if (n == partno) {
377 snprintf(path, sizeof(path), "%s/dev", d->d_name);
378 if (sysfs_scanf(cxt, path, "%d:%d", &maj, &min) == 2)
379 devno = makedev(maj, min);
380 break;
381 }
778ad369
KZ
382 }
383
384 closedir(dir);
385 return devno;
386}
387
388
7fe16fda
KZ
389int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr, const char *fmt, ...)
390{
391 FILE *f = sysfs_fopen(cxt, attr);
392 va_list ap;
393 int rc;
394
395 if (!f)
396 return -EINVAL;
397 va_start(ap, fmt);
398 rc = vfscanf(f, fmt, ap);
399 va_end(ap);
400
401 fclose(f);
402 return rc;
403}
404
413993fc 405
90e9fcda 406int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res)
7fe16fda 407{
90e9fcda
KZ
408 int64_t x = 0;
409
410 if (sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1) {
411 if (res)
412 *res = x;
413 return 0;
414 }
415 return -1;
7fe16fda
KZ
416}
417
90e9fcda 418int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res)
7fe16fda 419{
90e9fcda
KZ
420 uint64_t x = 0;
421
422 if (sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1) {
423 if (res)
424 *res = x;
425 return 0;
426 }
427 return -1;
7fe16fda
KZ
428}
429
90e9fcda 430int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res)
7fe16fda 431{
90e9fcda
KZ
432 int x = 0;
433
434 if (sysfs_scanf(cxt, attr, "%d", &x) == 1) {
435 if (res)
436 *res = x;
437 return 0;
438 }
439 return -1;
7fe16fda
KZ
440}
441
6c987ca9
KZ
442int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str)
443{
444 int fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC);
445 int rc, errsv;
446
447 if (fd < 0)
448 return -errno;
449 rc = write_all(fd, str, strlen(str));
450
451 errsv = errno;
452 close(fd);
453 errno = errsv;
454 return rc;
455}
456
457int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num)
458{
459 char buf[sizeof(stringify_value(ULLONG_MAX))];
460 int fd, rc = 0, len, errsv;
461
462 fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC);
463 if (fd < 0)
464 return -errno;
465
7231fb2a 466 len = snprintf(buf, sizeof(buf), "%" PRIu64, num);
06fa5817 467 if (len < 0 || (size_t) len >= sizeof(buf))
ff27b20f 468 rc = len < 0 ? -errno : -E2BIG;
6c987ca9
KZ
469 else
470 rc = write_all(fd, buf, len);
471
472 errsv = errno;
473 close(fd);
474 errno = errsv;
475 return rc;
476}
477
7fe16fda
KZ
478char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr)
479{
548b9714 480 char buf[BUFSIZ];
657d9adb 481 return sysfs_scanf(cxt, attr, "%1023[^\n]", buf) == 1 ?
7fe16fda
KZ
482 strdup(buf) : NULL;
483}
484
548b9714 485
7fe16fda
KZ
486int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr)
487{
488 DIR *dir;
489 int r = 0;
490
491 if (!(dir = sysfs_opendir(cxt, attr)))
492 return 0;
493
494 while (xreaddir(dir)) r++;
495
496 closedir(dir);
497 return r;
498}
499
500int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname)
501{
502 DIR *dir;
503 struct dirent *d;
504 int r = 0;
505
506 if (!(dir = sysfs_opendir(cxt, NULL)))
507 return 0;
508
509 while ((d = xreaddir(dir))) {
510 if (sysfs_is_partition_dirent(dir, d, devname))
511 r++;
512 }
513
514 closedir(dir);
515 return r;
516}
517
413993fc
KZ
518/*
519 * Returns slave name if there is only one slave, otherwise returns NULL.
520 * The result should be deallocated by free().
521 */
522char *sysfs_get_slave(struct sysfs_cxt *cxt)
523{
524 DIR *dir;
525 struct dirent *d;
526 char *name = NULL;
527
528 if (!(dir = sysfs_opendir(cxt, "slaves")))
529 return NULL;
530
531 while ((d = xreaddir(dir))) {
532 if (name)
533 goto err; /* more slaves */
534
535 name = strdup(d->d_name);
536 }
537
538 closedir(dir);
539 return name;
540err:
541 free(name);
5415374d 542 closedir(dir);
413993fc
KZ
543 return NULL;
544}
545
413993fc
KZ
546char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz)
547{
4419ffb9
KZ
548 char linkpath[PATH_MAX];
549 char *name;
550 ssize_t sz;
413993fc 551
4419ffb9 552 sz = sysfs_readlink(cxt, NULL, linkpath, sizeof(linkpath) - 1);
413993fc
KZ
553 if (sz < 0)
554 return NULL;
4419ffb9 555 linkpath[sz] = '\0';
413993fc 556
4419ffb9 557 name = strrchr(linkpath, '/');
413993fc
KZ
558 if (!name)
559 return NULL;
560
561 name++;
562 sz = strlen(name);
563
4419ffb9
KZ
564 if ((size_t) sz + 1 > bufsiz)
565 return NULL;
566
567 memcpy(buf, name, sz + 1);
6c62abc4
KZ
568 sysfs_devname_sys_to_dev(buf);
569
413993fc
KZ
570 return buf;
571}
7fe16fda 572
e017ef8b
KZ
573#define SUBSYSTEM_LINKNAME "/subsystem"
574
575/*
576 * For example:
577 *
578 * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
579 * 1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
580 *
581 * The function check if <chain>/subsystem symlink exists, if yes then returns
582 * basename of the readlink result, and remove the last subdirectory from the
583 * <chain> path.
584 */
585static char *get_subsystem(char *chain, char *buf, size_t bufsz)
586{
587 size_t len;
588 char *p;
589
590 if (!chain || !*chain)
591 return NULL;
592
593 len = strlen(chain);
594 if (len + sizeof(SUBSYSTEM_LINKNAME) > PATH_MAX)
595 return NULL;
596
597 do {
598 ssize_t sz;
599
600 /* append "/subsystem" to the path */
601 memcpy(chain + len, SUBSYSTEM_LINKNAME, sizeof(SUBSYSTEM_LINKNAME));
602
603 /* try if subsystem symlink exists */
604 sz = readlink(chain, buf, bufsz - 1);
605
606 /* remove last subsystem from chain */
607 chain[len] = '\0';
608 p = strrchr(chain, '/');
609 if (p) {
610 *p = '\0';
611 len = p - chain;
612 }
613
614 if (sz > 0) {
615 /* we found symlink to subsystem, return basename */
616 buf[sz] = '\0';
617 return basename(buf);
618 }
619
620 } while (p);
621
622 return NULL;
623}
624
625/*
a7349ee3 626 * Returns complete path to the device, the patch contains all subsystems
e017ef8b
KZ
627 * used for the device.
628 */
629char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz)
630{
631 /* read /sys/dev/block/<maj>:<min> symlink */
e8b150e9 632 ssize_t sz = sysfs_readlink(cxt, NULL, buf, bufsz);
e017ef8b
KZ
633 if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > bufsz)
634 return NULL;
635
636 buf[sz++] = '\0';
637
638 /* create absolute patch from the link */
639 memmove(buf + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, buf, sz);
640 memcpy(buf, _PATH_SYS_DEVBLOCK "/", sizeof(_PATH_SYS_DEVBLOCK "/") - 1);
641
642 return buf;
643}
644
645/*
646 * The @subsys returns the next subsystem in the chain. Function modifies
647 * @devchain string.
648 *
649 * Returns: 0 in success, <0 on error, 1 on end of chain
650 */
651int sysfs_next_subsystem(struct sysfs_cxt *cxt __attribute__((unused)),
652 char *devchain, char **subsys)
653{
654 char subbuf[PATH_MAX];
655 char *sub;
656
657 if (!subsys || !devchain)
658 return -EINVAL;
659
32c9ce4b
KZ
660 *subsys = NULL;
661
e017ef8b
KZ
662 while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) {
663 *subsys = strdup(sub);
664 if (!*subsys)
665 return -ENOMEM;
666 return 0;
667 }
668
669 return 1;
670}
671
672
673static int is_hotpluggable_subsystem(const char *name)
674{
675 static const char * const hotplug_subsystems[] = {
676 "usb",
677 "ieee1394",
678 "pcmcia",
679 "mmc",
680 "ccw"
681 };
682 size_t i;
683
684 for (i = 0; i < ARRAY_SIZE(hotplug_subsystems); i++)
685 if (strcmp(name, hotplug_subsystems[i]) == 0)
686 return 1;
687
688 return 0;
689}
690
691int sysfs_is_hotpluggable(struct sysfs_cxt *cxt)
692{
693 char buf[PATH_MAX], *chain, *sub;
694 int rc = 0;
695
696
697 /* check /sys/dev/block/<maj>:<min>/removable attribute */
698 if (sysfs_read_int(cxt, "removable", &rc) == 0 && rc == 1)
699 return 1;
700
701 chain = sysfs_get_devchain(cxt, buf, sizeof(buf));
702
703 while (chain && sysfs_next_subsystem(cxt, chain, &sub) == 0) {
704 rc = is_hotpluggable_subsystem(sub);
705 if (rc) {
706 free(sub);
707 break;
708 }
709 free(sub);
710 }
711
712 return rc;
713}
714
3b66f48e
ML
715static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname,
716 size_t len, dev_t *diskdevno)
717{
718 int rc = 0;
719 char *name;
720
721 /* Note, sysfs_get_slave() returns the first slave only,
722 * if there is more slaves, then return NULL
723 */
724 name = sysfs_get_slave(cxt);
725 if (!name)
726 return -1;
727
728 if (diskname && len) {
729 strncpy(diskname, name, len);
730 diskname[len - 1] = '\0';
731 }
732
733 if (diskdevno) {
734 *diskdevno = sysfs_devname_to_devno(name, NULL);
735 if (!*diskdevno)
736 rc = -1;
737 }
738
739 free(name);
740 return rc;
741}
742
b55e5886 743/*
9e930041 744 * Returns by @diskdevno whole disk device devno and (optionally) by
b55e5886
KZ
745 * @diskname the whole disk device name.
746 */
3b66f48e
ML
747int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
748 size_t len, dev_t *diskdevno)
749{
750 struct sysfs_cxt cxt;
751 int is_part = 0;
752
753 if (!dev || sysfs_init(&cxt, dev, NULL) != 0)
754 return -1;
755
756 is_part = sysfs_has_attribute(&cxt, "partition");
757 if (!is_part) {
758 /*
759 * Extra case for partitions mapped by device-mapper.
760 *
9e930041 761 * All regular partitions (added by BLKPG ioctl or kernel PT
3b66f48e
ML
762 * parser) have the /sys/.../partition file. The partitions
763 * mapped by DM don't have such file, but they have "part"
764 * prefix in DM UUID.
765 */
766 char *uuid = sysfs_strdup(&cxt, "dm/uuid");
767 char *tmp = uuid;
768 char *prefix = uuid ? strsep(&tmp, "-") : NULL;
769
770 if (prefix && strncasecmp(prefix, "part", 4) == 0)
771 is_part = 1;
772 free(uuid);
773
774 if (is_part &&
775 get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0)
776 /*
777 * partitioned device, mapped by DM
778 */
779 goto done;
780
781 is_part = 0;
782 }
783
784 if (!is_part) {
785 /*
786 * unpartitioned device
787 */
74ce680a
SK
788 if (diskname && len && !sysfs_get_devname(&cxt, diskname, len))
789 goto err;
3b66f48e
ML
790 if (diskdevno)
791 *diskdevno = dev;
792
793 } else {
794 /*
795 * partitioned device
796 * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
797 * - dirname ../../block/sda/sda1 = ../../block/sda
798 * - basename ../../block/sda = sda
799 */
800 char linkpath[PATH_MAX];
801 char *name;
e8b150e9 802 ssize_t linklen;
3b66f48e 803
e8b150e9 804 linklen = sysfs_readlink(&cxt, NULL, linkpath, sizeof(linkpath) - 1);
3b66f48e
ML
805 if (linklen < 0)
806 goto err;
807 linkpath[linklen] = '\0';
808
809 stripoff_last_component(linkpath); /* dirname */
810 name = stripoff_last_component(linkpath); /* basename */
811 if (!name)
812 goto err;
813
6c62abc4 814 sysfs_devname_sys_to_dev(name);
3b66f48e
ML
815 if (diskname && len) {
816 strncpy(diskname, name, len);
817 diskname[len - 1] = '\0';
818 }
819
820 if (diskdevno) {
821 *diskdevno = sysfs_devname_to_devno(name, NULL);
822 if (!*diskdevno)
823 goto err;
824 }
825 }
826
827done:
828 sysfs_deinit(&cxt);
829 return 0;
830err:
831 sysfs_deinit(&cxt);
832 return -1;
833}
834
39866431
KZ
835/*
836 * Returns 1 if the device is private LVM device.
837 */
838int sysfs_devno_is_lvm_private(dev_t devno)
839{
840 struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
841 char *uuid = NULL;
842 int rc = 0;
843
844 if (sysfs_init(&cxt, devno, NULL) != 0)
845 return 0;
846
847 uuid = sysfs_strdup(&cxt, "dm/uuid");
848
849 /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
850 * is the "LVM" prefix and "-<name>" postfix).
851 */
852 if (uuid && strncmp(uuid, "LVM-", 4) == 0) {
853 char *p = strrchr(uuid + 4, '-');
854
855 if (p && *(p + 1))
856 rc = 1;
857 }
858
859 sysfs_deinit(&cxt);
860 free(uuid);
861 return rc;
862}
d0f7e5b4 863
b55e5886
KZ
864/*
865 * Return 0 or 1, or < 0 in case of error
866 */
867int sysfs_devno_is_wholedisk(dev_t devno)
868{
869 dev_t disk;
b55e5886
KZ
870
871 if (sysfs_devno_to_wholedisk(devno, NULL, 0, &disk) != 0)
872 return -1;
873
874 return devno == disk;
875}
876
e017ef8b 877
d0f7e5b4
KZ
878int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, int *c, int *t, int *l)
879{
880 char buf[PATH_MAX], *hctl;
881 ssize_t len;
882
638402ed 883 if (!cxt || cxt->hctl_error)
d0f7e5b4
KZ
884 return -EINVAL;
885 if (cxt->has_hctl)
886 goto done;
887
638402ed 888 cxt->hctl_error = 1;
9f51089e 889 len = sysfs_readlink(cxt, "device", buf, sizeof(buf) - 1);
d0f7e5b4
KZ
890 if (len < 0)
891 return len;
892
893 buf[len] = '\0';
88a6006b 894 hctl = strrchr(buf, '/');
d0f7e5b4
KZ
895 if (!hctl)
896 return -1;
88a6006b 897 hctl++;
d0f7e5b4 898
a6d85ee0 899 if (sscanf(hctl, "%u:%u:%u:%u", &cxt->scsi_host, &cxt->scsi_channel,
d0f7e5b4
KZ
900 &cxt->scsi_target, &cxt->scsi_lun) != 4)
901 return -1;
902
903 cxt->has_hctl = 1;
904done:
905 if (h)
906 *h = cxt->scsi_host;
907 if (c)
908 *c = cxt->scsi_channel;
909 if (t)
910 *t = cxt->scsi_target;
911 if (l)
912 *l = cxt->scsi_lun;
638402ed
KZ
913
914 cxt->hctl_error = 0;
d0f7e5b4
KZ
915 return 0;
916}
917
23a11c74
KZ
918
919static char *sysfs_scsi_host_attribute_path(struct sysfs_cxt *cxt,
920 const char *type, char *buf, size_t bufsz, const char *attr)
921{
922 int len;
923 int host;
924
925 if (sysfs_scsi_get_hctl(cxt, &host, NULL, NULL, NULL))
926 return NULL;
927
928 if (attr)
a5dc4d2d 929 len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d/%s",
23a11c74
KZ
930 type, host, attr);
931 else
a5dc4d2d 932 len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d",
23a11c74
KZ
933 type, host);
934
06fa5817 935 return (len < 0 || (size_t) len >= bufsz) ? NULL : buf;
23a11c74
KZ
936}
937
938char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt,
939 const char *type, const char *attr)
940{
941 char buf[1024];
942 int rc;
943 FILE *f;
944
945 if (!attr || !type ||
946 !sysfs_scsi_host_attribute_path(cxt, type, buf, sizeof(buf), attr))
947 return NULL;
948
b1fa3e22 949 if (!(f = fopen(buf, "r" UL_CLOEXECSTR)))
23a11c74
KZ
950 return NULL;
951
952 rc = fscanf(f, "%1023[^\n]", buf);
953 fclose(f);
954
955 return rc == 1 ? strdup(buf) : NULL;
956}
957
958int sysfs_scsi_host_is(struct sysfs_cxt *cxt, const char *type)
959{
960 char buf[PATH_MAX];
961 struct stat st;
962
963 if (!type || !sysfs_scsi_host_attribute_path(cxt, type,
964 buf, sizeof(buf), NULL))
965 return 0;
966
967 return stat(buf, &st) == 0 && S_ISDIR(st.st_mode);
968}
969
a5dc4d2d
KZ
970static char *sysfs_scsi_attribute_path(struct sysfs_cxt *cxt,
971 char *buf, size_t bufsz, const char *attr)
972{
973 int len, h, c, t, l;
974
975 if (sysfs_scsi_get_hctl(cxt, &h, &c, &t, &l) != 0)
976 return NULL;
977
978 if (attr)
979 len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d/%s",
980 h,c,t,l, attr);
981 else
982 len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d",
983 h,c,t,l);
06fa5817 984 return (len < 0 || (size_t) len >= bufsz) ? NULL : buf;
a5dc4d2d
KZ
985}
986
987int sysfs_scsi_has_attribute(struct sysfs_cxt *cxt, const char *attr)
988{
989 char path[PATH_MAX];
990 struct stat st;
991
992 if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), attr))
993 return 0;
994
995 return stat(path, &st) == 0;
996}
997
998int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern)
999{
1000 char path[PATH_MAX], linkc[PATH_MAX];
1001 struct stat st;
1002 ssize_t len;
1003
1004 if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), NULL))
1005 return 0;
1006
1007 if (stat(path, &st) != 0)
1008 return 0;
1009
1010 len = readlink(path, linkc, sizeof(linkc) - 1);
1011 if (len < 0)
1012 return 0;
1013
1014 linkc[len] = '\0';
1015 return strstr(linkc, pattern) != NULL;
1016}
1017
e918cca5 1018#ifdef TEST_PROGRAM_SYSFS
7fe16fda
KZ
1019#include <errno.h>
1020#include <err.h>
1021#include <stdlib.h>
1022
1023int main(int argc, char *argv[])
1024{
4c5bbc5d 1025 struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
7fe16fda 1026 char *devname;
4419ffb9 1027 dev_t devno, disk_devno;
e017ef8b 1028 char path[PATH_MAX], *sub, *chain;
4419ffb9 1029 char diskname[32];
778ad369 1030 int i, is_part;
90e9fcda 1031 uint64_t u64;
413993fc 1032 ssize_t len;
7fe16fda
KZ
1033
1034 if (argc != 2)
1035 errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
1036
1037 devname = argv[1];
1038 devno = sysfs_devname_to_devno(devname, NULL);
1039
1040 if (!devno)
1041 err(EXIT_FAILURE, "failed to read devno");
1042
4419ffb9
KZ
1043 if (sysfs_init(&cxt, devno, NULL))
1044 return EXIT_FAILURE;
778ad369 1045
7fe16fda 1046 printf("NAME: %s\n", devname);
4419ffb9 1047 printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path)));
413993fc 1048 printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
4419ffb9
KZ
1049 printf("DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno));
1050 printf("DEVNO-PATH: %s\n", sysfs_devno_path(devno, path, sizeof(path)));
1051
1052 sysfs_devno_to_wholedisk(devno, diskname, sizeof(diskname), &disk_devno);
1053 printf("WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno, major(disk_devno), minor(disk_devno));
1054 printf("WHOLEDISK-DEVNAME: %s\n", diskname);
1055
1056 is_part = sysfs_devno_has_attribute(devno, "partition");
778ad369 1057 printf("PARTITION: %s\n", is_part ? "YES" : "NOT");
7fe16fda 1058
4419ffb9
KZ
1059 printf("HOTPLUG: %s\n", sysfs_is_hotpluggable(&cxt) ? "yes" : "no");
1060 printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves"));
7fe16fda 1061
413993fc
KZ
1062 len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1);
1063 if (len > 0) {
1064 path[len] = '\0';
1065 printf("DEVNOLINK: %s\n", path);
1066 }
1067
778ad369
KZ
1068 if (!is_part) {
1069 printf("First 5 partitions:\n");
1070 for (i = 1; i <= 5; i++) {
1071 dev_t dev = sysfs_partno_to_devno(&cxt, i);
1072 if (dev)
1073 printf("\t#%d %d:%d\n", i, major(dev), minor(dev));
1074 }
1075 }
1076
90e9fcda 1077 if (sysfs_read_u64(&cxt, "size", &u64))
413993fc 1078 printf("read SIZE failed\n");
90e9fcda
KZ
1079 else
1080 printf("SIZE: %jd\n", u64);
1081
1082 if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i))
413993fc 1083 printf("read SECTOR failed\n");
90e9fcda
KZ
1084 else
1085 printf("SECTOR: %d\n", i);
7fe16fda 1086
e017ef8b
KZ
1087
1088 chain = sysfs_get_devchain(&cxt, path, sizeof(path));
1089 printf("SUBSUSTEMS:\n");
1090
1091 while (chain && sysfs_next_subsystem(&cxt, chain, &sub) == 0) {
1092 printf("\t%s\n", sub);
1093 free(sub);
1094 }
1095
413993fc 1096
fb4a9e54 1097 sysfs_deinit(&cxt);
7fe16fda
KZ
1098 return EXIT_SUCCESS;
1099}
e8f7acb0 1100#endif /* TEST_PROGRAM_SYSFS */