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"
21 static void sysfs_blkdev_deinit_path(struct path_cxt
*pc
);
22 static int sysfs_blkdev_enoent_redirect(struct path_cxt
*pc
, const char *path
, int *dirfd
);
25 * Debug stuff (based on include/debug.h)
27 static UL_DEBUG_DEFINE_MASK(ulsysfs
);
28 UL_DEBUG_DEFINE_MASKNAMES(ulsysfs
) = UL_DEBUG_EMPTY_MASKNAMES
;
30 #define ULSYSFS_DEBUG_INIT (1 << 1)
31 #define ULSYSFS_DEBUG_CXT (1 << 2)
33 #define DBG(m, x) __UL_DBG(ulsysfs, ULSYSFS_DEBUG_, m, x)
34 #define ON_DBG(m, x) __UL_DBG_CALL(ulsysfs, ULSYSFS_DEBUG_, m, x)
36 #define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulsysfs)
39 void ul_sysfs_init_debug(void)
41 if (ulsysfs_debug_mask
)
43 __UL_INIT_DEBUG_FROM_ENV(ulsysfs
, ULSYSFS_DEBUG_
, 0, ULSYSFS_DEBUG
);
46 struct path_cxt
*ul_new_sysfs_path(dev_t devno
, struct path_cxt
*parent
, const char *prefix
)
48 struct path_cxt
*pc
= ul_new_path(NULL
);
53 ul_path_set_prefix(pc
, prefix
);
55 if (sysfs_blkdev_init_path(pc
, devno
, parent
) != 0) {
60 DBG(CXT
, ul_debugobj(pc
, "alloc"));
65 * sysfs_blkdev_* is sysfs extension to ul_path_* API for block devices.
67 * The function is possible to call in loop and without sysfs_blkdev_deinit_path().
68 * The sysfs_blkdev_deinit_path() is automatically called by ul_unref_path().
71 int sysfs_blkdev_init_path(struct path_cxt
*pc
, dev_t devno
, struct path_cxt
*parent
)
73 struct sysfs_blkdev
*blk
;
75 char buf
[sizeof(_PATH_SYS_DEVBLOCK
)
76 + sizeof(stringify_value(UINT32_MAX
)) * 2
79 /* define path to devno stuff */
80 snprintf(buf
, sizeof(buf
), _PATH_SYS_DEVBLOCK
"/%d:%d", major(devno
), minor(devno
));
81 rc
= ul_path_set_dir(pc
, buf
);
85 /* make sure path exists */
86 rc
= ul_path_get_dirfd(pc
);
90 /* initialize sysfs blkdev specific stuff */
91 blk
= ul_path_get_dialect(pc
);
93 DBG(CXT
, ul_debugobj(pc
, "alloc new sysfs handler"));
94 blk
= calloc(1, sizeof(struct sysfs_blkdev
));
98 ul_path_set_dialect(pc
, blk
, sysfs_blkdev_deinit_path
);
99 ul_path_set_enoent_redirect(pc
, sysfs_blkdev_enoent_redirect
);
102 DBG(CXT
, ul_debugobj(pc
, "init sysfs stuff"));
105 sysfs_blkdev_set_parent(pc
, parent
);
110 static void sysfs_blkdev_deinit_path(struct path_cxt
*pc
)
112 struct sysfs_blkdev
*blk
;
117 DBG(CXT
, ul_debugobj(pc
, "deinit"));
119 blk
= ul_path_get_dialect(pc
);
123 ul_ref_path(blk
->parent
);
126 ul_path_set_dialect(pc
, NULL
, NULL
);
129 int sysfs_blkdev_set_parent(struct path_cxt
*pc
, struct path_cxt
*parent
)
131 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
137 ul_unref_path(blk
->parent
);
143 blk
->parent
= parent
;
147 DBG(CXT
, ul_debugobj(pc
, "new parent"));
151 struct path_cxt
*sysfs_blkdev_get_parent(struct path_cxt
*pc
)
153 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
154 return blk
? blk
->parent
: NULL
;
158 * Redirects ENOENT errors to the parent, if the path is to the queue/
159 * sysfs directory. For example
161 * /sys/dev/block/8:1/queue/logical_block_size redirects to
162 * /sys/dev/block/8:0/queue/logical_block_size
164 static int sysfs_blkdev_enoent_redirect(struct path_cxt
*pc
, const char *path
, int *dirfd
)
166 struct sysfs_blkdev
*blk
= ul_path_get_dialect(pc
);
168 if (blk
&& blk
->parent
&& strncmp(path
, "queue/", 6) == 0) {
169 *dirfd
= ul_path_get_dirfd(blk
->parent
);
171 DBG(CXT
, ul_debugobj(pc
, "%s redirected to parent", path
));
175 return 1; /* no redirect */
178 char *sysfs_blkdev_get_name(struct path_cxt
*pc
, char *buf
, size_t bufsiz
)
184 /* read /sys/dev/block/<maj:min> link */
185 sz
= ul_path_readlink(pc
, link
, sizeof(link
) - 1, 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
)
215 const char *p
= parent_name
;
218 /* /dev/sda --> "sda" */
219 if (*parent_name
== '/') {
220 p
= strrchr(parent_name
, '/');
227 if (strlen(d
->d_name
) <= len
)
230 /* partitions subdir name is
231 * "<parent>[:digit:]" or "<parent>p[:digit:]"
233 return strncmp(p
, d
->d_name
, len
) == 0 &&
234 ((*(d
->d_name
+ len
) == 'p' && isdigit(*(d
->d_name
+ len
+ 1)))
235 || isdigit(*(d
->d_name
+ len
)));
238 /* Cannot use /partition file, not supported on old sysfs */
239 snprintf(path
, sizeof(path
), "%s/start", d
->d_name
);
241 return faccessat(dirfd(dir
), path
, R_OK
, 0) == 0;
244 int sysfs_blkdev_count_partitions(struct path_cxt
*pc
, const char *devname
)
250 dir
= ul_path_opendir(pc
, NULL
);
254 while ((d
= xreaddir(dir
))) {
255 if (sysfs_blkdev_is_partition_dirent(dir
, d
, devname
))
264 * Converts @partno (partition number) to devno of the partition.
265 * The @pc handles wholedisk device.
267 * Note that this code does not expect any special format of the
268 * partitions devnames.
270 dev_t
sysfs_blkdev_partno_to_devno(struct path_cxt
*pc
, int partno
)
276 dir
= ul_path_opendir(pc
, NULL
);
280 while ((d
= xreaddir(dir
))) {
283 if (!sysfs_blkdev_is_partition_dirent(dir
, d
, NULL
))
286 if (ul_path_readf_s32(pc
, &n
, "%s/partition", d
->d_name
))
290 if (ul_path_readf_majmin(pc
, &devno
, "%s/dev", d
->d_name
) == 0)
296 DBG(CXT
, ul_debugobj(pc
, "partno (%d) -> devno (%d)", (int) partno
, (int) devno
));
302 * Returns slave name if there is only one slave, otherwise returns NULL.
303 * The result should be deallocated by free().
305 char *sysfs_blkdev_get_slave(struct path_cxt
*pc
)
311 dir
= ul_path_opendir(pc
, "slaves");
315 while ((d
= xreaddir(dir
))) {
317 goto err
; /* more slaves */
318 name
= strdup(d
->d_name
);
330 #define SUBSYSTEM_LINKNAME "/subsystem"
335 * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
336 * 1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
338 * The function check if <chain>/subsystem symlink exists, if yes then returns
339 * basename of the readlink result, and remove the last subdirectory from the
342 static char *get_subsystem(char *chain
, char *buf
, size_t bufsz
)
347 if (!chain
|| !*chain
)
351 if (len
+ sizeof(SUBSYSTEM_LINKNAME
) > PATH_MAX
)
357 /* append "/subsystem" to the path */
358 memcpy(chain
+ len
, SUBSYSTEM_LINKNAME
, sizeof(SUBSYSTEM_LINKNAME
));
360 /* try if subsystem symlink exists */
361 sz
= readlink(chain
, buf
, bufsz
- 1);
363 /* remove last subsystem from chain */
365 p
= strrchr(chain
, '/');
372 /* we found symlink to subsystem, return basename */
374 return basename(buf
);
383 * Returns complete path to the device, the patch contains all subsystems
384 * used for the device.
386 char *sysfs_blkdev_get_devchain(struct path_cxt
*pc
, char *buf
, size_t bufsz
)
388 /* read /sys/dev/block/<maj>:<min> symlink */
389 ssize_t sz
= ul_path_readlink(pc
, buf
, bufsz
, NULL
);
393 if (sz
<= 0 || sz
+ sizeof(_PATH_SYS_DEVBLOCK
"/") > bufsz
)
397 prefix
= ul_path_get_prefix(pc
);
399 psz
= strlen(prefix
);
401 /* create absolute patch from the link */
402 memmove(buf
+ psz
+ sizeof(_PATH_SYS_DEVBLOCK
"/") - 1, buf
, sz
);
404 memcpy(buf
, prefix
, psz
);
406 memcpy(buf
+ psz
, _PATH_SYS_DEVBLOCK
"/", sizeof(_PATH_SYS_DEVBLOCK
"/") - 1);
411 * The @subsys returns the next subsystem in the chain. Function modifies
414 * Returns: 0 in success, <0 on error, 1 on end of chain
416 int sysfs_blkdev_next_subsystem(struct path_cxt
*pc
__attribute__((unused
)),
417 char *devchain
, char **subsys
)
419 char subbuf
[PATH_MAX
];
422 if (!subsys
|| !devchain
)
427 while ((sub
= get_subsystem(devchain
, subbuf
, sizeof(subbuf
)))) {
428 *subsys
= strdup(sub
);
438 static int is_hotpluggable_subsystem(const char *name
)
440 static const char * const hotplug_subsystems
[] = {
449 for (i
= 0; i
< ARRAY_SIZE(hotplug_subsystems
); i
++)
450 if (strcmp(name
, hotplug_subsystems
[i
]) == 0)
456 int sysfs_blkdev_is_hotpluggable(struct path_cxt
*pc
)
458 char buf
[PATH_MAX
], *chain
, *sub
;
462 /* check /sys/dev/block/<maj>:<min>/removable attribute */
463 if (ul_path_read_s32(pc
, &rc
, "removable") == 0 && rc
== 1)
466 chain
= sysfs_blkdev_get_devchain(pc
, buf
, sizeof(buf
));
468 while (chain
&& sysfs_blkdev_next_subsystem(pc
, chain
, &sub
) == 0) {
469 rc
= is_hotpluggable_subsystem(sub
);
480 static int get_dm_wholedisk(struct path_cxt
*pc
, char *diskname
,
481 size_t len
, dev_t
*diskdevno
)
486 /* Note, sysfs_blkdev_get_slave() returns the first slave only,
487 * if there is more slaves, then return NULL
489 name
= sysfs_blkdev_get_slave(pc
);
494 xstrncpy(diskname
, name
, len
);
497 *diskdevno
= __sysfs_devname_to_devno(ul_path_get_prefix(pc
), name
, NULL
);
507 * Returns by @diskdevno whole disk device devno and (optionally) by
508 * @diskname the whole disk device name.
510 int sysfs_blkdev_get_wholedisk( struct path_cxt
*pc
,
520 is_part
= ul_path_access(pc
, F_OK
, "partition") == 0;
523 * Extra case for partitions mapped by device-mapper.
525 * All regular partitions (added by BLKPG ioctl or kernel PT
526 * parser) have the /sys/.../partition file. The partitions
527 * mapped by DM don't have such file, but they have "part"
530 char *uuid
= NULL
, *tmp
, *prefix
;
532 ul_path_read_string(pc
, &uuid
, "dm/uuid");
534 prefix
= uuid
? strsep(&tmp
, "-") : NULL
;
536 if (prefix
&& strncasecmp(prefix
, "part", 4) == 0)
541 get_dm_wholedisk(pc
, diskname
, len
, diskdevno
) == 0)
543 * partitioned device, mapped by DM
552 * unpartitioned device
554 if (diskname
&& !sysfs_blkdev_get_name(pc
, diskname
, len
))
557 *diskdevno
= sysfs_blkdev_get_devno(pc
);
562 * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
563 * - dirname ../../block/sda/sda1 = ../../block/sda
564 * - basename ../../block/sda = sda
566 char linkpath
[PATH_MAX
];
570 linklen
= ul_path_readlink(pc
, linkpath
, sizeof(linkpath
) - 1, NULL
);
573 linkpath
[linklen
] = '\0';
575 stripoff_last_component(linkpath
); /* dirname */
576 name
= stripoff_last_component(linkpath
); /* basename */
580 sysfs_devname_sys_to_dev(name
);
582 xstrncpy(diskname
, name
, len
);
585 *diskdevno
= __sysfs_devname_to_devno(ul_path_get_prefix(pc
), name
, NULL
);
597 int sysfs_devno_to_wholedisk(dev_t devno
, char *diskname
,
598 size_t len
, dev_t
*diskdevno
)
605 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
609 rc
= sysfs_blkdev_get_wholedisk(pc
, diskname
, len
, diskdevno
);
615 * Returns 1 if the device is private device mapper device. The @uuid
616 * (if not NULL) returns DM device UUID, use free() to deallocate.
618 int sysfs_devno_is_dm_private(dev_t devno
, char **uuid
)
620 struct path_cxt
*pc
= NULL
;
624 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
627 if (ul_path_read_string(pc
, &id
, "dm/uuid") <= 0 || !id
)
630 /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
631 * is the "LVM" prefix and "-<name>" postfix).
633 if (strncmp(id
, "LVM-", 4) == 0) {
634 char *p
= strrchr(id
+ 4, '-');
639 /* Private Stratis devices prefix the UUID with "stratis-1-private"
641 } else if (strncmp(id
, "stratis-1-private", 17) == 0) {
654 * Return 0 or 1, or < 0 in case of error
656 int sysfs_devno_is_wholedisk(dev_t devno
)
660 if (sysfs_devno_to_wholedisk(devno
, NULL
, 0, &disk
) != 0)
663 return devno
== disk
;
667 int sysfs_blkdev_scsi_get_hctl(struct path_cxt
*pc
, int *h
, int *c
, int *t
, int *l
)
669 char buf
[PATH_MAX
], *hctl
;
670 struct sysfs_blkdev
*blk
;
673 blk
= ul_path_get_dialect(pc
);
675 if (!blk
|| blk
->hctl_error
)
681 len
= ul_path_readlink(pc
, buf
, sizeof(buf
) - 1, "device");
686 hctl
= strrchr(buf
, '/');
691 if (sscanf(hctl
, "%u:%u:%u:%u", &blk
->scsi_host
, &blk
->scsi_channel
,
692 &blk
->scsi_target
, &blk
->scsi_lun
) != 4)
700 *c
= blk
->scsi_channel
;
702 *t
= blk
->scsi_target
;
711 static char *scsi_host_attribute_path(
722 if (sysfs_blkdev_scsi_get_hctl(pc
, &host
, NULL
, NULL
, NULL
))
725 prefix
= ul_path_get_prefix(pc
);
730 len
= snprintf(buf
, bufsz
, "%s%s/%s_host/host%d/%s",
731 prefix
, _PATH_SYS_CLASS
, type
, host
, attr
);
733 len
= snprintf(buf
, bufsz
, "%s%s/%s_host/host%d",
734 prefix
, _PATH_SYS_CLASS
, type
, host
);
736 return (len
< 0 || (size_t) len
>= bufsz
) ? NULL
: buf
;
739 char *sysfs_blkdev_scsi_host_strdup_attribute(struct path_cxt
*pc
,
740 const char *type
, const char *attr
)
746 if (!attr
|| !type
||
747 !scsi_host_attribute_path(pc
, type
, buf
, sizeof(buf
), attr
))
750 if (!(f
= fopen(buf
, "r" UL_CLOEXECSTR
)))
753 rc
= fscanf(f
, "%1023[^\n]", buf
);
756 return rc
== 1 ? strdup(buf
) : NULL
;
759 int sysfs_blkdev_scsi_host_is(struct path_cxt
*pc
, const char *type
)
764 if (!type
|| !scsi_host_attribute_path(pc
, type
,
765 buf
, sizeof(buf
), NULL
))
768 return stat(buf
, &st
) == 0 && S_ISDIR(st
.st_mode
);
771 static char *scsi_attribute_path(struct path_cxt
*pc
,
772 char *buf
, size_t bufsz
, const char *attr
)
777 if (sysfs_blkdev_scsi_get_hctl(pc
, &h
, &c
, &t
, &l
) != 0)
780 prefix
= ul_path_get_prefix(pc
);
785 len
= snprintf(buf
, bufsz
, "%s%s/devices/%d:%d:%d:%d/%s",
786 prefix
, _PATH_SYS_SCSI
,
789 len
= snprintf(buf
, bufsz
, "%s%s/devices/%d:%d:%d:%d",
790 prefix
, _PATH_SYS_SCSI
,
792 return (len
< 0 || (size_t) len
>= bufsz
) ? NULL
: buf
;
795 int sysfs_blkdev_scsi_has_attribute(struct path_cxt
*pc
, const char *attr
)
800 if (!scsi_attribute_path(pc
, path
, sizeof(path
), attr
))
803 return stat(path
, &st
) == 0;
806 int sysfs_blkdev_scsi_path_contains(struct path_cxt
*pc
, const char *pattern
)
808 char path
[PATH_MAX
], linkc
[PATH_MAX
];
812 if (!scsi_attribute_path(pc
, path
, sizeof(path
), NULL
))
815 if (stat(path
, &st
) != 0)
818 len
= readlink(path
, linkc
, sizeof(linkc
) - 1);
823 return strstr(linkc
, pattern
) != NULL
;
826 static dev_t
read_devno(const char *path
)
829 int maj
= 0, min
= 0;
832 f
= fopen(path
, "r" UL_CLOEXECSTR
);
836 if (fscanf(f
, "%d:%d", &maj
, &min
) == 2)
837 dev
= makedev(maj
, min
);
842 dev_t
__sysfs_devname_to_devno(const char *prefix
, const char *name
, const char *parent
)
845 char *_name
= NULL
; /* name as encoded in sysfs */
854 if (strncmp("/dev/", name
, 5) == 0) {
860 if (stat(name
, &st
) == 0) {
864 name
+= 5; /* unaccesible, or not node in /dev */
867 _name
= strdup(name
);
870 sysfs_devname_dev_to_sys(_name
);
872 if (parent
&& strncmp("dm-", name
, 3)) {
874 * Create path to /sys/block/<parent>/<name>/dev
876 char *_parent
= strdup(parent
);
882 sysfs_devname_dev_to_sys(_parent
);
883 len
= snprintf(buf
, sizeof(buf
),
884 "%s" _PATH_SYS_BLOCK
"/%s/%s/dev",
885 prefix
, _parent
, _name
);
887 if (len
< 0 || (size_t) len
>= sizeof(buf
))
890 /* don't try anything else for dm-* */
891 dev
= read_devno(buf
);
896 * Read from /sys/block/<sysname>/dev
898 len
= snprintf(buf
, sizeof(buf
),
899 "%s" _PATH_SYS_BLOCK
"/%s/dev",
901 if (len
< 0 || (size_t) len
>= sizeof(buf
))
903 dev
= read_devno(buf
);
907 * Read from /sys/block/<sysname>/device/dev
909 len
= snprintf(buf
, sizeof(buf
),
910 "%s" _PATH_SYS_BLOCK
"/%s/device/dev",
912 if (len
< 0 || (size_t) len
>= sizeof(buf
))
914 dev
= read_devno(buf
);
921 dev_t
sysfs_devname_to_devno(const char *name
)
923 return __sysfs_devname_to_devno(NULL
, name
, NULL
);
926 char *sysfs_blkdev_get_path(struct path_cxt
*pc
, char *buf
, size_t bufsiz
)
928 const char *name
= sysfs_blkdev_get_name(pc
, buf
, bufsiz
);
937 if (sz
+ sizeof("/dev/") > bufsiz
)
940 /* create the final "/dev/<name>" string */
941 memmove(buf
+ 5, name
, sz
+ 1);
942 memcpy(buf
, "/dev/", 5);
944 if (!stat(buf
, &st
) && S_ISBLK(st
.st_mode
) && st
.st_rdev
== sysfs_blkdev_get_devno(pc
))
950 dev_t
sysfs_blkdev_get_devno(struct path_cxt
*pc
)
952 return ((struct sysfs_blkdev
*) ul_path_get_dialect(pc
))->devno
;
956 * Returns devname (e.g. "/dev/sda1") for the given devno.
958 * Please, use more robust blkid_devno_to_devname() in your applications.
960 char *sysfs_devno_to_devpath(dev_t devno
, char *buf
, size_t bufsiz
)
962 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
966 res
= sysfs_blkdev_get_path(pc
, buf
, bufsiz
);
972 char *sysfs_devno_to_devname(dev_t devno
, char *buf
, size_t bufsiz
)
974 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
978 res
= sysfs_blkdev_get_name(pc
, buf
, bufsiz
);
984 int sysfs_devno_count_partitions(dev_t devno
)
986 struct path_cxt
*pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
990 char buf
[PATH_MAX
+ 1];
991 char *name
= sysfs_blkdev_get_name(pc
, buf
, sizeof(buf
));
993 n
= sysfs_blkdev_count_partitions(pc
, name
);
1000 #ifdef TEST_PROGRAM_SYSFS
1005 int main(int argc
, char *argv
[])
1007 struct path_cxt
*pc
;
1009 dev_t devno
, disk_devno
;
1010 char path
[PATH_MAX
], *sub
, *chain
;
1012 int i
, is_part
, rc
= EXIT_SUCCESS
;
1016 errx(EXIT_FAILURE
, "usage: %s <devname>", argv
[0]);
1018 ul_sysfs_init_debug();
1021 devno
= sysfs_devname_to_devno(devname
);
1024 err(EXIT_FAILURE
, "failed to read devno");
1026 printf("non-context:\n");
1027 printf(" DEVNO: %u (%d:%d)\n", (unsigned int) devno
, major(devno
), minor(devno
));
1028 printf(" DEVNAME: %s\n", sysfs_devno_to_devname(devno
, path
, sizeof(path
)));
1029 printf(" DEVPATH: %s\n", sysfs_devno_to_devpath(devno
, path
, sizeof(path
)));
1031 sysfs_devno_to_wholedisk(devno
, diskname
, sizeof(diskname
), &disk_devno
);
1032 printf(" WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno
, major(disk_devno
), minor(disk_devno
));
1033 printf(" WHOLEDISK-DEVNAME: %s\n", diskname
);
1035 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
1039 printf("context based:\n");
1040 devno
= sysfs_blkdev_get_devno(pc
);
1041 printf(" DEVNO: %u (%d:%d)\n", (unsigned int) devno
, major(devno
), minor(devno
));
1042 printf(" DEVNAME: %s\n", sysfs_blkdev_get_name(pc
, path
, sizeof(path
)));
1043 printf(" DEVPATH: %s\n", sysfs_blkdev_get_path(pc
, path
, sizeof(path
)));
1045 sysfs_devno_to_wholedisk(devno
, diskname
, sizeof(diskname
), &disk_devno
);
1046 printf(" WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno
, major(disk_devno
), minor(disk_devno
));
1047 printf(" WHOLEDISK-DEVNAME: %s\n", diskname
);
1049 is_part
= ul_path_access(pc
, F_OK
, "partition") == 0;
1050 printf(" PARTITION: %s\n", is_part
? "YES" : "NOT");
1052 if (is_part
&& disk_devno
) {
1053 struct path_cxt
*disk_pc
= ul_new_sysfs_path(disk_devno
, NULL
, NULL
);
1054 sysfs_blkdev_set_parent(pc
, disk_pc
);
1056 ul_unref_path(disk_pc
);
1059 printf(" HOTPLUG: %s\n", sysfs_blkdev_is_hotpluggable(pc
) ? "yes" : "no");
1060 printf(" SLAVES: %d\n", ul_path_count_dirents(pc
, "slaves"));
1063 printf("First 5 partitions:\n");
1064 for (i
= 1; i
<= 5; i
++) {
1065 dev_t dev
= sysfs_blkdev_partno_to_devno(pc
, i
);
1067 printf("\t#%d %d:%d\n", i
, major(dev
), minor(dev
));
1071 if (ul_path_read_u64(pc
, &u64
, "size") != 0)
1072 printf(" (!) read SIZE failed\n");
1074 printf(" SIZE: %jd\n", u64
);
1076 if (ul_path_read_s32(pc
, &i
, "queue/hw_sector_size"))
1077 printf(" (!) read SECTOR failed\n");
1079 printf(" SECTOR: %d\n", i
);
1082 chain
= sysfs_blkdev_get_devchain(pc
, path
, sizeof(path
));
1083 printf(" SUBSUSTEMS:\n");
1085 while (chain
&& sysfs_blkdev_next_subsystem(pc
, chain
, &sub
) == 0) {
1086 printf("\t%s\n", sub
);
1095 #endif /* TEST_PROGRAM_SYSFS */