mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Tobias Waldekranz <tobias@waldekranz.com>
To: barebox@lists.infradead.org
Subject: [RFC next 2/2] commands: lvm: inspect VGs, activate LVs
Date: Mon, 24 Aug 2026 18:31:21 +0000	[thread overview]
Message-ID: <20260824183203.3759144-3-tobias@waldekranz.com> (raw)
In-Reply-To: <20260824183203.3759144-1-tobias@waldekranz.com>

Provide the most rudimentary commands needed to inspect and activate
volume groups (VGs) and logical volumes (LVs).

Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
 commands/Kconfig  |  11 ++
 commands/Makefile |   1 +
 commands/lvm.c    | 264 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 276 insertions(+)
 create mode 100644 commands/lvm.c

diff --git a/commands/Kconfig b/commands/Kconfig
index 08fe544ee1..d7e3bab3f9 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -808,6 +808,17 @@ config CMD_VERITYSETUP
 	  commands:
 	        open <data-dev> <name> <hash-dev> <root-hash>
 
+config CMD_LVM
+	tristate
+	depends on DM_LVM
+	prompt "lvm"
+	help
+	  lvm - inspect and activate LVM2 logical volumes
+
+	  commands:
+	        pvs / vgs / lvs
+	        activate <vg>/<lv> [<name>]
+
 # end Partition commands
 endmenu
 
diff --git a/commands/Makefile b/commands/Makefile
index 7f0c68a5e5..7dae582225 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -172,6 +172,7 @@ obj-$(CONFIG_CMD_EFI_HANDLE_DUMP)	+= efi_handle_dump.o
 obj-$(CONFIG_CMD_HOST)		+= host.o
 obj-$(CONFIG_CMD_DMSETUP)	+= dmsetup.o
 obj-$(CONFIG_CMD_VERITYSETUP)	+= veritysetup.o
+obj-$(CONFIG_CMD_LVM)		+= lvm.o
 obj-$(CONFIG_CMD_SCONFIG)	+= sconfig.o
 obj-$(CONFIG_CMD_RKSECURE)	+= rksecure.o
 UBSAN_SANITIZE_ubsan.o := y
diff --git a/commands/lvm.c b/commands/lvm.c
new file mode 100644
index 0000000000..9ae9e091c1
--- /dev/null
+++ b/commands/lvm.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2026 Tobias Waldekranz <tobias@waldekranz.com>
+
+#include <block.h>
+#include <command.h>
+#include <device-mapper.h>
+#include <driver.h>
+#include <fs.h>
+#include <stdio.h>
+#include <xfuncs.h>
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/sprintf.h>
+
+#include <lvm.h>
+
+static int lvm_vg_by_devpath(const char *devpath, struct lvm_vg **vgp)
+{
+	struct cdev *cdev;
+
+	cdev = cdev_by_name(devpath_to_name(devpath));
+	if (!cdev)
+		return -ENODEV;
+
+	return lvm_vg_alloc_by_cdev(cdev, vgp);
+}
+
+static int lvm_vg_by_spec(const char *spec, struct lvm_vg **vgp)
+{
+	int err;
+
+	err = lvm_vg_by_devpath(spec, vgp);
+	switch (err) {
+	case 0:
+		return 0;
+	case -ENODEV:
+		err = lvm_vg_alloc_by_name(spec, vgp);
+		if (err)
+			printf("Found no volume group named \"%s\"\n", spec);
+
+		return err;
+	}
+
+	printf("No volume group found on \"%s\"\n", spec);
+	return err;
+}
+
+/* Total size of a VG, in sectors, i.e. the sum of the extents of all
+ * its physical volumes.
+ */
+static blkcnt_t lvm_vg_size(struct lvm_vg *vg)
+{
+	blkcnt_t sectors = 0;
+	size_t i;
+
+	for (i = 0; i < vg->num_pvs; i++)
+		sectors += vg->pvs[i]->pe_count * vg->pe_size;
+
+	return sectors;
+}
+
+/* LVM UUIDs are conventionally displayed in 6-4-4-4-4-4-6 groups. */
+#define LVM_UUID_STR_LEN (LVM_UUID_LEN + 6)
+
+static const char *lvm_uuid_str(char *buf, const char *uuid)
+{
+	static const int grp[] = { 6, 4, 4, 4, 4, 4, 6 };
+	char *out = buf;
+	size_t g;
+
+	for (g = 0; g < ARRAY_SIZE(grp); g++) {
+		if (g)
+			*out++ = '-';
+		memcpy(out, uuid, grp[g]);
+		out += grp[g];
+		uuid += grp[g];
+	}
+	*out = '\0';
+	return buf;
+}
+
+static void lvm_info_vg(struct lvm_vg *vg)
+{
+	char uuid[LVM_UUID_STR_LEN + 1];
+	char esz[32], vsz[32];
+	struct lvm_pv *pv;
+	struct lvm_lv *lv;
+	struct cdev *cdev;
+	size_t i;
+
+	strcpy(vsz, size_human_readable(lvm_vg_size(vg) << SECTOR_SHIFT));
+	strcpy(esz, size_human_readable(vg->pe_size << SECTOR_SHIFT));
+
+	printf("VG \"%s\":\n"
+	       "  UUID:    %s\n"
+	       "  SeqNum:  %llu\n"
+	       "  Size:    %s (%s extents)\n"
+	       "  #PV:     %zu\n"
+	       "  #LV:     %zu\n",
+	       vg->name, lvm_uuid_str(uuid, vg->uuid), vg->seqno, vsz, esz,
+	       vg->num_pvs, vg->num_lvs);
+
+	for (i = 0; i < vg->num_pvs; i++) {
+		pv = vg->pvs[i];
+		cdev = lvm_pv_cdev(pv);
+
+		printf("  PV %s:\n"
+		       "    UUID:  %s\n"
+		       "    Size:  %s\n",
+		       cdev ? cdev_name(cdev) : "[missing]",
+		       lvm_uuid_str(uuid, pv->uuid),
+		       size_human_readable((u64)pv->dev_size << SECTOR_SHIFT));
+	}
+
+	for (i = 0; i < vg->num_lvs; i++) {
+		lv = vg->lvs[i];
+
+		printf("  LV \"%s\":\n"
+		       "    UUID:  %s\n"
+		       "    Type:  %s\n"
+		       "    Size:  %s\n",
+		       lv->name, lvm_uuid_str(uuid, lv->uuid),
+		       lv->type == LVM_LV_LINEAR ? "linear" : "unknown",
+		       size_human_readable((u64)lv->size << SECTOR_SHIFT));
+	}
+}
+
+static void lvm_info_all(void)
+{
+	struct lvm_vg_iter *iter;
+	struct lvm_vg *vg;
+	int i = 0;
+
+	iter = lvm_vg_iter_new();
+	while ((vg = lvm_vg_iter_next(iter))) {
+		if (i++)
+			putchar('\n');
+
+		lvm_info_vg(vg);
+		lvm_vg_free(vg);
+	}
+
+	lvm_vg_iter_free(iter);
+}
+
+static int lvm_info(int argc, char *argv[])
+{
+	struct lvm_vg *vg = NULL;
+
+	if (argc > 1)
+		return COMMAND_ERROR_USAGE;
+
+	if (argc == 0) {
+		lvm_info_all();
+		return COMMAND_SUCCESS;
+	}
+
+	if (lvm_vg_by_spec(argv[0], &vg))
+		return COMMAND_ERROR;
+
+	lvm_info_vg(vg);
+	lvm_vg_free(vg);
+	return COMMAND_SUCCESS;
+}
+
+static int lvm_activate_lv(struct lvm_lv *lv, const char *name)
+{
+	char *defname, *table;
+	struct dm_device *dm;
+
+	table = lvm_lv_dm_ctable(lv);
+	if (IS_ERR(table)) {
+		printf("Cannot map %s/%s: %pe\n", lv->vg->name, lv->name, table);
+		return PTR_ERR(table);
+	}
+
+	defname = name ? NULL : xasprintf("%s-%s", lv->vg->name, lv->name);
+
+	dm = dm_create(name ? : defname, table);
+	free(table);
+	if (IS_ERR(dm))
+		printf("Failed to create %s: %pe\n", name ? : defname, dm);
+	else
+		printf("Created %s\n", name ? : defname);
+
+	free(defname);
+	return IS_ERR(dm) ? PTR_ERR(dm) : 0;
+}
+
+static int lvm_activate(int argc, char *argv[])
+{
+	struct lvm_vg *vg = NULL;
+	struct lvm_lv *lv;
+	int err;
+
+	if (argc < 2 || argc > 3)
+		return COMMAND_ERROR_USAGE;
+
+	if (lvm_vg_by_spec(argv[0], &vg))
+		return COMMAND_ERROR;
+
+	lv = lvm_vg_lv_by_name(vg, argv[1]);
+	if (!lv) {
+		printf("Logical volume \"%s/%s\" not found\n", vg->name, argv[1]);
+		err = -ENOENT;
+		goto out_free;
+	}
+
+	err = lvm_activate_lv(lv, (argc == 3) ? argv[2] : NULL);
+
+out_free:
+	lvm_vg_free(vg);
+	return err ? COMMAND_ERROR : COMMAND_SUCCESS;
+}
+
+static int do_lvm(int argc, char *argv[])
+{
+	const char *cmd;
+
+	switch (argc) {
+	case 1:
+		cmd = "info";
+		argc--;
+		argv++;
+		break;
+	default:
+		cmd = argv[1];
+		argc -= 2;
+		argv += 2;
+		break;
+	}
+
+	if (!strcmp(cmd, "info"))
+		return lvm_info(argc, argv);
+	else if (!strcmp(cmd, "activate"))
+		return lvm_activate(argc, argv);
+
+	printf("Unknown command: %s\n", cmd);
+	return COMMAND_ERROR_USAGE;
+}
+
+BAREBOX_CMD_HELP_START(lvm)
+BAREBOX_CMD_HELP_TEXT("lvm - inspect and activate LVM logical volumes")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Assembles the volume groups described by the LVM physical volumes")
+BAREBOX_CMD_HELP_TEXT("found on the available block devices, and activates logical volumes")
+BAREBOX_CMD_HELP_TEXT("as device mapper devices.")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("A <vg> may be given either as a volume group name or as the path to")
+BAREBOX_CMD_HELP_TEXT("a block device holding one of its physical volumes.")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("commands:")
+BAREBOX_CMD_HELP_OPT("info [<vg>]", "Show volume groups (all if <vg> is omitted)")
+BAREBOX_CMD_HELP_OPT("activate <vg> <lv> [<name>]", "Create a dm device for an LV")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(lvm)
+	.cmd = do_lvm,
+	BAREBOX_CMD_DESC("inspect and activate LVM logical volumes")
+	BAREBOX_CMD_OPTS("<command> [args...]")
+	BAREBOX_CMD_GROUP(CMD_GRP_PART)
+	BAREBOX_CMD_HELP(cmd_lvm_help)
+BAREBOX_CMD_END
-- 
2.43.0




  parent reply	other threads:[~2026-08-24 18:34 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-24 18:31 [RFC next 0/2] dm: lvm: Initial Logical Volume Management support Tobias Waldekranz
2026-08-24 18:31 ` [RFC next 1/2] " Tobias Waldekranz
2026-08-28 14:19   ` Sascha Hauer
2026-08-31  8:39     ` Tobias Waldekranz
2026-08-24 18:31 ` Tobias Waldekranz [this message]
2026-08-28 14:20   ` [RFC next 2/2] commands: lvm: inspect VGs, activate LVs Sascha Hauer
2026-08-31  8:40     ` Tobias Waldekranz
2026-08-28 14:10 ` [RFC next 0/2] dm: lvm: Initial Logical Volume Management support Sascha Hauer
2026-08-31  8:37   ` Tobias Waldekranz
2026-08-31  9:11     ` Sascha Hauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260824183203.3759144-3-tobias@waldekranz.com \
    --to=tobias@waldekranz.com \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox