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