[PATCH] util-linux: add minimal lsblk implementation

Osama Abdelkader osama.abdelkader at gmail.com
Thu Jan 29 21:12:02 UTC 2026


On Thu, Dec 04, 2025 at 10:08:46PM +0100, Osama Abdelkader wrote:
> Add a simple lsblk utility that lists information about block devices.
> Reads from /sys/block to enumerate devices and displays their size,
> type, and mount point.
> 
> Features:
> - Lists all block devices or specific devices
> - Shows device size in human-readable format (B, K, M, G, T, P)
> - Shows device type (disk, loop, rom, etc.)
> - Shows mount point if device is mounted
> - Sorts devices alphabetically
> - Minimal implementation (~2.5 kb)
> - NOFORK applet for efficiency
> 
> Signed-off-by: Osama Abdelkader <osama.abdelkader at gmail.com>
> ---
>  util-linux/lsblk.c | 219 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 219 insertions(+)
>  create mode 100644 util-linux/lsblk.c
> 
> diff --git a/util-linux/lsblk.c b/util-linux/lsblk.c
> new file mode 100644
> index 000000000..47f217fb8
> --- /dev/null
> +++ b/util-linux/lsblk.c
> @@ -0,0 +1,219 @@
> +/* vi: set sw=4 ts=4: */
> +/*
> + * Mini lsblk implementation for busybox
> + *
> + * Licensed under GPLv2 or later, see file LICENSE in this source tree.
> + */
> +//config:config LSBLK
> +//config:	bool "lsblk (2.5 kb)"
> +//config:	default y
> +//config:	help
> +//config:	List information about all available or specified block devices.
> +
> +//applet:IF_LSBLK(APPLET_NOFORK(lsblk, lsblk, BB_DIR_USR_BIN, BB_SUID_DROP, lsblk))
> +
> +//kbuild:lib-$(CONFIG_LSBLK) += lsblk.o
> +
> +//usage:#define lsblk_trivial_usage
> +//usage:       "[BLOCKDEVICE...]"
> +//usage:#define lsblk_full_usage "\n\n"
> +//usage:       "List information about all available or specified block devices"
> +
> +#include "libbb.h"
> +#include <mntent.h>
> +
> +/* This is a NOFORK applet. Be very careful! */
> +
> +struct blockdev_info {
> +	char *name;
> +	unsigned long long size;
> +	char *type;
> +	char *mountpoint;
> +};
> +
> +static unsigned long long read_size_from_sysfs(const char *devname)
> +{
> +	char path[256];
> +	char buf[64];
> +	ssize_t len;
> +	unsigned long long size = 0;
> +
> +	snprintf(path, sizeof(path), "/sys/block/%s/size", devname);
> +	len = open_read_close(path, buf, sizeof(buf) - 1);
> +	if (len > 0) {
> +		buf[len] = '\0';
> +		/* Remove trailing newline if present */
> +		if (buf[len - 1] == '\n')
> +			buf[len - 1] = '\0';
> +		size = bb_strtoull(buf, NULL, 10);
> +		/* size is in 512-byte sectors, convert to bytes */
> +		size *= 512;
> +	}
> +	return size;
> +}
> +
> +static char *get_device_type(const char *devname)
> +{
> +	char path[256];
> +	char *buf, *type = NULL;
> +	size_t len;
> +
> +	/* Try to read from uevent */
> +	snprintf(path, sizeof(path), "/sys/block/%s/uevent", devname);
> +	buf = xmalloc_open_read_close(path, &len);
> +	if (buf) {
> +		char *p = buf;
> +		while (*p) {
> +			if (strncmp(p, "DEVTYPE=", 8) == 0) {
> +				char *end;
> +				type = xstrdup(p + 8);
> +				/* Remove newline */
> +				end = strchr(type, '\n');
> +				if (end) *end = '\0';
> +				break;
> +			}
> +			p = strchr(p, '\n');
> +			if (!p) break;
> +			p++;
> +		}
> +		free(buf);
> +	}
> +
> +	/* Fallback: guess from device name */
> +	if (!type) {
> +		if (strncmp(devname, "loop", 4) == 0)
> +			type = xstrdup("loop");
> +		else if (strncmp(devname, "ram", 3) == 0)
> +			type = xstrdup("ram");
> +		else if (strncmp(devname, "nvme", 4) == 0 || strncmp(devname, "sd", 2) == 0 ||
> +		         strncmp(devname, "hd", 2) == 0 || strncmp(devname, "vd", 2) == 0)
> +			type = xstrdup("disk");
> +		else
> +			type = xstrdup("disk");
> +	}
> +
> +	return type;
> +}
> +
> +static char *get_mountpoint(const char *devname)
> +{
> +	char devpath[256];
> +	struct mntent *mnt;
> +	FILE *mtab;
> +	char *mountpoint = NULL;
> +
> +	snprintf(devpath, sizeof(devpath), "/dev/%s", devname);
> +	mtab = setmntent(bb_path_mtab_file, "r");
> +	if (mtab) {
> +		while ((mnt = getmntent(mtab)) != NULL) {
> +			if (strcmp(mnt->mnt_fsname, devpath) == 0) {
> +				mountpoint = xstrdup(mnt->mnt_dir);
> +				break;
> +			}
> +		}
> +		endmntent(mtab);
> +	}
> +
> +	return mountpoint;
> +}
> +
> +static void print_size(unsigned long long size)
> +{
> +	const char *units[] = {"B", "K", "M", "G", "T", "P"};
> +	int unit_idx = 0;
> +	double dsize = size;
> +
> +	while (dsize >= 1024.0 && unit_idx < 5) {
> +		dsize /= 1024.0;
> +		unit_idx++;
> +	}
> +
> +	if (unit_idx == 0)
> +		printf("%llu", size);
> +	else
> +		printf("%.1f%c", dsize, units[unit_idx][0]);
> +}
> +
> +static int compare_devices(const void *a, const void *b)
> +{
> +	const struct blockdev_info *da = (const struct blockdev_info *)a;
> +	const struct blockdev_info *db = (const struct blockdev_info *)b;
> +	return strcmp(da->name, db->name);
> +}
> +
> +int lsblk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
> +int lsblk_main(int argc UNUSED_PARAM, char **argv)
> +{
> +	DIR *dir;
> +	struct dirent *entry;
> +	struct blockdev_info *devices = NULL;
> +	int count = 0;
> +	int i;
> +	unsigned opt;
> +
> +	opt = getopt32(argv, "");
> +	argv += optind;
> +
> +	/* If specific devices are requested, process them */
> +	if (*argv) {
> +		while (*argv) {
> +			char *devname = *argv;
> +			/* Remove /dev/ prefix if present */
> +			if (strncmp(devname, "/dev/", 5) == 0)
> +				devname += 5;
> +
> +			devices = xrealloc_vector(devices, 4, count);
> +			devices[count].name = xstrdup(devname);
> +			devices[count].size = read_size_from_sysfs(devname);
> +			devices[count].type = get_device_type(devname);
> +			devices[count].mountpoint = get_mountpoint(devname);
> +			count++;
> +			argv++;
> +		}
> +	} else {
> +		/* Read all devices from /sys/block */
> +		dir = opendir("/sys/block");
> +		if (!dir)
> +			bb_simple_perror_msg_and_die("/sys/block");
> +
> +		while ((entry = readdir(dir)) != NULL) {
> +			if (DOT_OR_DOTDOT(entry->d_name))
> +				continue;
> +
> +			devices = xrealloc_vector(devices, 4, count);
> +			devices[count].name = xstrdup(entry->d_name);
> +			devices[count].size = read_size_from_sysfs(entry->d_name);
> +			devices[count].type = get_device_type(entry->d_name);
> +			devices[count].mountpoint = get_mountpoint(entry->d_name);
> +			count++;
> +		}
> +		closedir(dir);
> +	}
> +
> +	/* Sort devices by name */
> +	qsort(devices, count, sizeof(struct blockdev_info), compare_devices);
> +
> +	/* Print header */
> +	printf("%-15s %8s %-6s %s\n", "NAME", "SIZE", "TYPE", "MOUNTPOINT");
> +
> +	/* Print devices */
> +	for (i = 0; i < count; i++) {
> +		printf("%-15s ", devices[i].name);
> +		print_size(devices[i].size);
> +		printf(" %-6s ", devices[i].type);
> +		if (devices[i].mountpoint)
> +			printf("%s", devices[i].mountpoint);
> +		printf("\n");
> +
> +		free(devices[i].name);
> +		free(devices[i].type);
> +		if (devices[i].mountpoint)
> +			free(devices[i].mountpoint);
> +	}
> +
> +	if (devices)
> +		free(devices);
> +
> +	return fflush_all();
> +}
> +
> -- 
> 2.43.0
> 

ping.


More information about the busybox mailing list