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