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