On Thu, Jan 29, 2026 at 10:12 PM Osama Abdelkader via busybox
<[email protected]> wrote:
>
> 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 <[email protected]>
> > ---
> >  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) {
Why not get rid of while() loop and just do one search?
is_prefixed_with(buf, "DEVTYPE=") || strstr(buf, "\nDEVTYPE=")

> > +                             char *end;

> > +                             type = xstrdup(p + 8);
> > +                             /* Remove newline */
> > +                             end = strchr(type, '\n');
> > +                             if (end) *end = '\0';
> > +                             break;

Instead, you can:
p += 8;
end = strchrnul(type, '\n');
*end++ = '\0';
memmove(buf, p, end - p);
return xrealloc(buf, end - p);

If you do this, "if (!type)' later is always true and doesn't need to
be checked.

> > +                     }
> > +                     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");

We don't need to always malloc the value.
Just return constant (not-allocated) strings.


> > +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;
> > +}

Check whether find_mount_point() can be used for this.

> > +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]);
> > +}

The above can be done with smart_ulltoa5()

> > +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, "");

Variable 'opt' is unused.

> > +     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);

strdup'ing is not needed

> > +                     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");

util-linux's lsblk shows partitions too.
They can be found if you scan "/sys/class/block" instead.
(It also doesn't show "loop" - maybe shows only if it's attached?)

> > +             if (!dir)
> > +                     bb_simple_perror_msg_and_die("/sys/block");

Use xopendir()

> > +             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);

if (ENABLE_FEATURE_CLEAN_UP) closedir(dir);

> > +     /* 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");

This can be combined into fewer printf's

> > +             free(devices[i].name);
> > +             free(devices[i].type);
> > +             if (devices[i].mountpoint)
> > +                     free(devices[i].mountpoint);

Just do not bother. (Allows optimization where strings are *sometimes*
malloced).

> > +     }
> > +
> > +     if (devices)
> > +             free(devices);
> > +
> > +     return fflush_all();

fflush_stdout_and_exit_SUCCESS();
_______________________________________________
busybox mailing list
[email protected]
https://lists.busybox.net/mailman/listinfo/busybox

Reply via email to