2 * No copyright is claimed. This code is in the public domain; do with
5 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
14 #include "pathnames.h"
16 #include "fileutils.h"
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
);
26 * Debug stuff (based on include/debug.h)
28 static UL_DEBUG_DEFINE_MASK(ulsysfs
);
29 UL_DEBUG_DEFINE_MASKNAMES(ulsysfs
) = UL_DEBUG_EMPTY_MASKNAMES
;
31 #define ULSYSFS_DEBUG_INIT (1 << 1)
32 #define ULSYSFS_DEBUG_CXT (1 << 2)
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)
37 #define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulsysfs)
40 void ul_sysfs_init_debug(void)
42 if (ulsysfs_debug_mask
)
44 __UL_INIT_DEBUG_FROM_ENV(ulsysfs
, ULSYSFS_DEBUG_
, 0, ULSYSFS_DEBUG
);
47 struct path_cxt
*ul_new_sysfs_path(dev_t devno
, struct path_cxt
*parent
, const char *prefix
)
49 struct path_cxt
*pc
= ul_new_path(NULL
);
54 ul_path_set_prefix(pc
, prefix
);
56 if (sysfs_blkdev_init_path(pc
, devno
, parent
) != 0) {
61 DBG(CXT
, ul_debugobj(pc
, "alloc"));
66 * sysfs_blkdev_* is sysfs extension to ul_path_* API for block devices.
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().
72 int sysfs_blkdev_init_path(struct path_cxt
*pc
, dev_t devno
, struct path_cxt
*parent
)
74 struct sysfs_blkdev
*blk
;
76 char buf
[sizeof(_PATH_SYS_DEVBLOCK
)
77 + sizeof(stringify_value(UINT32_MAX
)) * 2
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
);
86 /* make sure path exists */
87 rc
= ul_path_get_dirfd(pc
);
91 /* initialize sysfs blkdev specific stuff */
92 blk
= ul_path_get_dialect(pc
);
94 DBG(CXT
, ul_debugobj(pc
, "alloc new sysfs handler"));
95 blk
= calloc(1, sizeof(struct sysfs_blkdev
));
99 ul_path_set_dialect(pc
, blk
, sysfs_blkdev_deinit_path
);
100 ul_path_set_enoent_redirect(pc
, sysfs_blkdev_enoent_redirect
);
103 DBG(CXT
, ul_debugobj(pc
, "init sysfs stuff"));
106 sysfs_blkdev_set_parent(pc
, parent
);
111 static void sysfs_blkdev_deinit_path(struct path_cxt
*pc
)
113 struct sysfs_blkdev
*blk
;
118 DBG(CXT
, ul_debugobj(pc
, "deinit"));
120 blk
= ul_path_get_dialect(pc
);
124 ul_unref_path(blk
->parent
);
127 ul_path_set_dialect(pc
, NULL
, NULL
);
130 int sysfs_blkdev_set_parent(struct path_cxt
*pc
, struct path_cxt
*parent
)
132 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
138 ul_unref_path(blk
->parent
);
144 blk
->parent
= parent
;
148 DBG(CXT
, ul_debugobj(pc
, "new parent"));
152 struct path_cxt
*sysfs_blkdev_get_parent(struct path_cxt
*pc
)
154 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
155 return blk
? blk
->parent
: NULL
;
159 * Redirects ENOENT errors to the parent.
162 * /sys/dev/block/8:1/queue/logical_block_size redirects to
163 * /sys/dev/block/8:0/queue/logical_block_size
165 static int sysfs_blkdev_enoent_redirect(struct path_cxt
*pc
, const char *path
, int *dirfd
)
167 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
169 if (blk
&& blk
->parent
&& path
) {
170 *dirfd
= ul_path_get_dirfd(blk
->parent
);
172 DBG(CXT
, ul_debugobj(pc
, "%s redirected to parent", path
));
176 return 1; /* no redirect */
179 char *sysfs_blkdev_get_name(struct path_cxt
*pc
, char *buf
, size_t bufsiz
)
185 /* read /sys/dev/block/<maj:min> link */
186 sz
= ul_path_readlink(pc
, link
, sizeof(link
), NULL
);
190 name
= strrchr(link
, '/');
196 if ((size_t) sz
+ 1 > bufsiz
)
199 memcpy(buf
, name
, sz
+ 1);
200 sysfs_devname_sys_to_dev(buf
);
204 int sysfs_blkdev_is_partition_dirent(DIR *dir
, struct dirent
*d
, const char *parent_name
)
206 char path
[NAME_MAX
+ 6 + 1];
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
)
217 const char *p
= parent_name
;
219 /* /dev/sda --> "sda" */
220 if (*parent_name
== '/') {
221 p
= strrchr(parent_name
, '/');
228 if ((strlen(d
->d_name
) <= len
) || (strncmp(p
, d
->d_name
, len
) != 0))
233 /* partitions subdir name is
234 * "<parent>[:digit:]" or "<parent>p[:digit:]"
236 return ((*(d
->d_name
+ len
) == 'p' && isdigit(*(d
->d_name
+ len
+ 1)))
237 || isdigit(*(d
->d_name
+ len
)));
240 /* Cannot use /partition file, not supported on old sysfs */
241 snprintf(path
, sizeof(path
), "%s/start", d
->d_name
);
243 return faccessat(dirfd(dir
), path
, R_OK
, 0) == 0;
246 int sysfs_blkdev_count_partitions(struct path_cxt
*pc
, const char *devname
)
252 dir
= ul_path_opendir(pc
, NULL
);
256 while ((d
= xreaddir(dir
))) {
257 if (sysfs_blkdev_is_partition_dirent(dir
, d
, devname
))
266 * Converts @partno (partition number) to devno of the partition.
267 * The @pc handles wholedisk device.
269 * Note that this code does not expect any special format of the
270 * partitions devnames.
272 dev_t
sysfs_blkdev_partno_to_devno(struct path_cxt
*pc
, int partno
)
278 dir
= ul_path_opendir(pc
, NULL
);
282 while ((d
= xreaddir(dir
))) {
285 if (!sysfs_blkdev_is_partition_dirent(dir
, d
, NULL
))
288 if (ul_path_readf_s32(pc
, &n
, "%s/partition", d
->d_name
))
292 if (ul_path_readf_majmin(pc
, &devno
, "%s/dev", d
->d_name
) == 0)
298 DBG(CXT
, ul_debugobj(pc
, "partno (%d) -> devno (%d)", (int) partno
, (int) devno
));
304 * Returns slave name if there is only one slave, otherwise returns NULL.
305 * The result should be deallocated by free().
307 char *sysfs_blkdev_get_slave(struct path_cxt
*pc
)
313 dir
= ul_path_opendir(pc
, "slaves");
317 while ((d
= xreaddir(dir
))) {
319 goto err
; /* more slaves */
320 name
= strdup(d
->d_name
);
332 #define SUBSYSTEM_LINKNAME "/subsystem"
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
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
344 static char *get_subsystem(char *chain
, char *buf
, size_t bufsz
)
349 if (!chain
|| !*chain
)
353 if (len
+ sizeof(SUBSYSTEM_LINKNAME
) > PATH_MAX
)
359 /* append "/subsystem" to the path */
360 memcpy(chain
+ len
, SUBSYSTEM_LINKNAME
, sizeof(SUBSYSTEM_LINKNAME
));
362 /* try if subsystem symlink exists */
363 sz
= readlink(chain
, buf
, bufsz
- 1);
365 /* remove last subsystem from chain */
367 p
= strrchr(chain
, '/');
374 /* we found symlink to subsystem, return basename */
376 return basename(buf
);
385 * Returns complete path to the device, the path contains all subsystems
386 * used for the device.
388 char *sysfs_blkdev_get_devchain(struct path_cxt
*pc
, char *buf
, size_t bufsz
)
390 /* read /sys/dev/block/<maj>:<min> symlink */
393 struct ul_buffer tmp
= UL_INIT_BUFFER
;
397 ssz
= ul_path_readlink(pc
, buf
, bufsz
, NULL
);
401 if ((p
= ul_path_get_prefix(pc
)))
402 ul_buffer_append_string(&tmp
, p
);
404 ul_buffer_append_string(&tmp
, _PATH_SYS_DEVBLOCK
"/");
405 ul_buffer_append_data(&tmp
, buf
, ssz
);
407 p
= ul_buffer_get_data(&tmp
, &sz
, NULL
);
408 if (p
&& sz
< bufsz
) {
412 ul_buffer_free_data(&tmp
);
417 * The @subsys returns the next subsystem in the chain. Function modifies
420 * Returns: 0 in success, <0 on error, 1 on end of chain
422 int sysfs_blkdev_next_subsystem(struct path_cxt
*pc
__attribute__((unused
)),
423 char *devchain
, char **subsys
)
425 char subbuf
[PATH_MAX
];
428 if (!subsys
|| !devchain
)
433 while ((sub
= get_subsystem(devchain
, subbuf
, sizeof(subbuf
)))) {
434 *subsys
= strdup(sub
);
443 #define REMOVABLE_FILENAME "/removable"
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
451 static int sysfs_devchain_is_removable(char *chain
)
457 if (!chain
|| !*chain
)
461 if (len
+ sizeof(REMOVABLE_FILENAME
) > PATH_MAX
)
467 /* append "/removable" to the path */
468 memcpy(chain
+ len
, REMOVABLE_FILENAME
, sizeof(REMOVABLE_FILENAME
));
471 fd
= open(chain
, O_RDONLY
);
473 rc
= read_all(fd
, buf
, sizeof(buf
));
477 if (strncmp(buf
, "fixed", min(rc
, 5)) == 0) {
479 } else if (strncmp(buf
, "removable", min(rc
, 9)) == 0) {
485 /* remove last subsystem from chain */
487 p
= strrchr(chain
, '/');
498 int sysfs_blkdev_is_hotpluggable(struct path_cxt
*pc
)
500 char buf
[PATH_MAX
], *chain
;
502 chain
= sysfs_blkdev_get_devchain(pc
, buf
, sizeof(buf
));
503 return sysfs_devchain_is_removable(chain
);
506 int sysfs_blkdev_is_removable(struct path_cxt
*pc
)
510 // FIXME usb is not actually removable
512 /* check /sys/dev/block/<maj>:<min>/removable attribute */
513 if (ul_path_read_s32(pc
, &rc
, "removable") == 0)
519 static int get_dm_wholedisk(struct path_cxt
*pc
, char *diskname
,
520 size_t len
, dev_t
*diskdevno
)
525 /* Note, sysfs_blkdev_get_slave() returns the first slave only,
526 * if there is more slaves, then return NULL
528 name
= sysfs_blkdev_get_slave(pc
);
533 xstrncpy(diskname
, name
, len
);
536 *diskdevno
= __sysfs_devname_to_devno(ul_path_get_prefix(pc
), name
, NULL
);
546 * Returns by @diskdevno whole disk device devno and (optionally) by
547 * @diskname the whole disk device name.
549 int sysfs_blkdev_get_wholedisk( struct path_cxt
*pc
,
559 is_part
= ul_path_access(pc
, F_OK
, "partition") == 0;
562 * Extra case for partitions mapped by device-mapper.
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"
569 char *uuid
= NULL
, *tmp
, *prefix
;
571 ul_path_read_string(pc
, &uuid
, "dm/uuid");
573 prefix
= uuid
? strsep(&tmp
, "-") : NULL
;
575 if (prefix
&& strncasecmp(prefix
, "part", 4) == 0)
580 get_dm_wholedisk(pc
, diskname
, len
, diskdevno
) == 0)
582 * partitioned device, mapped by DM
591 * unpartitioned device
593 if (diskname
&& !sysfs_blkdev_get_name(pc
, diskname
, len
))
596 *diskdevno
= sysfs_blkdev_get_devno(pc
);
601 * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
602 * - dirname ../../block/sda/sda1 = ../../block/sda
603 * - basename ../../block/sda = sda
605 char linkpath
[PATH_MAX
];
609 linklen
= ul_path_readlink(pc
, linkpath
, sizeof(linkpath
), NULL
);
613 stripoff_last_component(linkpath
); /* dirname */
614 name
= stripoff_last_component(linkpath
); /* basename */
618 sysfs_devname_sys_to_dev(name
);
620 xstrncpy(diskname
, name
, len
);
623 *diskdevno
= __sysfs_devname_to_devno(ul_path_get_prefix(pc
), name
, NULL
);
635 int sysfs_devno_to_wholedisk(dev_t devno
, char *diskname
,
636 size_t len
, dev_t
*diskdevno
)
643 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
647 rc
= sysfs_blkdev_get_wholedisk(pc
, diskname
, len
, diskdevno
);
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.
656 int sysfs_devno_is_dm_private(dev_t devno
, char **uuid
)
658 struct path_cxt
*pc
= NULL
;
662 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
665 if (ul_path_read_string(pc
, &id
, "dm/uuid") <= 0 || !id
)
668 /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
669 * is the "LVM" prefix and "-<name>" postfix).
671 if (strncmp(id
, "LVM-", 4) == 0) {
672 char *p
= strrchr(id
+ 4, '-');
677 /* Private Stratis devices prefix the UUID with "stratis-1-private"
679 } else if (strncmp(id
, "stratis-1-private", 17) == 0) {
692 * Return 0 or 1, or < 0 in case of error
694 int sysfs_devno_is_wholedisk(dev_t devno
)
698 if (sysfs_devno_to_wholedisk(devno
, NULL
, 0, &disk
) != 0)
701 return devno
== disk
;
705 int sysfs_blkdev_scsi_get_hctl(struct path_cxt
*pc
, int *h
, int *c
, int *t
, int *l
)
707 char buf
[PATH_MAX
], *hctl
;
708 struct sysfs_blkdev
*blk
;
711 blk
= ul_path_get_dialect(pc
);
713 if (!blk
|| blk
->hctl_error
)
719 len
= ul_path_readlink(pc
, buf
, sizeof(buf
), "device");
723 hctl
= strrchr(buf
, '/');
728 if (sscanf(hctl
, "%u:%u:%u:%u", &blk
->scsi_host
, &blk
->scsi_channel
,
729 &blk
->scsi_target
, &blk
->scsi_lun
) != 4)
737 *c
= blk
->scsi_channel
;
739 *t
= blk
->scsi_target
;
748 static char *scsi_host_attribute_path(
759 if (sysfs_blkdev_scsi_get_hctl(pc
, &host
, NULL
, NULL
, NULL
))
762 prefix
= ul_path_get_prefix(pc
);
767 len
= snprintf(buf
, bufsz
, "%s%s/%s_host/host%d/%s",
768 prefix
, _PATH_SYS_CLASS
, type
, host
, attr
);
770 len
= snprintf(buf
, bufsz
, "%s%s/%s_host/host%d",
771 prefix
, _PATH_SYS_CLASS
, type
, host
);
773 return (len
< 0 || (size_t) len
>= bufsz
) ? NULL
: buf
;
776 char *sysfs_blkdev_scsi_host_strdup_attribute(struct path_cxt
*pc
,
777 const char *type
, const char *attr
)
783 if (!attr
|| !type
||
784 !scsi_host_attribute_path(pc
, type
, buf
, sizeof(buf
), attr
))
787 if (!(f
= fopen(buf
, "r" UL_CLOEXECSTR
)))
790 rc
= fscanf(f
, "%1023[^\n]", buf
);
793 return rc
== 1 ? strdup(buf
) : NULL
;
796 int sysfs_blkdev_scsi_host_is(struct path_cxt
*pc
, const char *type
)
801 if (!type
|| !scsi_host_attribute_path(pc
, type
,
802 buf
, sizeof(buf
), NULL
))
805 return stat(buf
, &st
) == 0 && S_ISDIR(st
.st_mode
);
808 static char *scsi_attribute_path(struct path_cxt
*pc
,
809 char *buf
, size_t bufsz
, const char *attr
)
814 if (sysfs_blkdev_scsi_get_hctl(pc
, &h
, &c
, &t
, &l
) != 0)
817 prefix
= ul_path_get_prefix(pc
);
822 len
= snprintf(buf
, bufsz
, "%s%s/devices/%d:%d:%d:%d/%s",
823 prefix
, _PATH_SYS_SCSI
,
826 len
= snprintf(buf
, bufsz
, "%s%s/devices/%d:%d:%d:%d",
827 prefix
, _PATH_SYS_SCSI
,
829 return (len
< 0 || (size_t) len
>= bufsz
) ? NULL
: buf
;
832 int sysfs_blkdev_scsi_has_attribute(struct path_cxt
*pc
, const char *attr
)
837 if (!scsi_attribute_path(pc
, path
, sizeof(path
), attr
))
840 return stat(path
, &st
) == 0;
843 int sysfs_blkdev_scsi_path_contains(struct path_cxt
*pc
, const char *pattern
)
845 char path
[PATH_MAX
], linkc
[PATH_MAX
];
849 if (!scsi_attribute_path(pc
, path
, sizeof(path
), NULL
))
852 if (stat(path
, &st
) != 0)
855 len
= readlink(path
, linkc
, sizeof(linkc
) - 1);
860 return strstr(linkc
, pattern
) != NULL
;
863 static dev_t
read_devno(const char *path
)
866 int maj
= 0, min
= 0;
869 f
= fopen(path
, "r" UL_CLOEXECSTR
);
873 if (fscanf(f
, "%d:%d", &maj
, &min
) == 2)
874 dev
= makedev(maj
, min
);
879 int sysfs_devname_is_hidden(const char *prefix
, const char *name
)
882 int rc
= 0, hidden
= 0, len
;
885 if (strncmp("/dev/", name
, 5) == 0)
891 * Create path to /sys/block/<name>/hidden
893 len
= snprintf(buf
, sizeof(buf
),
894 "%s" _PATH_SYS_BLOCK
"/%s/hidden",
897 if (len
< 0 || (size_t) len
+ 1 > sizeof(buf
))
900 f
= fopen(buf
, "r" UL_CLOEXECSTR
);
904 rc
= fscanf(f
, "%d", &hidden
);
907 return rc
== 1 ? hidden
: 0;
911 dev_t
__sysfs_devname_to_devno(const char *prefix
, const char *name
, const char *parent
)
914 char *_name
= NULL
, *_parent
= NULL
; /* name as encoded in sysfs */
923 if (strncmp("/dev/", name
, 5) == 0) {
929 if (stat(name
, &st
) == 0) {
933 name
+= 5; /* unaccessible, or not node in /dev */
936 _name
= strdup(name
);
939 sysfs_devname_dev_to_sys(_name
);
942 _parent
= strdup(parent
);
947 if (parent
&& strncmp("dm-", name
, 3) != 0) {
949 * Create path to /sys/block/<parent>/<name>/dev
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
))
958 /* don't try anything else for dm-* */
959 dev
= read_devno(buf
);
964 * Read from /sys/block/<sysname>/dev
966 len
= snprintf(buf
, sizeof(buf
),
967 "%s" _PATH_SYS_BLOCK
"/%s/dev",
969 if (len
< 0 || (size_t) len
>= sizeof(buf
))
971 dev
= read_devno(buf
);
974 * Read from /sys/block/<parent>/<partition>/dev
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
))
982 dev
= read_devno(buf
);
986 * Read from /sys/block/<sysname>/device/dev
989 len
= snprintf(buf
, sizeof(buf
),
990 "%s" _PATH_SYS_BLOCK
"/%s/device/dev",
992 if (len
< 0 || (size_t) len
>= sizeof(buf
))
994 dev
= read_devno(buf
);
1002 dev_t
sysfs_devname_to_devno(const char *name
)
1004 return __sysfs_devname_to_devno(NULL
, name
, NULL
);
1007 char *sysfs_blkdev_get_path(struct path_cxt
*pc
, char *buf
, size_t bufsiz
)
1009 const char *name
= sysfs_blkdev_get_name(pc
, buf
, bufsiz
);
1018 if (sz
+ sizeof("/dev/") > bufsiz
)
1021 /* create the final "/dev/<name>" string */
1022 memmove(buf
+ 5, name
, sz
+ 1);
1023 memcpy(buf
, "/dev/", 5);
1025 if (!stat(buf
, &st
) && S_ISBLK(st
.st_mode
) && st
.st_rdev
== sysfs_blkdev_get_devno(pc
))
1031 dev_t
sysfs_blkdev_get_devno(struct path_cxt
*pc
)
1033 return ((struct sysfs_blkdev
*) ul_path_get_dialect(pc
))->devno
;
1037 * Returns devname (e.g. "/dev/sda1") for the given devno.
1039 * Please, use more robust blkid_devno_to_devname() in your applications.
1041 char *sysfs_devno_to_devpath(dev_t devno
, char *buf
, size_t bufsiz
)
1043 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
1047 res
= sysfs_blkdev_get_path(pc
, buf
, bufsiz
);
1053 char *sysfs_devno_to_devname(dev_t devno
, char *buf
, size_t bufsiz
)
1055 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
1059 res
= sysfs_blkdev_get_name(pc
, buf
, bufsiz
);
1065 int sysfs_devno_count_partitions(dev_t devno
)
1067 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
1071 char buf
[PATH_MAX
+ 1];
1072 char *name
= sysfs_blkdev_get_name(pc
, buf
, sizeof(buf
));
1074 n
= sysfs_blkdev_count_partitions(pc
, name
);
1080 char *sysfs_chrdev_devno_to_devname(dev_t devno
, char *buf
, size_t bufsiz
)
1082 char link
[PATH_MAX
];
1083 struct path_cxt
*pc
;
1087 pc
= ul_new_path(_PATH_SYS_DEVCHAR
"/%u:%u", major(devno
), minor(devno
));
1091 /* read /sys/dev/char/<maj:min> link */
1092 sz
= ul_path_readlink(pc
, link
, sizeof(link
), NULL
);
1098 name
= strrchr(link
, '/');
1104 if ((size_t) sz
+ 1 > bufsiz
)
1107 memcpy(buf
, name
, sz
+ 1);
1108 sysfs_devname_sys_to_dev(buf
);
1113 enum sysfs_byteorder
sysfs_get_byteorder(struct path_cxt
*pc
)
1117 enum sysfs_byteorder ret
;
1119 rc
= ul_path_read_buffer(pc
, buf
, sizeof(buf
), _PATH_SYS_CPU_BYTEORDER
);
1123 if (strncmp(buf
, "little", sizeof(buf
)) == 0) {
1124 ret
= SYSFS_BYTEORDER_LITTLE
;
1126 } else if (strncmp(buf
, "big", sizeof(buf
)) == 0) {
1127 ret
= SYSFS_BYTEORDER_BIG
;
1132 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1133 ret
= SYSFS_BYTEORDER_LITTLE
;
1134 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1135 ret
= SYSFS_BYTEORDER_BIG
;
1137 #error Unknown byte order
1144 int sysfs_get_address_bits(struct path_cxt
*pc
)
1149 rc
= ul_path_scanf(pc
, _PATH_SYS_ADDRESS_BITS
, "%d", &address_bits
);
1152 if (address_bits
< 0)
1154 return address_bits
;
1158 #ifdef TEST_PROGRAM_SYSFS
1163 int main(int argc
, char *argv
[])
1165 struct path_cxt
*pc
;
1167 dev_t devno
, disk_devno
;
1168 char path
[PATH_MAX
], *sub
, *chain
;
1170 int i
, is_part
, rc
= EXIT_SUCCESS
;
1174 errx(EXIT_FAILURE
, "usage: %s <devname>", argv
[0]);
1176 ul_sysfs_init_debug();
1179 devno
= sysfs_devname_to_devno(devname
);
1182 err(EXIT_FAILURE
, "failed to read devno");
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
)));
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
);
1193 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
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
)));
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
);
1207 is_part
= ul_path_access(pc
, F_OK
, "partition") == 0;
1208 printf(" PARTITION: %s\n", is_part
? "YES" : "NOT");
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
);
1214 ul_unref_path(disk_pc
);
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"));
1222 printf("First 5 partitions:\n");
1223 for (i
= 1; i
<= 5; i
++) {
1224 dev_t dev
= sysfs_blkdev_partno_to_devno(pc
, i
);
1226 printf("\t#%d %d:%d\n", i
, major(dev
), minor(dev
));
1230 if (ul_path_read_u64(pc
, &u64
, "size") != 0)
1231 printf(" (!) read SIZE failed\n");
1233 printf(" SIZE: %jd\n", u64
);
1235 if (ul_path_read_s32(pc
, &i
, "queue/hw_sector_size"))
1236 printf(" (!) read SECTOR failed\n");
1238 printf(" SECTOR: %d\n", i
);
1241 chain
= sysfs_blkdev_get_devchain(pc
, path
, sizeof(path
));
1242 printf(" SUBSYSTEMS:\n");
1244 while (chain
&& sysfs_blkdev_next_subsystem(pc
, chain
, &sub
) == 0) {
1245 printf("\t%s\n", sub
);
1254 #endif /* TEST_PROGRAM_SYSFS */