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