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