2 * No copyright is claimed. This code is in the public domain; do with
5 * Written by Karel Zak <kzak@redhat.com>
14 #include "pathnames.h"
16 #include "fileutils.h"
20 static void sysfs_blkdev_deinit_path(struct path_cxt
*pc
);
21 static int sysfs_blkdev_enoent_redirect(struct path_cxt
*pc
, const char *path
, int *dirfd
);
24 * Debug stuff (based on include/debug.h)
26 static UL_DEBUG_DEFINE_MASK(ulsysfs
);
27 UL_DEBUG_DEFINE_MASKNAMES(ulsysfs
) = UL_DEBUG_EMPTY_MASKNAMES
;
29 #define ULSYSFS_DEBUG_INIT (1 << 1)
30 #define ULSYSFS_DEBUG_CXT (1 << 2)
32 #define DBG(m, x) __UL_DBG(ulsysfs, ULSYSFS_DEBUG_, m, x)
33 #define ON_DBG(m, x) __UL_DBG_CALL(ulsysfs, ULSYSFS_DEBUG_, m, x)
35 #define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulsysfs)
38 void ul_sysfs_init_debug(void)
40 if (ulsysfs_debug_mask
)
42 __UL_INIT_DEBUG_FROM_ENV(ulsysfs
, ULSYSFS_DEBUG_
, 0, ULSYSFS_DEBUG
);
45 struct path_cxt
*ul_new_sysfs_path(dev_t devno
, struct path_cxt
*parent
, const char *prefix
)
47 struct path_cxt
*pc
= ul_new_path(NULL
);
52 ul_path_set_prefix(pc
, prefix
);
54 if (sysfs_blkdev_init_path(pc
, devno
, parent
) != 0) {
59 DBG(CXT
, ul_debugobj(pc
, "alloc"));
64 * sysfs_blkdev_* is sysfs extension to ul_path_* API for block devices.
66 * The function is possible to call in loop and without sysfs_blkdev_deinit_path().
67 * The sysfs_blkdev_deinit_path() is automatically called by ul_unref_path().
70 int sysfs_blkdev_init_path(struct path_cxt
*pc
, dev_t devno
, struct path_cxt
*parent
)
72 struct sysfs_blkdev
*blk
;
74 char buf
[sizeof(_PATH_SYS_DEVBLOCK
)
75 + sizeof(stringify_value(UINT32_MAX
)) * 2
78 /* define path to devno stuff */
79 snprintf(buf
, sizeof(buf
), _PATH_SYS_DEVBLOCK
"/%d:%d", major(devno
), minor(devno
));
80 rc
= ul_path_set_dir(pc
, buf
);
84 /* make sure path exists */
85 rc
= ul_path_get_dirfd(pc
);
89 /* initialize sysfs blkdev specific stuff */
90 blk
= ul_path_get_dialect(pc
);
92 DBG(CXT
, ul_debugobj(pc
, "alloc new sysfs handler"));
93 blk
= calloc(1, sizeof(struct sysfs_blkdev
));
97 ul_path_set_dialect(pc
, blk
, sysfs_blkdev_deinit_path
);
98 ul_path_set_enoent_redirect(pc
, sysfs_blkdev_enoent_redirect
);
101 DBG(CXT
, ul_debugobj(pc
, "init sysfs stuff"));
104 sysfs_blkdev_set_parent(pc
, parent
);
109 static void sysfs_blkdev_deinit_path(struct path_cxt
*pc
)
111 struct sysfs_blkdev
*blk
;
116 DBG(CXT
, ul_debugobj(pc
, "deinit"));
118 blk
= ul_path_get_dialect(pc
);
122 ul_ref_path(blk
->parent
);
125 ul_path_set_dialect(pc
, NULL
, NULL
);
128 int sysfs_blkdev_set_parent(struct path_cxt
*pc
, struct path_cxt
*parent
)
130 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
136 ul_unref_path(blk
->parent
);
142 blk
->parent
= parent
;
146 DBG(CXT
, ul_debugobj(pc
, "new parent"));
150 struct path_cxt
*sysfs_blkdev_get_parent(struct path_cxt
*pc
)
152 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
153 return blk
? blk
->parent
: NULL
;
157 * Redirects ENOENT errors to the parent, if the path is to the queue/
158 * sysfs directory. For example
160 * /sys/dev/block/8:1/queue/logical_block_size redirects to
161 * /sys/dev/block/8:0/queue/logical_block_size
163 static int sysfs_blkdev_enoent_redirect(struct path_cxt
*pc
, const char *path
, int *dirfd
)
165 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
167 if (blk
&& blk
->parent
&& strncmp(path
, "queue/", 6) == 0) {
168 *dirfd
= ul_path_get_dirfd(blk
->parent
);
170 DBG(CXT
, ul_debugobj(pc
, "%s redirected to parent", path
));
174 return 1; /* no redirect */
177 char *sysfs_blkdev_get_name(struct path_cxt
*pc
, char *buf
, size_t bufsiz
)
183 /* read /sys/dev/block/<maj:min> link */
184 sz
= ul_path_readlink(pc
, link
, sizeof(link
) - 1, NULL
);
189 name
= strrchr(link
, '/');
195 if ((size_t) sz
+ 1 > bufsiz
)
198 memcpy(buf
, name
, sz
+ 1);
199 sysfs_devname_sys_to_dev(buf
);
203 static struct dirent
*xreaddir(DIR *dp
)
207 while ((d
= readdir(dp
))) {
208 if (!strcmp(d
->d_name
, ".") ||
209 !strcmp(d
->d_name
, ".."))
212 /* blacklist here? */
218 int sysfs_blkdev_is_partition_dirent(DIR *dir
, struct dirent
*d
, const char *parent_name
)
220 char path
[NAME_MAX
+ 6 + 1];
222 #ifdef _DIRENT_HAVE_D_TYPE
223 if (d
->d_type
!= DT_DIR
&&
224 d
->d_type
!= DT_LNK
&&
225 d
->d_type
!= DT_UNKNOWN
)
229 const char *p
= parent_name
;
232 /* /dev/sda --> "sda" */
233 if (*parent_name
== '/') {
234 p
= strrchr(parent_name
, '/');
241 if (strlen(d
->d_name
) <= len
)
244 /* partitions subdir name is
245 * "<parent>[:digit:]" or "<parent>p[:digit:]"
247 return strncmp(p
, d
->d_name
, len
) == 0 &&
248 ((*(d
->d_name
+ len
) == 'p' && isdigit(*(d
->d_name
+ len
+ 1)))
249 || isdigit(*(d
->d_name
+ len
)));
252 /* Cannot use /partition file, not supported on old sysfs */
253 snprintf(path
, sizeof(path
), "%s/start", d
->d_name
);
255 return faccessat(dirfd(dir
), path
, R_OK
, 0) == 0;
258 int sysfs_blkdev_count_partitions(struct path_cxt
*pc
, const char *devname
)
264 dir
= ul_path_opendir(pc
, NULL
);
268 while ((d
= xreaddir(dir
))) {
269 if (sysfs_blkdev_is_partition_dirent(dir
, d
, devname
))
278 * Converts @partno (partition number) to devno of the partition.
279 * The @pc handles wholedisk device.
281 * Note that this code does not expect any special format of the
282 * partitions devnames.
284 dev_t
sysfs_blkdev_partno_to_devno(struct path_cxt
*pc
, int partno
)
290 dir
= ul_path_opendir(pc
, NULL
);
294 while ((d
= xreaddir(dir
))) {
297 if (!sysfs_blkdev_is_partition_dirent(dir
, d
, NULL
))
300 if (ul_path_readf_s32(pc
, &n
, "%s/partition", d
->d_name
))
304 if (ul_path_readf_majmin(pc
, &devno
, "%s/dev", d
->d_name
) == 0)
310 DBG(CXT
, ul_debugobj(pc
, "partno (%d) -> devno (%d)", (int) partno
, (int) devno
));
316 * Returns slave name if there is only one slave, otherwise returns NULL.
317 * The result should be deallocated by free().
319 char *sysfs_blkdev_get_slave(struct path_cxt
*pc
)
325 dir
= ul_path_opendir(pc
, "slaves");
329 while ((d
= xreaddir(dir
))) {
331 goto err
; /* more slaves */
332 name
= strdup(d
->d_name
);
344 #define SUBSYSTEM_LINKNAME "/subsystem"
349 * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
350 * 1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
352 * The function check if <chain>/subsystem symlink exists, if yes then returns
353 * basename of the readlink result, and remove the last subdirectory from the
356 static char *get_subsystem(char *chain
, char *buf
, size_t bufsz
)
361 if (!chain
|| !*chain
)
365 if (len
+ sizeof(SUBSYSTEM_LINKNAME
) > PATH_MAX
)
371 /* append "/subsystem" to the path */
372 memcpy(chain
+ len
, SUBSYSTEM_LINKNAME
, sizeof(SUBSYSTEM_LINKNAME
));
374 /* try if subsystem symlink exists */
375 sz
= readlink(chain
, buf
, bufsz
- 1);
377 /* remove last subsystem from chain */
379 p
= strrchr(chain
, '/');
386 /* we found symlink to subsystem, return basename */
388 return basename(buf
);
397 * Returns complete path to the device, the patch contains all subsystems
398 * used for the device.
400 char *sysfs_blkdev_get_devchain(struct path_cxt
*pc
, char *buf
, size_t bufsz
)
402 /* read /sys/dev/block/<maj>:<min> symlink */
403 ssize_t sz
= ul_path_readlink(pc
, buf
, bufsz
, NULL
);
407 if (sz
<= 0 || sz
+ sizeof(_PATH_SYS_DEVBLOCK
"/") > bufsz
)
411 prefix
= ul_path_get_prefix(pc
);
413 psz
= strlen(prefix
);
415 /* create absolute patch from the link */
416 memmove(buf
+ psz
+ sizeof(_PATH_SYS_DEVBLOCK
"/") - 1, buf
, sz
);
418 memcpy(buf
, prefix
, psz
);
420 memcpy(buf
+ psz
, _PATH_SYS_DEVBLOCK
"/", sizeof(_PATH_SYS_DEVBLOCK
"/") - 1);
425 * The @subsys returns the next subsystem in the chain. Function modifies
428 * Returns: 0 in success, <0 on error, 1 on end of chain
430 int sysfs_blkdev_next_subsystem(struct path_cxt
*pc
__attribute__((unused
)),
431 char *devchain
, char **subsys
)
433 char subbuf
[PATH_MAX
];
436 if (!subsys
|| !devchain
)
441 while ((sub
= get_subsystem(devchain
, subbuf
, sizeof(subbuf
)))) {
442 *subsys
= strdup(sub
);
452 static int is_hotpluggable_subsystem(const char *name
)
454 static const char * const hotplug_subsystems
[] = {
463 for (i
= 0; i
< ARRAY_SIZE(hotplug_subsystems
); i
++)
464 if (strcmp(name
, hotplug_subsystems
[i
]) == 0)
470 int sysfs_blkdev_is_hotpluggable(struct path_cxt
*pc
)
472 char buf
[PATH_MAX
], *chain
, *sub
;
476 /* check /sys/dev/block/<maj>:<min>/removable attribute */
477 if (ul_path_read_s32(pc
, &rc
, "removable") == 0 && rc
== 1)
480 chain
= sysfs_blkdev_get_devchain(pc
, buf
, sizeof(buf
));
482 while (chain
&& sysfs_blkdev_next_subsystem(pc
, chain
, &sub
) == 0) {
483 rc
= is_hotpluggable_subsystem(sub
);
494 static int get_dm_wholedisk(struct path_cxt
*pc
, char *diskname
,
495 size_t len
, dev_t
*diskdevno
)
500 /* Note, sysfs_blkdev_get_slave() returns the first slave only,
501 * if there is more slaves, then return NULL
503 name
= sysfs_blkdev_get_slave(pc
);
507 if (diskname
&& len
) {
508 strncpy(diskname
, name
, len
);
509 diskname
[len
- 1] = '\0';
513 *diskdevno
= __sysfs_devname_to_devno(ul_path_get_prefix(pc
), name
, NULL
);
523 * Returns by @diskdevno whole disk device devno and (optionally) by
524 * @diskname the whole disk device name.
526 int sysfs_blkdev_get_wholedisk( struct path_cxt
*pc
,
536 is_part
= ul_path_access(pc
, F_OK
, "partition") == 0;
539 * Extra case for partitions mapped by device-mapper.
541 * All regular partitions (added by BLKPG ioctl or kernel PT
542 * parser) have the /sys/.../partition file. The partitions
543 * mapped by DM don't have such file, but they have "part"
546 char *uuid
= NULL
, *tmp
, *prefix
;
548 ul_path_read_string(pc
, &uuid
, "dm/uuid");
550 prefix
= uuid
? strsep(&tmp
, "-") : NULL
;
552 if (prefix
&& strncasecmp(prefix
, "part", 4) == 0)
557 get_dm_wholedisk(pc
, diskname
, len
, diskdevno
) == 0)
559 * partitioned device, mapped by DM
568 * unpartitioned device
570 if (diskname
&& !sysfs_blkdev_get_name(pc
, diskname
, len
))
573 *diskdevno
= sysfs_blkdev_get_devno(pc
);
578 * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
579 * - dirname ../../block/sda/sda1 = ../../block/sda
580 * - basename ../../block/sda = sda
582 char linkpath
[PATH_MAX
];
586 linklen
= ul_path_readlink(pc
, linkpath
, sizeof(linkpath
) - 1, NULL
);
589 linkpath
[linklen
] = '\0';
591 stripoff_last_component(linkpath
); /* dirname */
592 name
= stripoff_last_component(linkpath
); /* basename */
596 sysfs_devname_sys_to_dev(name
);
597 if (diskname
&& len
) {
598 strncpy(diskname
, name
, len
);
599 diskname
[len
- 1] = '\0';
603 *diskdevno
= __sysfs_devname_to_devno(ul_path_get_prefix(pc
), name
, NULL
);
615 int sysfs_devno_to_wholedisk(dev_t devno
, char *diskname
,
616 size_t len
, dev_t
*diskdevno
)
623 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
627 rc
= sysfs_blkdev_get_wholedisk(pc
, diskname
, len
, diskdevno
);
633 * Returns 1 if the device is private device mapper device. The @uuid
634 * (if not NULL) returns DM device UUID, use free() to deallocate.
636 int sysfs_devno_is_dm_private(dev_t devno
, char **uuid
)
638 struct path_cxt
*pc
= NULL
;
642 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
645 if (ul_path_read_string(pc
, &id
, "dm/uuid") <= 0 || !id
)
648 /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
649 * is the "LVM" prefix and "-<name>" postfix).
651 if (strncmp(id
, "LVM-", 4) == 0) {
652 char *p
= strrchr(id
+ 4, '-');
657 /* Private Stratis devices prefix the UUID with "stratis-1-private"
659 } else if (strncmp(id
, "stratis-1-private", 17) == 0) {
672 * Return 0 or 1, or < 0 in case of error
674 int sysfs_devno_is_wholedisk(dev_t devno
)
678 if (sysfs_devno_to_wholedisk(devno
, NULL
, 0, &disk
) != 0)
681 return devno
== disk
;
685 int sysfs_blkdev_scsi_get_hctl(struct path_cxt
*pc
, int *h
, int *c
, int *t
, int *l
)
687 char buf
[PATH_MAX
], *hctl
;
688 struct sysfs_blkdev
*blk
;
691 blk
= ul_path_get_dialect(pc
);
693 if (!blk
|| blk
->hctl_error
)
699 len
= ul_path_readlink(pc
, buf
, sizeof(buf
) - 1, "device");
704 hctl
= strrchr(buf
, '/');
709 if (sscanf(hctl
, "%u:%u:%u:%u", &blk
->scsi_host
, &blk
->scsi_channel
,
710 &blk
->scsi_target
, &blk
->scsi_lun
) != 4)
718 *c
= blk
->scsi_channel
;
720 *t
= blk
->scsi_target
;
729 static char *scsi_host_attribute_path(
740 if (sysfs_blkdev_scsi_get_hctl(pc
, &host
, NULL
, NULL
, NULL
))
743 prefix
= ul_path_get_prefix(pc
);
748 len
= snprintf(buf
, bufsz
, "%s%s/%s_host/host%d/%s",
749 prefix
, _PATH_SYS_CLASS
, type
, host
, attr
);
751 len
= snprintf(buf
, bufsz
, "%s%s/%s_host/host%d",
752 prefix
, _PATH_SYS_CLASS
, type
, host
);
754 return (len
< 0 || (size_t) len
>= bufsz
) ? NULL
: buf
;
757 char *sysfs_blkdev_scsi_host_strdup_attribute(struct path_cxt
*pc
,
758 const char *type
, const char *attr
)
764 if (!attr
|| !type
||
765 !scsi_host_attribute_path(pc
, type
, buf
, sizeof(buf
), attr
))
768 if (!(f
= fopen(buf
, "r" UL_CLOEXECSTR
)))
771 rc
= fscanf(f
, "%1023[^\n]", buf
);
774 return rc
== 1 ? strdup(buf
) : NULL
;
777 int sysfs_blkdev_scsi_host_is(struct path_cxt
*pc
, const char *type
)
782 if (!type
|| !scsi_host_attribute_path(pc
, type
,
783 buf
, sizeof(buf
), NULL
))
786 return stat(buf
, &st
) == 0 && S_ISDIR(st
.st_mode
);
789 static char *scsi_attribute_path(struct path_cxt
*pc
,
790 char *buf
, size_t bufsz
, const char *attr
)
795 if (sysfs_blkdev_scsi_get_hctl(pc
, &h
, &c
, &t
, &l
) != 0)
798 prefix
= ul_path_get_prefix(pc
);
803 len
= snprintf(buf
, bufsz
, "%s%s/devices/%d:%d:%d:%d/%s",
804 prefix
, _PATH_SYS_SCSI
,
807 len
= snprintf(buf
, bufsz
, "%s%s/devices/%d:%d:%d:%d",
808 prefix
, _PATH_SYS_SCSI
,
810 return (len
< 0 || (size_t) len
>= bufsz
) ? NULL
: buf
;
813 int sysfs_blkdev_scsi_has_attribute(struct path_cxt
*pc
, const char *attr
)
818 if (!scsi_attribute_path(pc
, path
, sizeof(path
), attr
))
821 return stat(path
, &st
) == 0;
824 int sysfs_blkdev_scsi_path_contains(struct path_cxt
*pc
, const char *pattern
)
826 char path
[PATH_MAX
], linkc
[PATH_MAX
];
830 if (!scsi_attribute_path(pc
, path
, sizeof(path
), NULL
))
833 if (stat(path
, &st
) != 0)
836 len
= readlink(path
, linkc
, sizeof(linkc
) - 1);
841 return strstr(linkc
, pattern
) != NULL
;
844 static dev_t
read_devno(const char *path
)
847 int maj
= 0, min
= 0;
850 f
= fopen(path
, "r" UL_CLOEXECSTR
);
854 if (fscanf(f
, "%d:%d", &maj
, &min
) == 2)
855 dev
= makedev(maj
, min
);
860 dev_t
__sysfs_devname_to_devno(const char *prefix
, const char *name
, const char *parent
)
863 char *_name
= NULL
; /* name as encoded in sysfs */
872 if (strncmp("/dev/", name
, 5) == 0) {
878 if (stat(name
, &st
) == 0) {
882 name
+= 5; /* unaccesible, or not node in /dev */
885 _name
= strdup(name
);
888 sysfs_devname_dev_to_sys(_name
);
890 if (parent
&& strncmp("dm-", name
, 3)) {
892 * Create path to /sys/block/<parent>/<name>/dev
894 char *_parent
= strdup(parent
);
900 sysfs_devname_dev_to_sys(_parent
);
901 len
= snprintf(buf
, sizeof(buf
),
902 "%s" _PATH_SYS_BLOCK
"/%s/%s/dev",
903 prefix
, _parent
, _name
);
905 if (len
< 0 || (size_t) len
>= sizeof(buf
))
908 /* don't try anything else for dm-* */
909 dev
= read_devno(buf
);
914 * Read from /sys/block/<sysname>/dev
916 len
= snprintf(buf
, sizeof(buf
),
917 "%s" _PATH_SYS_BLOCK
"/%s/dev",
919 if (len
< 0 || (size_t) len
>= sizeof(buf
))
921 dev
= read_devno(buf
);
925 * Read from /sys/block/<sysname>/device/dev
927 len
= snprintf(buf
, sizeof(buf
),
928 "%s" _PATH_SYS_BLOCK
"/%s/device/dev",
930 if (len
< 0 || (size_t) len
>= sizeof(buf
))
932 dev
= read_devno(buf
);
939 dev_t
sysfs_devname_to_devno(const char *name
)
941 return __sysfs_devname_to_devno(NULL
, name
, NULL
);
944 char *sysfs_blkdev_get_path(struct path_cxt
*pc
, char *buf
, size_t bufsiz
)
946 const char *name
= sysfs_blkdev_get_name(pc
, buf
, bufsiz
);
955 if (sz
+ sizeof("/dev/") > bufsiz
)
958 /* create the final "/dev/<name>" string */
959 memmove(buf
+ 5, name
, sz
+ 1);
960 memcpy(buf
, "/dev/", 5);
962 if (!stat(buf
, &st
) && S_ISBLK(st
.st_mode
) && st
.st_rdev
== sysfs_blkdev_get_devno(pc
))
968 dev_t
sysfs_blkdev_get_devno(struct path_cxt
*pc
)
970 return ((struct sysfs_blkdev
*) ul_path_get_dialect(pc
))->devno
;
974 * Returns devname (e.g. "/dev/sda1") for the given devno.
976 * Please, use more robust blkid_devno_to_devname() in your applications.
978 char *sysfs_devno_to_devpath(dev_t devno
, char *buf
, size_t bufsiz
)
980 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
984 res
= sysfs_blkdev_get_path(pc
, buf
, bufsiz
);
990 char *sysfs_devno_to_devname(dev_t devno
, char *buf
, size_t bufsiz
)
992 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
996 res
= sysfs_blkdev_get_name(pc
, buf
, bufsiz
);
1002 int sysfs_devno_count_partitions(dev_t devno
)
1004 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
1008 char buf
[PATH_MAX
+ 1];
1009 char *name
= sysfs_blkdev_get_name(pc
, buf
, sizeof(buf
));
1011 n
= sysfs_blkdev_count_partitions(pc
, name
);
1018 #ifdef TEST_PROGRAM_SYSFS
1023 int main(int argc
, char *argv
[])
1025 struct path_cxt
*pc
;
1027 dev_t devno
, disk_devno
;
1028 char path
[PATH_MAX
], *sub
, *chain
;
1030 int i
, is_part
, rc
= EXIT_SUCCESS
;
1034 errx(EXIT_FAILURE
, "usage: %s <devname>", argv
[0]);
1036 ul_sysfs_init_debug();
1039 devno
= sysfs_devname_to_devno(devname
);
1042 err(EXIT_FAILURE
, "failed to read devno");
1044 printf("non-context:\n");
1045 printf(" DEVNO: %u (%d:%d)\n", (unsigned int) devno
, major(devno
), minor(devno
));
1046 printf(" DEVNAME: %s\n", sysfs_devno_to_devname(devno
, path
, sizeof(path
)));
1047 printf(" DEVPATH: %s\n", sysfs_devno_to_devpath(devno
, path
, sizeof(path
)));
1049 sysfs_devno_to_wholedisk(devno
, diskname
, sizeof(diskname
), &disk_devno
);
1050 printf(" WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno
, major(disk_devno
), minor(disk_devno
));
1051 printf(" WHOLEDISK-DEVNAME: %s\n", diskname
);
1053 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
1057 printf("context based:\n");
1058 devno
= sysfs_blkdev_get_devno(pc
);
1059 printf(" DEVNO: %u (%d:%d)\n", (unsigned int) devno
, major(devno
), minor(devno
));
1060 printf(" DEVNAME: %s\n", sysfs_blkdev_get_name(pc
, path
, sizeof(path
)));
1061 printf(" DEVPATH: %s\n", sysfs_blkdev_get_path(pc
, path
, sizeof(path
)));
1063 sysfs_devno_to_wholedisk(devno
, diskname
, sizeof(diskname
), &disk_devno
);
1064 printf(" WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno
, major(disk_devno
), minor(disk_devno
));
1065 printf(" WHOLEDISK-DEVNAME: %s\n", diskname
);
1067 is_part
= ul_path_access(pc
, F_OK
, "partition") == 0;
1068 printf(" PARTITION: %s\n", is_part
? "YES" : "NOT");
1070 if (is_part
&& disk_devno
) {
1071 struct path_cxt
*disk_pc
= ul_new_sysfs_path(disk_devno
, NULL
, NULL
);
1072 sysfs_blkdev_set_parent(pc
, disk_pc
);
1074 ul_unref_path(disk_pc
);
1077 printf(" HOTPLUG: %s\n", sysfs_blkdev_is_hotpluggable(pc
) ? "yes" : "no");
1078 printf(" SLAVES: %d\n", ul_path_count_dirents(pc
, "slaves"));
1081 printf("First 5 partitions:\n");
1082 for (i
= 1; i
<= 5; i
++) {
1083 dev_t dev
= sysfs_blkdev_partno_to_devno(pc
, i
);
1085 printf("\t#%d %d:%d\n", i
, major(dev
), minor(dev
));
1089 if (ul_path_read_u64(pc
, &u64
, "size") != 0)
1090 printf(" (!) read SIZE failed\n");
1092 printf(" SIZE: %jd\n", u64
);
1094 if (ul_path_read_s32(pc
, &i
, "queue/hw_sector_size"))
1095 printf(" (!) read SECTOR failed\n");
1097 printf(" SECTOR: %d\n", i
);
1100 chain
= sysfs_blkdev_get_devchain(pc
, path
, sizeof(path
));
1101 printf(" SUBSUSTEMS:\n");
1103 while (chain
&& sysfs_blkdev_next_subsystem(pc
, chain
, &sub
) == 0) {
1104 printf("\t%s\n", sub
);
1113 #endif /* TEST_PROGRAM_SYSFS */