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