2 * No copyright is claimed. This code is in the public domain; do with
5 * Written by Karel Zak <kzak@redhat.com> [2011]
15 #include "pathnames.h"
17 #include "fileutils.h"
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
);
27 * Debug stuff (based on include/debug.h)
29 static UL_DEBUG_DEFINE_MASK(ulsysfs
);
30 UL_DEBUG_DEFINE_MASKNAMES(ulsysfs
) = UL_DEBUG_EMPTY_MASKNAMES
;
32 #define ULSYSFS_DEBUG_INIT (1 << 1)
33 #define ULSYSFS_DEBUG_CXT (1 << 2)
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)
38 #define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulsysfs)
41 void ul_sysfs_init_debug(void)
43 if (ulsysfs_debug_mask
)
45 __UL_INIT_DEBUG_FROM_ENV(ulsysfs
, ULSYSFS_DEBUG_
, 0, ULSYSFS_DEBUG
);
48 struct path_cxt
*ul_new_sysfs_path(dev_t devno
, struct path_cxt
*parent
, const char *prefix
)
50 struct path_cxt
*pc
= ul_new_path(NULL
);
55 ul_path_set_prefix(pc
, prefix
);
57 if (sysfs_blkdev_init_path(pc
, devno
, parent
) != 0) {
62 DBG(CXT
, ul_debugobj(pc
, "alloc"));
67 * sysfs_blkdev_* is sysfs extension to ul_path_* API for block devices.
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().
73 int sysfs_blkdev_init_path(struct path_cxt
*pc
, dev_t devno
, struct path_cxt
*parent
)
75 struct sysfs_blkdev
*blk
;
77 char buf
[sizeof(_PATH_SYS_DEVBLOCK
)
78 + sizeof(stringify_value(UINT32_MAX
)) * 2
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
);
87 /* make sure path exists */
88 rc
= ul_path_get_dirfd(pc
);
92 /* initialize sysfs blkdev specific stuff */
93 blk
= ul_path_get_dialect(pc
);
95 DBG(CXT
, ul_debugobj(pc
, "alloc new sysfs handler"));
96 blk
= calloc(1, sizeof(struct sysfs_blkdev
));
100 ul_path_set_dialect(pc
, blk
, sysfs_blkdev_deinit_path
);
101 ul_path_set_enoent_redirect(pc
, sysfs_blkdev_enoent_redirect
);
104 DBG(CXT
, ul_debugobj(pc
, "init sysfs stuff"));
107 sysfs_blkdev_set_parent(pc
, parent
);
112 static void sysfs_blkdev_deinit_path(struct path_cxt
*pc
)
114 struct sysfs_blkdev
*blk
;
119 DBG(CXT
, ul_debugobj(pc
, "deinit"));
121 blk
= ul_path_get_dialect(pc
);
125 ul_unref_path(blk
->parent
);
128 ul_path_set_dialect(pc
, NULL
, NULL
);
131 int sysfs_blkdev_set_parent(struct path_cxt
*pc
, struct path_cxt
*parent
)
133 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
139 ul_unref_path(blk
->parent
);
145 blk
->parent
= parent
;
149 DBG(CXT
, ul_debugobj(pc
, "new parent"));
153 struct path_cxt
*sysfs_blkdev_get_parent(struct path_cxt
*pc
)
155 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
156 return blk
? blk
->parent
: NULL
;
160 * Redirects ENOENT errors to the parent.
163 * /sys/dev/block/8:1/queue/logical_block_size redirects to
164 * /sys/dev/block/8:0/queue/logical_block_size
166 static int sysfs_blkdev_enoent_redirect(struct path_cxt
*pc
, const char *path
, int *dirfd
)
168 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
170 if (blk
&& blk
->parent
&& path
) {
171 *dirfd
= ul_path_get_dirfd(blk
->parent
);
173 DBG(CXT
, ul_debugobj(pc
, "%s redirected to parent", path
));
177 return 1; /* no redirect */
180 char *sysfs_blkdev_get_name(struct path_cxt
*pc
, char *buf
, size_t bufsiz
)
186 /* read /sys/dev/block/<maj:min> link */
187 sz
= ul_path_readlink(pc
, link
, sizeof(link
), NULL
);
191 name
= strrchr(link
, '/');
197 if ((size_t) sz
+ 1 > bufsiz
)
200 memcpy(buf
, name
, sz
+ 1);
201 sysfs_devname_sys_to_dev(buf
);
205 int sysfs_blkdev_is_partition_dirent(DIR *dir
, struct dirent
*d
, const char *parent_name
)
207 char path
[NAME_MAX
+ 6 + 1];
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
)
218 const char *p
= parent_name
;
220 /* /dev/sda --> "sda" */
221 if (*parent_name
== '/') {
222 p
= strrchr(parent_name
, '/');
229 if ((strlen(d
->d_name
) <= len
) || (strncmp(p
, d
->d_name
, len
) != 0))
234 /* partitions subdir name is
235 * "<parent>[:digit:]" or "<parent>p[:digit:]"
237 return ((*(d
->d_name
+ len
) == 'p' && isdigit(*(d
->d_name
+ len
+ 1)))
238 || isdigit(*(d
->d_name
+ len
)));
241 /* Cannot use /partition file, not supported on old sysfs */
242 snprintf(path
, sizeof(path
), "%s/start", d
->d_name
);
244 return faccessat(dirfd(dir
), path
, R_OK
, 0) == 0;
247 int sysfs_blkdev_count_partitions(struct path_cxt
*pc
, const char *devname
)
253 dir
= ul_path_opendir(pc
, NULL
);
257 while ((d
= xreaddir(dir
))) {
258 if (sysfs_blkdev_is_partition_dirent(dir
, d
, devname
))
267 * Converts @partno (partition number) to devno of the partition.
268 * The @pc handles wholedisk device.
270 * Note that this code does not expect any special format of the
271 * partitions devnames.
273 dev_t
sysfs_blkdev_partno_to_devno(struct path_cxt
*pc
, int partno
)
279 dir
= ul_path_opendir(pc
, NULL
);
283 while ((d
= xreaddir(dir
))) {
286 if (!sysfs_blkdev_is_partition_dirent(dir
, d
, NULL
))
289 if (ul_path_readf_s32(pc
, &n
, "%s/partition", d
->d_name
))
293 if (ul_path_readf_majmin(pc
, &devno
, "%s/dev", d
->d_name
) == 0)
299 DBG(CXT
, ul_debugobj(pc
, "partno (%d) -> devno (%d)", (int) partno
, (int) devno
));
305 * Returns slave name if there is only one slave, otherwise returns NULL.
306 * The result should be deallocated by free().
308 char *sysfs_blkdev_get_slave(struct path_cxt
*pc
)
314 dir
= ul_path_opendir(pc
, "slaves");
318 while ((d
= xreaddir(dir
))) {
320 goto err
; /* more slaves */
321 name
= strdup(d
->d_name
);
333 #define SUBSYSTEM_LINKNAME "/subsystem"
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
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
345 static char *get_subsystem(char *chain
, char *buf
, size_t bufsz
)
350 if (!chain
|| !*chain
)
354 if (len
+ sizeof(SUBSYSTEM_LINKNAME
) > PATH_MAX
)
360 /* append "/subsystem" to the path */
361 memcpy(chain
+ len
, SUBSYSTEM_LINKNAME
, sizeof(SUBSYSTEM_LINKNAME
));
363 /* try if subsystem symlink exists */
364 sz
= readlink(chain
, buf
, bufsz
- 1);
366 /* remove last subsystem from chain */
368 p
= strrchr(chain
, '/');
375 /* we found symlink to subsystem, return basename */
377 return basename(buf
);
386 * Returns complete path to the device, the path contains all subsystems
387 * used for the device.
389 char *sysfs_blkdev_get_devchain(struct path_cxt
*pc
, char *buf
, size_t bufsz
)
391 /* read /sys/dev/block/<maj>:<min> symlink */
394 struct ul_buffer tmp
= UL_INIT_BUFFER
;
398 ssz
= ul_path_readlink(pc
, buf
, bufsz
, NULL
);
402 if ((p
= ul_path_get_prefix(pc
)))
403 ul_buffer_append_string(&tmp
, p
);
405 ul_buffer_append_string(&tmp
, _PATH_SYS_DEVBLOCK
"/");
406 ul_buffer_append_data(&tmp
, buf
, ssz
);
408 p
= ul_buffer_get_string(&tmp
, &sz
, NULL
);
409 if (p
&& sz
<= bufsz
) {
413 ul_buffer_free_data(&tmp
);
418 * The @subsys returns the next subsystem in the chain. Function modifies
421 * Returns: 0 in success, <0 on error, 1 on end of chain
423 int sysfs_blkdev_next_subsystem(struct path_cxt
*pc
__attribute__((unused
)),
424 char *devchain
, char **subsys
)
426 char subbuf
[PATH_MAX
];
429 if (!subsys
|| !devchain
)
434 while ((sub
= get_subsystem(devchain
, subbuf
, sizeof(subbuf
)))) {
435 *subsys
= strdup(sub
);
444 #define REMOVABLE_FILENAME "/removable"
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
452 static int sysfs_devchain_is_removable(char *chain
)
458 if (!chain
|| !*chain
)
462 if (len
+ sizeof(REMOVABLE_FILENAME
) > PATH_MAX
)
468 /* append "/removable" to the path */
469 memcpy(chain
+ len
, REMOVABLE_FILENAME
, sizeof(REMOVABLE_FILENAME
));
471 /* root of device hierarchy */
472 if (strcmp(chain
, "/sys/dev/block" REMOVABLE_FILENAME
) == 0)
476 fd
= open(chain
, O_RDONLY
);
478 rc
= read_all(fd
, buf
, sizeof(buf
));
482 if (strncmp(buf
, "fixed", min(rc
, 5)) == 0) {
484 } else if (strncmp(buf
, "removable", min(rc
, 9)) == 0) {
490 /* remove last subsystem from chain */
492 p
= strrchr(chain
, '/');
503 int sysfs_blkdev_is_hotpluggable(struct path_cxt
*pc
)
505 char buf
[PATH_MAX
], *chain
;
507 chain
= sysfs_blkdev_get_devchain(pc
, buf
, sizeof(buf
));
508 return sysfs_devchain_is_removable(chain
);
511 int sysfs_blkdev_is_removable(struct path_cxt
*pc
)
515 // FIXME usb is not actually removable
517 /* check /sys/dev/block/<maj>:<min>/removable attribute */
518 if (ul_path_read_s32(pc
, &rc
, "removable") == 0)
524 static int get_dm_wholedisk(struct path_cxt
*pc
, char *diskname
,
525 size_t len
, dev_t
*diskdevno
)
530 /* Note, sysfs_blkdev_get_slave() returns the first slave only,
531 * if there is more slaves, then return NULL
533 name
= sysfs_blkdev_get_slave(pc
);
538 xstrncpy(diskname
, name
, len
);
541 *diskdevno
= __sysfs_devname_to_devno(ul_path_get_prefix(pc
), name
, NULL
);
551 * Returns by @diskdevno whole disk device devno and (optionally) by
552 * @diskname the whole disk device name.
554 int sysfs_blkdev_get_wholedisk( struct path_cxt
*pc
,
564 is_part
= ul_path_access(pc
, F_OK
, "partition") == 0;
567 * Extra case for partitions mapped by device-mapper.
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"
574 char *uuid
= NULL
, *tmp
, *prefix
;
576 ul_path_read_string(pc
, &uuid
, "dm/uuid");
578 prefix
= uuid
? strsep(&tmp
, "-") : NULL
;
580 if (prefix
&& c_strncasecmp(prefix
, "part", 4) == 0)
585 get_dm_wholedisk(pc
, diskname
, len
, diskdevno
) == 0)
587 * partitioned device, mapped by DM
596 * unpartitioned device
598 if (diskname
&& !sysfs_blkdev_get_name(pc
, diskname
, len
))
601 *diskdevno
= sysfs_blkdev_get_devno(pc
);
606 * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
607 * - dirname ../../block/sda/sda1 = ../../block/sda
608 * - basename ../../block/sda = sda
610 char linkpath
[PATH_MAX
];
614 linklen
= ul_path_readlink(pc
, linkpath
, sizeof(linkpath
), NULL
);
618 stripoff_last_component(linkpath
); /* dirname */
619 name
= stripoff_last_component(linkpath
); /* basename */
623 sysfs_devname_sys_to_dev(name
);
625 xstrncpy(diskname
, name
, len
);
628 *diskdevno
= __sysfs_devname_to_devno(ul_path_get_prefix(pc
), name
, NULL
);
640 int sysfs_devno_to_wholedisk(dev_t devno
, char *diskname
,
641 size_t len
, dev_t
*diskdevno
)
648 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
652 rc
= sysfs_blkdev_get_wholedisk(pc
, diskname
, len
, diskdevno
);
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.
661 int sysfs_devno_is_dm_private(dev_t devno
, char **uuid
)
663 struct path_cxt
*pc
= NULL
;
667 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
670 if (ul_path_read_string(pc
, &id
, "dm/uuid") <= 0 || !id
)
673 /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
674 * is the "LVM" prefix and "-<name>" postfix).
676 if (strncmp(id
, "LVM-", 4) == 0) {
677 char *p
= strrchr(id
+ 4, '-');
682 /* Private Stratis devices prefix the UUID with "stratis-1-private"
684 } else if (strncmp(id
, "stratis-1-private", 17) == 0) {
697 * Return 0 or 1, or < 0 in case of error
699 int sysfs_devno_is_wholedisk(dev_t devno
)
703 if (sysfs_devno_to_wholedisk(devno
, NULL
, 0, &disk
) != 0)
706 return devno
== disk
;
710 int sysfs_blkdev_scsi_get_hctl(struct path_cxt
*pc
, int *h
, int *c
, int *t
, int *l
)
712 char buf
[PATH_MAX
], *hctl
;
713 struct sysfs_blkdev
*blk
;
716 blk
= ul_path_get_dialect(pc
);
718 if (!blk
|| blk
->hctl_error
)
724 len
= ul_path_readlink(pc
, buf
, sizeof(buf
), "device");
728 hctl
= strrchr(buf
, '/');
733 if (sscanf(hctl
, "%u:%u:%u:%u", &blk
->scsi_host
, &blk
->scsi_channel
,
734 &blk
->scsi_target
, &blk
->scsi_lun
) != 4)
742 *c
= blk
->scsi_channel
;
744 *t
= blk
->scsi_target
;
753 static char *scsi_host_attribute_path(
764 if (sysfs_blkdev_scsi_get_hctl(pc
, &host
, NULL
, NULL
, NULL
))
767 prefix
= ul_path_get_prefix(pc
);
772 len
= snprintf(buf
, bufsz
, "%s%s/%s_host/host%d/%s",
773 prefix
, _PATH_SYS_CLASS
, type
, host
, attr
);
775 len
= snprintf(buf
, bufsz
, "%s%s/%s_host/host%d",
776 prefix
, _PATH_SYS_CLASS
, type
, host
);
778 return (len
< 0 || (size_t) len
>= bufsz
) ? NULL
: buf
;
781 char *sysfs_blkdev_scsi_host_strdup_attribute(struct path_cxt
*pc
,
782 const char *type
, const char *attr
)
788 if (!attr
|| !type
||
789 !scsi_host_attribute_path(pc
, type
, buf
, sizeof(buf
), attr
))
792 if (!(f
= fopen(buf
, "r" UL_CLOEXECSTR
)))
795 rc
= fscanf(f
, "%1023[^\n]", buf
);
798 return rc
== 1 ? strdup(buf
) : NULL
;
801 int sysfs_blkdev_scsi_host_is(struct path_cxt
*pc
, const char *type
)
806 if (!type
|| !scsi_host_attribute_path(pc
, type
,
807 buf
, sizeof(buf
), NULL
))
810 return stat(buf
, &st
) == 0 && S_ISDIR(st
.st_mode
);
813 static char *scsi_attribute_path(struct path_cxt
*pc
,
814 char *buf
, size_t bufsz
, const char *attr
)
819 if (sysfs_blkdev_scsi_get_hctl(pc
, &h
, &c
, &t
, &l
) != 0)
822 prefix
= ul_path_get_prefix(pc
);
827 len
= snprintf(buf
, bufsz
, "%s%s/devices/%d:%d:%d:%d/%s",
828 prefix
, _PATH_SYS_SCSI
,
831 len
= snprintf(buf
, bufsz
, "%s%s/devices/%d:%d:%d:%d",
832 prefix
, _PATH_SYS_SCSI
,
834 return (len
< 0 || (size_t) len
>= bufsz
) ? NULL
: buf
;
837 int sysfs_blkdev_scsi_has_attribute(struct path_cxt
*pc
, const char *attr
)
842 if (!scsi_attribute_path(pc
, path
, sizeof(path
), attr
))
845 return stat(path
, &st
) == 0;
848 int sysfs_blkdev_scsi_path_contains(struct path_cxt
*pc
, const char *pattern
)
850 char path
[PATH_MAX
], linkc
[PATH_MAX
];
854 if (!scsi_attribute_path(pc
, path
, sizeof(path
), NULL
))
857 if (stat(path
, &st
) != 0)
860 len
= readlink(path
, linkc
, sizeof(linkc
) - 1);
865 return strstr(linkc
, pattern
) != NULL
;
868 static dev_t
read_devno(const char *path
)
871 int maj
= 0, min
= 0;
874 f
= fopen(path
, "r" UL_CLOEXECSTR
);
878 if (fscanf(f
, "%d:%d", &maj
, &min
) == 2)
879 dev
= makedev(maj
, min
);
884 int sysfs_devname_is_hidden(const char *prefix
, const char *name
)
887 int rc
= 0, hidden
= 0, len
;
890 if (strncmp("/dev/", name
, 5) == 0)
896 * Create path to /sys/block/<name>/hidden
898 len
= snprintf(buf
, sizeof(buf
),
899 "%s" _PATH_SYS_BLOCK
"/%s/hidden",
902 if (len
< 0 || (size_t) len
+ 1 > sizeof(buf
))
905 f
= fopen(buf
, "r" UL_CLOEXECSTR
);
909 rc
= fscanf(f
, "%d", &hidden
);
912 return rc
== 1 ? hidden
: 0;
916 dev_t
__sysfs_devname_to_devno(const char *prefix
, const char *name
, const char *parent
)
919 char *_name
= NULL
, *_parent
= NULL
; /* name as encoded in sysfs */
928 if (strncmp("/dev/", name
, 5) == 0) {
934 if (stat(name
, &st
) == 0) {
938 name
+= 5; /* unaccessible, or not node in /dev */
941 _name
= strdup(name
);
944 sysfs_devname_dev_to_sys(_name
);
947 _parent
= strdup(parent
);
952 if (parent
&& strncmp("dm-", name
, 3) != 0) {
954 * Create path to /sys/block/<parent>/<name>/dev
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
))
963 /* don't try anything else for dm-* */
964 dev
= read_devno(buf
);
969 * Read from /sys/block/<sysname>/dev
971 len
= snprintf(buf
, sizeof(buf
),
972 "%s" _PATH_SYS_BLOCK
"/%s/dev",
974 if (len
< 0 || (size_t) len
>= sizeof(buf
))
976 dev
= read_devno(buf
);
979 * Read from /sys/block/<parent>/<partition>/dev
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
))
987 dev
= read_devno(buf
);
991 * Read from /sys/block/<sysname>/device/dev
994 len
= snprintf(buf
, sizeof(buf
),
995 "%s" _PATH_SYS_BLOCK
"/%s/device/dev",
997 if (len
< 0 || (size_t) len
>= sizeof(buf
))
999 dev
= read_devno(buf
);
1007 dev_t
sysfs_devname_to_devno(const char *name
)
1009 return __sysfs_devname_to_devno(NULL
, name
, NULL
);
1012 char *sysfs_blkdev_get_path(struct path_cxt
*pc
, char *buf
, size_t bufsiz
)
1014 const char *name
= sysfs_blkdev_get_name(pc
, buf
, bufsiz
);
1023 if (sz
+ sizeof("/dev/") > bufsiz
)
1026 /* create the final "/dev/<name>" string */
1027 memmove(buf
+ 5, name
, sz
+ 1);
1028 memcpy(buf
, "/dev/", 5);
1030 if (!stat(buf
, &st
) && S_ISBLK(st
.st_mode
) && st
.st_rdev
== sysfs_blkdev_get_devno(pc
))
1036 dev_t
sysfs_blkdev_get_devno(struct path_cxt
*pc
)
1038 return ((struct sysfs_blkdev
*) ul_path_get_dialect(pc
))->devno
;
1042 * Returns devname (e.g. "/dev/sda1") for the given devno.
1044 * Please, use more robust blkid_devno_to_devname() in your applications.
1046 char *sysfs_devno_to_devpath(dev_t devno
, char *buf
, size_t bufsiz
)
1048 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
1052 res
= sysfs_blkdev_get_path(pc
, buf
, bufsiz
);
1058 char *sysfs_devno_to_devname(dev_t devno
, char *buf
, size_t bufsiz
)
1060 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
1064 res
= sysfs_blkdev_get_name(pc
, buf
, bufsiz
);
1070 int sysfs_devno_count_partitions(dev_t devno
)
1072 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
1076 char buf
[PATH_MAX
+ 1];
1077 char *name
= sysfs_blkdev_get_name(pc
, buf
, sizeof(buf
));
1079 n
= sysfs_blkdev_count_partitions(pc
, name
);
1085 char *sysfs_chrdev_devno_to_devname(dev_t devno
, char *buf
, size_t bufsiz
)
1087 char link
[PATH_MAX
];
1088 struct path_cxt
*pc
;
1092 pc
= ul_new_path(_PATH_SYS_DEVCHAR
"/%u:%u", major(devno
), minor(devno
));
1096 /* read /sys/dev/char/<maj:min> link */
1097 sz
= ul_path_readlink(pc
, link
, sizeof(link
), NULL
);
1103 name
= strrchr(link
, '/');
1109 if ((size_t) sz
+ 1 > bufsiz
)
1112 memcpy(buf
, name
, sz
+ 1);
1113 sysfs_devname_sys_to_dev(buf
);
1118 enum sysfs_byteorder
sysfs_get_byteorder(struct path_cxt
*pc
)
1122 enum sysfs_byteorder ret
;
1124 rc
= ul_path_read_buffer(pc
, buf
, sizeof(buf
), _PATH_SYS_CPU_BYTEORDER
);
1128 if (strncmp(buf
, "little", sizeof(buf
)) == 0) {
1129 ret
= SYSFS_BYTEORDER_LITTLE
;
1131 } else if (strncmp(buf
, "big", sizeof(buf
)) == 0) {
1132 ret
= SYSFS_BYTEORDER_BIG
;
1137 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1138 ret
= SYSFS_BYTEORDER_LITTLE
;
1139 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1140 ret
= SYSFS_BYTEORDER_BIG
;
1142 #error Unknown byte order
1149 int sysfs_get_address_bits(struct path_cxt
*pc
)
1154 rc
= ul_path_scanf(pc
, _PATH_SYS_ADDRESS_BITS
, "%d", &address_bits
);
1157 if (address_bits
< 0)
1159 return address_bits
;
1163 #ifdef TEST_PROGRAM_SYSFS
1168 int main(int argc
, char *argv
[])
1170 struct path_cxt
*pc
;
1172 dev_t devno
, disk_devno
;
1173 char path
[PATH_MAX
], *sub
, *chain
;
1175 int i
, is_part
, rc
= EXIT_SUCCESS
;
1179 errx(EXIT_FAILURE
, "usage: %s <devname>", argv
[0]);
1181 ul_sysfs_init_debug();
1184 devno
= sysfs_devname_to_devno(devname
);
1187 err(EXIT_FAILURE
, "failed to read devno");
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
)));
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
);
1198 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
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
)));
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
);
1212 is_part
= ul_path_access(pc
, F_OK
, "partition") == 0;
1213 printf(" PARTITION: %s\n", is_part
? "YES" : "NOT");
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
);
1219 ul_unref_path(disk_pc
);
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"));
1227 printf("First 5 partitions:\n");
1228 for (i
= 1; i
<= 5; i
++) {
1229 dev_t dev
= sysfs_blkdev_partno_to_devno(pc
, i
);
1231 printf("\t#%d %d:%d\n", i
, major(dev
), minor(dev
));
1235 if (ul_path_read_u64(pc
, &u64
, "size") != 0)
1236 printf(" (!) read SIZE failed\n");
1238 printf(" SIZE: %jd\n", u64
);
1240 if (ul_path_read_s32(pc
, &i
, "queue/hw_sector_size"))
1241 printf(" (!) read SECTOR failed\n");
1243 printf(" SECTOR: %d\n", i
);
1246 chain
= sysfs_blkdev_get_devchain(pc
, path
, sizeof(path
));
1247 printf(" SUBSYSTEMS:\n");
1249 while (chain
&& sysfs_blkdev_next_subsystem(pc
, chain
, &sub
) == 0) {
1250 printf("\t%s\n", sub
);
1259 #endif /* TEST_PROGRAM_SYSFS */