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