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