public inbox for linux-btrfs@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] RFC: add the btrfs_info helper
@ 2023-12-06 19:32 Goffredo Baroncelli
  2023-12-06 19:32 ` [PATCH 1/4] btrfs-info: some utility to query a filesystem info Goffredo Baroncelli
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Goffredo Baroncelli @ 2023-12-06 19:32 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba, Goffredo Baroncelli

From: Goffredo Baroncelli <kreijack@inwind.it>

The aim of this patches set is to add some helper to query the layout
of a btrfs filesystem.

A btrfs filesystem may be composed by multiple disks, and several
mountpoints on which different subvolumes are mounted.

Sometime we need to know the devices list starting from a path, or
a path starting from a device.

These helpers allow to know all the information of a filesystem like
- UUID
- Label
- Mountpoints list (if mounted), and for each mountpoint
   - subvolume
   - options
- Devices list, and for each device
   - major, minor
   - device name
   - partuuid, uuid_sub 
   - devid (if the user is root)

And it is possible to build these information giving any of:
- a path (if mounted)
- UUID
- Label
- partuuid
- uuid_sub


These helpers uses only libblkid and /proc/self/mountinfo to extract all
these information. It is not required to be root; if the user is root
it is returned also the devid of a device.

The first patch add the helpers. From the second patch there are two new
commands ('btrfs fi info' and 'btrfs fi get-info') and an enachement of 
'btrfs fi label', which can be used passing both a device or a path,
 or UUID=<uuid>, LABEL=<label>... ; before the user has to pass a path
if the filesystem is mounted, and a device otherwise. This command
refuse to work with a device when the fielsystem is mounted, which is
not very user friendly.

The goal is to enanche some btrfs commands that now force the user to
pass a device or a path even when it is possible to pass any valid
reference to a btrfs filesystem (like UUID or Label).

BR

Goffredo Baroncelli (4):
  btrfs-info: some utility to query a filesystem info
  new command: btrfs filesystem info [<path>...]
  new command: btrfs filesystem get-info ...
  btrfs-progs: 'btrfs fi label' using a dev instead of a path

 Makefile                  |    1 +
 cmds/filesystem.c         |  141 +++++
 common/btrfs-info.c       | 1036 +++++++++++++++++++++++++++++++++++++
 common/btrfs-info.h       |  220 ++++++++
 common/filesystem-utils.c |   51 +-
 5 files changed, 1437 insertions(+), 12 deletions(-)
 create mode 100644 common/btrfs-info.c
 create mode 100644 common/btrfs-info.h

-- 
2.43.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/4] btrfs-info: some utility to query a filesystem info
  2023-12-06 19:32 [PATCH 0/4] RFC: add the btrfs_info helper Goffredo Baroncelli
@ 2023-12-06 19:32 ` Goffredo Baroncelli
  2023-12-06 19:32 ` [PATCH 2/4] new command: btrfs filesystem info [<path>...] Goffredo Baroncelli
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Goffredo Baroncelli @ 2023-12-06 19:32 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba, Goffredo Baroncelli

From: Goffredo Baroncelli <kreijack@inwind.it>

btrfs_info_XXX are a set of function to query the layout of a btrfs
filesystem.

Example:

	const struct btrfs_info *bi;
	int r = btrfs_info_find(path, &bi);
	if (r < 0) {
		printf("Error: %d - %s\n", -r, strerror(-r));
		return;
	}

	print("Label: %s\n", bi->label);
	print("UUID: %s\n", bi->uuid);

	printf("Devices:");
	for (const struct btrfs_info_device *bid = bi->devices ;
		bid ; bid = bid->next)
			printf("\t%s\n", bid->name);

	if (bi->mounts) {
		printf("Mounts:")
		for (const btrfs_info_mount *bim = bi->mounts ;
			bim ; bim = bim->next )
				print("\t%s\n", bim->dest);
	}


The client doesn't have to manage any memory allocation/deletion.
The functions return an internal list build on the basis of the
information returned by /proc/self/mountinfo and libblkid.
It is not required to be root; however only root can access
the devid of the devices.

Signed-off-by: Goffredo Baroncelli <kreijack@libero.it>
---
 Makefile            |    1 +
 common/btrfs-info.c | 1036 +++++++++++++++++++++++++++++++++++++++++++
 common/btrfs-info.h |  220 +++++++++
 3 files changed, 1257 insertions(+)
 create mode 100644 common/btrfs-info.c
 create mode 100644 common/btrfs-info.h

diff --git a/Makefile b/Makefile
index b7585674..2182bf03 100644
--- a/Makefile
+++ b/Makefile
@@ -213,6 +213,7 @@ objects = \
 	common/parse-utils.o	\
 	common/path-utils.o	\
 	common/rbtree-utils.o	\
+	common/btrfs-info.o	\
 	common/send-stream.o	\
 	common/send-utils.o	\
 	common/sort-utils.o	\
diff --git a/common/btrfs-info.c b/common/btrfs-info.c
new file mode 100644
index 00000000..d5c508f2
--- /dev/null
+++ b/common/btrfs-info.c
@@ -0,0 +1,1036 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <blkid/blkid.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/sysmacros.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "common/btrfs-info.h"
+#include "kernel-shared/ctree.h"
+
+static void free_info_dev(struct btrfs_info_device *pdev)
+{
+	if (pdev) {
+		free((char *)pdev->name);
+		free((char *)pdev->uuid_sub);
+		free((char *)pdev->partuuid);
+	}
+	free(pdev);
+}
+
+static void free_info_mount(struct btrfs_info_mount *p)
+{
+	if (p) {
+		free((char *)p->subvol);
+		free((char *)p->rootpath);
+		free((char *)p->dest);
+		free((char *)p->options);
+	}
+	free(p);
+}
+
+static void btrfs_info_free(struct btrfs_info *p)
+{
+	while (p) {
+		struct btrfs_info *pnext2;
+
+		pnext2 = (struct btrfs_info *)p->next;
+
+		free((char *)p->uuid);
+		free((char *)p->label);
+
+		struct btrfs_info_device *pdev;
+
+		pdev = (struct btrfs_info_device *)p->devices;
+		while (pdev) {
+			struct btrfs_info_device *pnext;
+
+			pnext = (struct btrfs_info_device *)pdev->next;
+
+			free_info_dev(pdev);
+			pdev = pnext;
+		}
+
+		struct btrfs_info_mount *pmnt;
+
+		pmnt = (struct btrfs_info_mount *)p->mounts;
+		while (pmnt) {
+			struct btrfs_info_mount *pnext;
+			pnext = (struct btrfs_info_mount *)pmnt->next;
+
+			free_info_mount(pmnt);
+			pmnt = pnext;
+		}
+
+		free(p);
+		p = pnext2;
+	}
+}
+
+static int get_dev_id(const char *devname, const char *uuid_sub, uint64_t *ret)
+{
+	*ret = (uint64_t)-1;
+
+	if (access(devname, R_OK))
+		return 0;
+
+	int fd = open(devname, O_RDONLY);
+	if (fd < 0)
+		return -errno;
+
+	int64_t offsets[] = { 64*1024, 64*1024*1024, 256 * 1024*1024*1024llu, 0 };
+	int j;
+
+	for (j = 0 ; offsets[j] ; j++) {
+
+		struct btrfs_super_block sb;
+		int r = lseek(fd, offsets[j], SEEK_SET);
+		if (r < 0)
+			continue;
+
+		int l = read(fd, &sb, sizeof(sb));
+		if (l != sizeof(sb))
+			continue;
+
+		/*
+		 * TODO: check:
+		 * - cksum
+		 */
+
+		if (le64_to_cpu(sb.magic) != BTRFS_MAGIC)
+			continue;
+
+		unsigned char *p = sb.dev_item.uuid;
+		const char *p2 = uuid_sub;
+		int i;
+
+		for (i = 0; i < BTRFS_UUID_SIZE ; i++, p++, p2 += 2) {
+			if (*p2 == '-')
+				p2++;
+			if (!*p2 || !*(p2+1))
+				break;
+
+			char buf[3] = { *p2, *(p2+1), 0 };
+			unsigned char v = (unsigned char)strtoul(buf, NULL, 16);
+
+			if (v != *p)
+				break;
+		}
+		if (i < BTRFS_UUID_SIZE)
+			continue;
+
+		*ret = le64_to_cpu(sb.dev_item.devid);
+		return 0;
+	}
+	close(fd);
+	return -EINVAL;
+
+}
+
+static int append_btrfs_info_device(struct btrfs_info *bi, const char *name,
+				    const char *uuid_sub, const char *partuuid,
+				    int major, int minor)
+{
+	struct btrfs_info_device *p = NULL;
+
+	p = calloc(1, sizeof(struct btrfs_info_device));
+	if (!p)
+		goto nomem;
+
+	if (get_dev_id(name, uuid_sub, &p->devid) < 0)
+		goto nomem;
+
+	if (!name)
+		name = "";
+	p->name = strdup(name);
+	if (!p->name)
+		goto nomem;
+
+	p->uuid_sub = strdup(uuid_sub);
+	if (!p->uuid_sub)
+		goto nomem;
+
+	if (!partuuid)
+		partuuid = "";
+	p->partuuid = strdup(partuuid);
+	if (!p->partuuid)
+		goto nomem;
+
+	p->major = major;
+	p->minor = minor;
+
+	p->next = bi->devices;
+	bi->devices = p;
+
+	return 0;
+
+nomem:
+	free_info_dev(p);
+	return -ENOMEM;
+
+}
+
+static int append_or_get_btrfs_info(const char *uuid,
+				    struct btrfs_info **root,
+				    struct btrfs_info **ret)
+{
+	const struct btrfs_info *pc;
+
+	for (pc = *root ; pc ; pc = pc->next)
+		if (!strcmp(pc->uuid, uuid)) {
+			*ret = (struct btrfs_info *)pc;
+			return 0;
+		}
+
+	struct btrfs_info *p;
+
+	p = calloc(1, sizeof(struct btrfs_info));
+	if (!p) {
+		*ret = NULL;
+		return -ENOMEM;
+	}
+
+	p->uuid = strdup(uuid);
+	if (!p->uuid) {
+		free(p);
+		*ret = NULL;
+		return -ENOMEM;
+	}
+
+	p->next = *root;
+	*ret = *root = p;
+
+	return 0;
+}
+
+void btrfs_info_dump_single_entry(const struct btrfs_info *p, FILE *o)
+{
+	fprintf(o, "UUID:    %s\n", p->uuid);
+	fprintf(o, "LABEL:   %s\n", p->label);
+
+	fprintf(o, "devices:\n");
+	const struct btrfs_info_device *pdev;
+
+	for (pdev = p->devices ; pdev ; pdev = pdev->next) {
+		fprintf(o, "\tdev:      %s\n", pdev->name);
+		fprintf(o, "\tUUID_SUB: %s\n", pdev->uuid_sub);
+		fprintf(o, "\tPARTUUID: %s\n", pdev->partuuid);
+		fprintf(o, "\tmajor:    %d\n", pdev->major);
+		fprintf(o, "\tminor:    %d\n", pdev->minor);
+		fprintf(o, "\tdevid:    %" PRIu64 "\n", pdev->devid);
+		fprintf(o, "\n");
+	}
+
+	fprintf(o, "mounts:\n");
+	const struct btrfs_info_mount *pmnt;
+
+	for (pmnt = p->mounts ; pmnt ; pmnt = pmnt->next) {
+		fprintf(o, "\tdest:     %s\n", pmnt->dest);
+		fprintf(o, "\trootpath: %s\n", pmnt->rootpath);
+		fprintf(o, "\tsubvol:   %s\n", pmnt->subvol);
+		fprintf(o, "\toptions:  %s\n", pmnt->options);
+		fprintf(o, "\tseq:      %d\n", pmnt->seq);
+		fprintf(o, "\tover:     %d\n", pmnt->over);
+		fprintf(o, "\n");
+	}
+
+	fprintf(o, "\n");
+}
+
+void btrfs_info_dump(const struct btrfs_info *p, FILE *o)
+{
+	for (; p ; p = (struct btrfs_info *)p->next)
+		btrfs_info_dump_single_entry(p, o);
+}
+
+/*
+ * /proc/self/mountinfo escapes space, '\' and comma with the sequence
+ * \ooo
+ */
+static char *unescape(char *src)
+{
+	if (!src)
+		return NULL;
+
+	char *p, *s;
+
+	p = s = src;
+
+	while (*s) {
+		if (*s != '\\') {
+			*p++ = *s++;
+			continue;
+		}
+
+		/* convert \xxx to a char value */
+		s++; /* skip '\' */
+
+		int v, i;
+
+		for (v = 0, i = 0 ; i < 3 ; i++) {
+			assert(isdigit(*s));
+			v = v * 8 + *s++ - '0';
+		}
+		*p++ = v;
+	}
+	*p = 0;
+	return src;
+}
+
+static char *find_subvol(const char *options)
+{
+	const char *p1 = options;
+
+	while (p1) {
+		const char *p2 = strchr(p1, ',');
+		if (!p2) {
+			p2 = p1;
+			while (*p2)
+				p2++;
+		}
+
+		/* NB: strlen(subvol=) == 7 */
+		if (strncmp(p1, "subvol=", 7)) {
+			p1 = p2;
+			if (*p1)
+				p1++;
+			continue;
+		}
+
+		p1 += 7;
+		int l = p2 - p1 + 1;
+
+		char *dest = malloc(l);
+		if (!dest)
+			return NULL;
+
+		strncpy(dest, p1, l);
+		return dest;
+	}
+	return strdup("");
+}
+
+static int add_mounts(struct btrfs_info *root,
+		      int seq, int over, const char *rootpath, const char *dest,
+		      const char *options1, const char *options2,
+		      const char *dev)
+{
+	struct stat st;
+	const struct btrfs_info *bi;
+	int mj, mn, l;
+	struct btrfs_info_mount *pmnt;
+
+	stat(dev, &st);
+	mj = major(st.st_rdev);
+	mn = minor(st.st_rdev);
+
+	for (bi = root ; bi ; bi = bi->next) {
+		const struct btrfs_info_device *bdev;
+
+		for (bdev = bi->devices ; bdev ; bdev = bdev->next) {
+			if (bdev->major == mj && bdev->minor == mn)
+				break;
+		}
+		if (bdev)
+			break;
+	}
+
+	if (!bi)
+		return -ENOENT;
+
+	pmnt = calloc(1, sizeof(struct btrfs_info_mount));
+	if (!pmnt)
+		goto nomem;
+
+	pmnt->dest = unescape(strdup(dest));
+	if (!pmnt->dest)
+		goto nomem;
+
+	pmnt->rootpath = unescape(strdup(rootpath));
+	if (!pmnt->dest)
+		goto nomem;
+
+	l = strlen(options1) + strlen(options2) + 2;
+	pmnt->options = malloc(l);
+	if (!pmnt->options)
+		goto nomem;
+
+	strcpy((char *)pmnt->options, options1);
+	strcat((char *)pmnt->options, ",");
+	strcat((char *)pmnt->options, options2);
+
+	pmnt->subvol = unescape(find_subvol(pmnt->options));
+	if (!pmnt->subvol)
+		goto nomem;
+
+	pmnt->seq = seq;
+	pmnt->over = over;
+
+	pmnt->next = bi->mounts;
+	((struct btrfs_info *)bi)->mounts = pmnt;
+
+	return 0;
+
+nomem:
+	free_info_mount(pmnt);
+	return -ENOMEM;
+}
+
+struct _btrfs_info_dest {
+	const char *dest;
+	int seq;
+
+	struct _btrfs_info_dest *next;
+};
+
+static void free_info_dests(struct _btrfs_info_dest *p)
+{
+	while (p) {
+		struct _btrfs_info_dest *pnext = p->next;
+
+		free((char *)p->dest);
+		free(p);
+		p = pnext;
+	}
+}
+
+static int add_info_dest(struct _btrfs_info_dest **info_dests,
+			 const char *dest, int seq)
+{
+	struct _btrfs_info_dest *bi_dest;
+
+	bi_dest = calloc(1, sizeof(struct _btrfs_info_dest));
+	if (!bi_dest)
+		return -ENOMEM;
+
+	bi_dest->dest = strdup(dest);
+	if (!bi_dest->dest) {
+		free(bi_dest);
+		return -ENOMEM;
+	}
+
+	bi_dest->seq = seq;
+
+	bi_dest->next = *info_dests;
+	*info_dests = bi_dest;
+
+	return 0;
+}
+
+static int btrfs_info_init(struct btrfs_info **root_ret,
+			   struct _btrfs_info_dest **info_dests_ret)
+{
+	int r;
+	blkid_cache cache;
+	blkid_dev_iterate iter;
+	blkid_dev dev;
+	struct btrfs_info *root = NULL;
+	struct _btrfs_info_dest *info_dests = NULL;
+	FILE *fp = NULL;
+	char buf[2048];
+	int ret;
+
+	*root_ret = NULL;
+	*info_dests_ret = NULL;
+
+	r = blkid_get_cache(&cache, NULL);
+	if (r < 0) {
+		fprintf(stderr, "ERROR: cannot load cache\n");
+		return -EACCES;
+	}
+
+	iter = blkid_dev_iterate_begin(cache);
+	blkid_dev_set_search(iter, "TYPE", "btrfs");
+	while (!blkid_dev_next(iter, &dev)) {
+		struct stat st;
+		const char *n = blkid_dev_devname(dev);
+		struct btrfs_info *p;
+		const char *l, *uuid, *uuid_sub, *partuuid;
+
+		uuid = blkid_get_tag_value(cache, "UUID", n);
+		uuid_sub = blkid_get_tag_value(cache, "UUID_SUB", n);
+		r = append_or_get_btrfs_info(uuid, &root, &p);
+
+		if (r < 0) {
+			fprintf(stderr, "ERROR: not enough memory\n");
+			blkid_dev_iterate_end(iter);
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		l = blkid_get_tag_value(cache, "LABEL", n);
+		if (l) {
+			p->label = strdup(l);
+			if (!p->label) {
+				fprintf(stderr, "ERROR: not enough memory\n");
+				blkid_dev_iterate_end(iter);
+				ret = -ENOMEM;
+				goto out;
+			}
+		}
+
+		partuuid = blkid_get_tag_value(cache, "PARTUUID", n);
+
+		r = stat(n, &st);
+		if (r < 0) {
+			fprintf(stderr, "ERROR: cannot stat '%s'\n", n);
+			blkid_dev_iterate_end(iter);
+			ret = -ENODEV;
+			goto out;
+		}
+
+		r = append_btrfs_info_device(p, n, uuid_sub, partuuid,
+					     major(st.st_rdev), minor(st.st_rdev));
+		if (r < 0) {
+			fprintf(stderr, "ERROR: not enough memory\n");
+			blkid_dev_iterate_end(iter);
+			ret = -ENOMEM;
+			goto out;
+		}
+
+	}
+	blkid_dev_iterate_end(iter);
+
+	fp = fopen("/proc/self/mountinfo", "r");
+	if (!fp) {
+		int e = errno;
+
+		fprintf(stderr, "ERROR: cannot open '/proc/self/mountinfo': %s [%d]\n",
+		    strerror(e), e);
+		ret = -EACCES;
+		goto out;
+	}
+
+	while (fgets(buf, 2047, fp) != NULL) {
+		int l = strlen(buf);
+		char *tk;
+		char *dest = NULL, *subvol = NULL, *options1 = NULL;
+		char *devname = NULL, *options2 = NULL;
+		int seq = -1, i, over = -1;
+		int skip = 0;
+
+		if (l < 1)
+			continue;
+		if (buf[l-1] != '\n') {
+			fprintf(stderr, "ERROR: line too long in '/proc/self/mountinfo'\n");
+			ret = -E2BIG;
+			goto out;
+		}
+
+		buf[l-1] = 0;
+
+		for (tk = strtok(buf, " "), i = 0;
+		     tk;
+		     tk = strtok(NULL, " "), i++) {
+			switch (i) {
+			case 0:
+				seq = atoi(tk);
+				break;
+			case 1:
+				over = atoi(tk);
+				break;
+			case 3:
+				subvol = tk;
+				break;
+			case 4:
+				dest = tk;
+				break;
+			case 5:
+				options1 = tk;
+				break;
+			case 8:
+				if (strcmp(tk, "btrfs"))
+					skip = 1;
+				break;
+			case 9:
+				devname = tk;
+				break;
+			case 10:
+				options2 = tk;
+				break;
+			}
+		}
+
+		if (add_info_dest(&info_dests, dest, seq) < 0) {
+			fprintf(stderr, "ERROR: not enough memory\n");
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		if (skip)
+			continue;
+
+		r = add_mounts(root, seq, over, subvol, dest,
+			       options1, options2, devname);
+		if (r < 0) {
+			fprintf(stderr, "ERROR: not enough memory\n");
+			ret = -ENOMEM;
+			goto out;
+		}
+	}
+
+	ret = 0;
+out:
+
+	if (ret) {
+		btrfs_info_free(root);
+		free_info_dests(info_dests);
+	} else {
+		*root_ret = root;
+		*info_dests_ret = info_dests;
+	}
+	if (fp)
+		fclose(fp);
+	return ret;
+
+}
+
+static struct btrfs_info *_btrfs_info_global; /* default to NULL */
+static struct _btrfs_info_dest *_btrfs_info_dests_global; /* default to NULL */
+
+int btrfs_info_get(const struct btrfs_info **ret)
+{
+	int r = 0;
+	if (!_btrfs_info_global)
+		r = btrfs_info_init(&_btrfs_info_global,
+				    &_btrfs_info_dests_global);
+
+	if (ret)
+		*ret = _btrfs_info_global;
+	return r;
+}
+
+int btrfs_info_refresh(const struct btrfs_info **ret)
+{
+	if (_btrfs_info_global) {
+		btrfs_info_free(_btrfs_info_global);
+		free_info_dests(_btrfs_info_dests_global);
+		_btrfs_info_global = NULL;
+		_btrfs_info_dests_global = NULL;
+	}
+	return btrfs_info_get(ret);
+}
+
+int btrfs_info_find_by_label(const char *label, const struct btrfs_info **ret)
+{
+	const struct btrfs_info *root;
+
+	if (ret)
+		*ret = NULL;
+
+	int r = btrfs_info_get(&root);
+	if (r)
+		return r;
+
+	const struct btrfs_info *p;
+
+	for (p = root; p ; p = p->next) {
+		if (!strcmp(p->label, label)) {
+			if (ret)
+				*ret = p;
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
+int btrfs_info_find_by_uuid(const char *uuid, const struct btrfs_info **ret)
+{
+	const struct btrfs_info *root;
+
+	if (ret)
+		*ret = NULL;
+
+	int r = btrfs_info_get(&root);
+	if (r)
+		return r;
+
+	const struct btrfs_info *p;
+
+	for (p = root; p ; p = p->next) {
+		if (!strcasecmp(p->uuid, uuid)) {
+			if (ret)
+				*ret = p;
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
+int btrfs_info_find_by_dev(const char *dev, const struct btrfs_info **ret,
+			    const char **retdev)
+{
+
+	if (ret)
+		*ret = NULL;
+	if (retdev)
+		*retdev = NULL;
+
+	struct stat st;
+	int r;
+
+	r = stat(dev, &st);
+	if (r)
+		return -EINVAL;
+
+	int mj, mn;
+
+	mj = major(st.st_rdev);
+	mn = minor(st.st_rdev);
+
+	const struct btrfs_info *root;
+
+	r = btrfs_info_get(&root);
+	if (r)
+		return r;
+
+	const struct btrfs_info *p;
+
+	for (p = root ; p ; p = p->next) {
+		const struct btrfs_info_device *d;
+
+		for (d = p->devices; d ; d = d->next) {
+			if (mj == d->major && mn == d->minor) {
+				if (ret)
+					*ret = p;
+				if (retdev)
+					*retdev = d->name;
+				return 0;
+			}
+		}
+	}
+
+	return -ENOENT;
+}
+
+int btrfs_info_find_by_uuid_sub(const char *uuid_sub,
+				const struct btrfs_info **ret,
+				const char **retdev)
+{
+	if (ret)
+		*ret = NULL;
+	if (retdev)
+		*retdev = NULL;
+
+	const struct btrfs_info *root;
+	int r = btrfs_info_get(&root);
+	if (r)
+		return r;
+
+	const struct btrfs_info *p;
+	for (p = root; p ; p = p->next) {
+		const struct btrfs_info_device *d;
+
+		for (d = p->devices; d ; d = d->next) {
+			if (!strcmp(uuid_sub, d->uuid_sub)) {
+				if (ret)
+					*ret = p;
+				if (retdev)
+					*retdev = d->name;
+				return 0;
+			}
+		}
+	}
+
+	return -ENOENT;
+}
+
+int btrfs_info_find_by_partuuid(const char *partuuid,
+				const struct btrfs_info **ret,
+				const char **retdev)
+{
+
+	if (ret)
+		*ret = NULL;
+	if (retdev)
+		*retdev = NULL;
+
+	const struct btrfs_info *root;
+	int r = btrfs_info_get(&root);
+	if (r)
+		return r;
+
+	const struct btrfs_info *p;
+
+	for (p = root; p ; p = p->next) {
+		const struct btrfs_info_device *d;
+
+		for (d = p->devices; d ; d = d->next) {
+			if (!strcmp(partuuid, d->partuuid)) {
+				if (ret)
+					*ret = p;
+				if (retdev)
+					*retdev = d->name;
+				return 0;
+			}
+		}
+	}
+
+	return -ENOENT;
+}
+
+int btrfs_info_find_by_path(const char *path,
+			    const struct btrfs_info **ret)
+{
+	if (ret)
+		*ret = NULL;
+
+	/* fill the cache */
+	const struct btrfs_info *root;
+	int r = btrfs_info_get(&root);
+	if (r) {
+		/* if /proc or /sys aren't available, return path */
+		return r;
+	}
+
+	/*
+	 * search in all the possible mount points first, then look for the found seq
+	 */
+	int seq = -1;
+	struct _btrfs_info_dest *bi_dest;
+
+	for (bi_dest = _btrfs_info_dests_global; bi_dest; bi_dest = bi_dest->next) {
+		int l = strlen(bi_dest->dest);
+		if (strncmp(bi_dest->dest, path, l))
+			continue;
+		/*
+		 * search for the highest seq
+		 */
+		if (seq != -1 && seq > bi_dest->seq)
+			continue;
+
+		seq = bi_dest->seq;
+	}
+
+	/*
+	 * look if seq is a valid btrfs mountpoint
+	 */
+	const struct btrfs_info *p;
+
+	for (p = root ; p ; p = p->next) {
+		const struct btrfs_info_mount *pm;
+
+		for (pm = p->mounts ; pm ; pm = pm->next)
+			if (pm->seq == seq) {
+				if (ret)
+					*ret = p;
+				return 0;
+			}
+	}
+
+	return -ENOENT;
+}
+
+int btrfs_info_find(const char *filter, const struct btrfs_info **ret)
+{
+
+	if (ret)
+		*ret = NULL;
+
+	if (!strncmp(filter, "LABEL=", 6))
+		return btrfs_info_find_by_label(filter + 6, ret);
+
+	if (!strncmp(filter, "UUID=", 5))
+		return btrfs_info_find_by_uuid(filter + 5, ret);
+
+	if (!strncmp(filter, "UUID_SUB=", 9))
+		return btrfs_info_find_by_uuid_sub(filter + 9, ret, NULL);
+
+	if (!strncmp(filter, "PARTUUID=", 9))
+		return btrfs_info_find_by_partuuid(filter + 9, ret, NULL);
+
+	struct stat st;
+	int r;
+
+	r = stat(filter, &st);
+	if (r < 0) {
+		fprintf(stderr, "ERROR: cannot access '%s'\n", filter);
+		return -EINVAL;
+	}
+	if (S_ISBLK(st.st_mode))
+		return btrfs_info_find_by_dev(filter, ret, NULL);
+
+	char *path = realpath(filter, NULL);
+	if (!path) {
+		fprintf(stderr, "ERROR: in realpath(%s)\n", filter);
+		return -EINVAL;
+	}
+
+	r = btrfs_info_find_by_path(path, ret);
+	free(path);
+
+	return r;
+}
+
+const char *btrfs_info_find_valid_path(const struct btrfs_info *bfs)
+{
+	const struct btrfs_info_mount *pm;
+	const char *ret = NULL;
+
+	for (pm = bfs->mounts ; pm ; pm = pm->next) {
+		int seq = -1;
+		struct _btrfs_info_dest *bi_dest;
+
+		for (bi_dest = _btrfs_info_dests_global; bi_dest; bi_dest = bi_dest->next) {
+			int l = strlen(bi_dest->dest);
+
+			if (strncmp(bi_dest->dest, pm->dest, l))
+				continue;
+
+			/*
+			 * search for the highest seq
+			 */
+			if (seq != -1 && seq > bi_dest->seq)
+				continue;
+
+			seq = bi_dest->seq;
+		}
+
+		if (seq != pm->seq)
+			continue;
+		if (access(pm->dest, R_OK|X_OK))
+			continue;
+
+		/*
+		 * search for the shortest path
+		 */
+		if (!ret || strlen(ret) > strlen(pm->dest))
+			ret = pm->dest;
+	}
+
+	return ret;
+}
+
+const char *btrfs_info_find_valid_dev(const struct btrfs_info *bfs)
+{
+	const struct btrfs_info_device *pd;
+
+	for (pd = bfs->devices ; pd ; pd = pd->next) {
+		if (access(pd->name, R_OK|X_OK))
+			return pd->name;
+	}
+
+	return NULL;
+}
+
+
+int btrfs_info_find_dev(const char *filter,
+			const struct btrfs_info **inforet,
+			const char **retdev)
+{
+	int r;
+
+	if (!strncmp(filter, "UUID_SUB=", 9)) {
+		r = btrfs_info_find_by_uuid_sub(filter + 9, inforet, retdev);
+		if (r < 0)
+			*retdev = NULL;
+	} else if (!strncmp(filter, "PARTUUID=", 9)) {
+		r = btrfs_info_find_by_partuuid(filter + 9, inforet, retdev);
+		if (r < 0)
+			*retdev = NULL;
+	} else {
+		r = btrfs_info_find_by_dev(filter, inforet, retdev);
+		if (r < 0)
+			*retdev = filter;
+	}
+
+	return r;
+}
+
+#ifdef TEST
+/*
+ * compile with:
+ * gcc -DTEST  -Wall -I.. -I../include -o /tmp/btrfs-info btrfs-info.c -lblkid
+ */
+int main(int argc, char **argv)
+{
+
+	if (argc == 1) {
+		struct btrfs_info *root = NULL;
+		struct _btrfs_info_dest *dest_root = NULL;
+
+		btrfs_info_init(&root, &dest_root);
+		btrfs_info_dump(root, stdout);
+		btrfs_info_free(root);
+		free_info_dests(dest_root);
+
+		return 0;
+	}
+
+	int i = 1;
+
+	while (i < argc) {
+
+		const char *filter = argv[i];
+		const char *retdev = NULL;
+		const char *retpath = NULL;
+		const struct btrfs_info *info;
+		int r;
+
+		if (!strncmp(argv[i], "/dev/", 5) ||
+		    !strncmp(argv[i], "PARTUUID=", 9) ||
+		    !strncmp(argv[i], "UUID_SUB=", 9)) {
+			/* search by dev */
+			r = btrfs_info_find_dev(filter, &info, &retdev);
+			if (!r)
+				retpath = btrfs_info_find_valid_path(info);
+		} else if (!strncmp(argv[i], "UUID=", 5) ||
+			   !strncmp(argv[i], "LABEL=", 6)) {
+			/* search by a filesystem ID */
+			r = btrfs_info_find(filter, &info);
+			if (!r) {
+				retpath = btrfs_info_find_valid_path(info);
+				retdev = btrfs_info_find_valid_dev(info);
+			}
+		} else {
+			/* search by path */
+			r = btrfs_info_find(filter, &info);
+			if (!r) {
+				retpath = filter;
+				retdev = btrfs_info_find_valid_dev(info);
+			}
+		}
+
+		if (r) {
+			fprintf(stderr, "ERROR: cannot find info about '%s'; %s [%d]\n",
+				argv[i], strerror(-r), -r);
+			i += 1;
+			continue;
+		}
+		printf("%s:\n", argv[i]);
+		btrfs_info_dump_single_entry(info, stdout);
+		printf("\n");
+
+		printf("path: %s\n", retpath);
+		printf("dev:  %s\n", retdev);
+		i++;
+	}
+
+	return 0;
+}
+
+#endif
diff --git a/common/btrfs-info.h b/common/btrfs-info.h
new file mode 100644
index 00000000..791ae4f7
--- /dev/null
+++ b/common/btrfs-info.h
@@ -0,0 +1,220 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+struct btrfs_info_device {
+	int major, minor;
+	uint64_t devid;		/* only if invoked by root */
+	const char *name;
+	const char *uuid_sub;
+	const char *partuuid;
+
+	const struct btrfs_info_device *next;
+};
+
+struct btrfs_info_mount {
+	const char *dest;	/* dest dir of mount */
+	const char *rootpath;	/* src dir of mount */
+	const char *subvol;	/* subvol which contains src dir */
+	const char *options;	/* comma separated options */
+	int seq, over;
+
+	const struct btrfs_info_mount *next;
+};
+/*
+ * Two words about the differences between 'rootpath' and 'subvol'.
+ * VFS doesn't have the concept of subvol. But it has the concept to mount a
+ * subdir of a filesystem.
+ * BTRFS uses this capability to mount a subvolume of a filesystem using
+ * the -o subvol=<subvolpath> syntax. But this is equal to the more generic syntax
+ * -o X-mount.subdir=<subvolpath> . In the latter case you are not limited to
+ * mount a subvol but it is possible to mount an arbitrary subdir.
+ *
+ * In case you do a
+ *   mount -o X-mount.subdir=<subvolpath/subdirname> <dev> <dest>
+ * you will get
+ *   dest = <dest>
+ *   rootpath = <subvolpath/subdirname>
+ *   subvol = <subvolpath>
+ *
+ * Instead if you do
+ *   mount -o X-mount.subdir=<subvolpath> <dev> <dest>
+ * or
+ *   mount -o subvol=<subvolpath> <dev> <dest>
+ * you will get
+ *   dest = <dest>
+ *   rootpath = <subvolpath>
+ *   subvol = <subvolpath>
+ */
+
+struct btrfs_info {
+	/* UUID of the filesystem */
+	const char *uuid;
+
+	/* LABEL of the filesystem, may be empty "" */
+	const char *label;
+
+	/*
+	 * this is a list of the mount points; mounts may be NULL if the
+	 * filesystem is not mounted
+	 */
+	const struct btrfs_info_mount *mounts;
+
+	/* list of filesystem devices */
+	const struct btrfs_info_device *devices;
+
+	/* next filesystem in the list */
+	const struct btrfs_info *next;
+};
+
+/*
+ * This function returns the btrfs_info structures list in *ret, doing a scan
+ * if it wasn't already done.
+ *
+ * This function return 0 if everything is OK, or -errno in case of error.
+ */
+int btrfs_info_get(const struct btrfs_info **ret);
+/*
+ * This function returns the btrfs_info structures list, a scan is performed
+ * always.
+ *
+ * This function return 0 if everything is OK, or -errno in case of error.
+ */
+int btrfs_info_refresh(const struct btrfs_info **ret);
+
+/*
+ * This function returns the btrfs_info structure related to a btrfs filesystem
+ * that contains the path 'path'. If path is a device, the information of
+ * the filesystem containing a device is returned.
+ *
+ * This function return 0 if everything is OK, or -errno in case of error.
+ */
+int btrfs_info_find_by_path(const char *path, const struct btrfs_info **ret);
+
+/*
+ * This function returns the btrfs_info structure related to a btrfs filesystem
+ * that matches the UUID 'uuid'
+ *
+ * This function return 0 if everything is OK, or -errno in case of error.
+ */
+int btrfs_info_find_by_uuid(const char *uuid, const struct btrfs_info **ret);
+
+/*
+ * This function returns the btrfs_info structure related to a btrfs filesystem
+ * that matches the label 'label'.
+ *
+ * This function return 0 if everything is OK, or -errno in case of error.
+ */
+int btrfs_info_find_by_label(const char *label, const struct btrfs_info **ret);
+
+/*
+ * This function returns the btrfs_info structure related to a btrfs filesystem
+ * that matches the UUID_SUB 'uuid_sub'
+ * If retdev is not null, it will contains the device path.
+ *
+ * This function return 0 if everything is OK, or -errno in case of error.
+ */
+int btrfs_info_find_by_uuid_sub(const char *uuid_sub,
+				const struct btrfs_info **ret,
+				const char **retdev);
+
+/*
+ * This function returns the btrfs_info structure related to a btrfs filesystem
+ * that matches the block device 'dev'.
+ * If retdev is not null, it will contains the canonical device path.
+ *
+ * This function return 0 if everything is OK, or -errno in case of error.
+ */
+int btrfs_info_find_by_dev(const char *dev, const struct btrfs_info **ret,
+			   const char **retdev);
+
+/*
+ * This function returns the btrfs_info structure related to a btrfs filesystem
+ * that matches the partuuid 'partuuid'.
+ * If retdev is not null, it will contains the device path.
+ *
+ * This function return 0 if everything is OK, or -errno in case of error.
+ */
+int btrfs_info_find_by_partuuid(const char *partuuid,
+				const struct btrfs_info **ret,
+				const char **retdev);
+
+/*
+ * This function returns the btrfs_info structure related to a btrfs filesystem
+ * that matches by UUID_SUB, partuuid or device.
+ * If retdev is not null, it will contains the device path.
+ *
+ * This function return 0 if everything is OK, or -errno in case of error.
+ */
+int btrfs_info_find_dev(const char *filter,
+			const struct btrfs_info **ret,
+			const char **retdev);
+
+/*
+ * This function returns the btrfs_info structure related to a btrfs filesystem
+ * that match:
+ *   - an UUID, if filter is "UUID=<uuid>"
+ *   - or a LABEL, if filter is "LABEL=<label>"
+ *   - or a device, if filter is "<devname>", and <devname> is a block device
+ *   - or otherwise a filesystem which contains the path 'filter' (the filter
+ *     is canonicalized before)
+ *
+ * This function return 0 if everything is OK, or -errno in case of error.
+ */
+int btrfs_info_find(const char *filter, const struct btrfs_info **ret);
+
+/*
+ * return a accessible [*] mountpoint of a btrfs filesystem.
+ * [*] accessible means:
+ *   - the user has the right to access it R/X
+ *   - the mount point is not hided by another mount
+ *
+ * This function return NULL in case of an accessible path doesn't exist
+ */
+const char *btrfs_info_find_valid_path(const struct btrfs_info *bfs);
+
+/*
+ * return a accessible [*] dev of a btrfs filesystem.
+ * [*] accessible means:
+ *   - the user has the right to access it R/X
+ *
+ * This function return NULL in case of an accessible device doesn't exist
+ */
+const char *btrfs_info_find_valid_dev(const struct btrfs_info *bfs);
+
+/*
+ * check if a filesystem is mounted
+ *
+ * This function return 0 if the filesystem is not mounted, otherwise a value
+ * different from 0
+ */
+static inline int btrfs_info_is_mounted(const struct btrfs_info *bfs)
+{
+	return bfs->mounts != NULL;
+}
+
+/*
+ * dump an entry of btrfs info list
+ */
+void btrfs_info_dump_single_entry(const struct btrfs_info *p, FILE *o);
+
+/*
+ * dump a list of btrfs info
+ */
+void btrfs_info_dump(const struct btrfs_info *p, FILE *o);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/4] new command: btrfs filesystem info [<path>...]
  2023-12-06 19:32 [PATCH 0/4] RFC: add the btrfs_info helper Goffredo Baroncelli
  2023-12-06 19:32 ` [PATCH 1/4] btrfs-info: some utility to query a filesystem info Goffredo Baroncelli
@ 2023-12-06 19:32 ` Goffredo Baroncelli
  2023-12-06 19:32 ` [PATCH 3/4] new command: btrfs filesystem get-info Goffredo Baroncelli
  2023-12-06 19:32 ` [PATCH 4/4] btrfs-progs: 'btrfs fi label' using a dev instead of a path Goffredo Baroncelli
  3 siblings, 0 replies; 5+ messages in thread
From: Goffredo Baroncelli @ 2023-12-06 19:32 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba, Goffredo Baroncelli

From: Goffredo Baroncelli <kreijack@inwind.it>

This command shows all the main info of a (or all) btrfs filesystem.
The argument may be in any of the following form:
- btrfs filesystem path
- btrfs device
- UUID=<uuid>
- UUID_sub=<uuid_sub>
- PARTUUID=<partuuid>

If no argument is passed, the informaton of all the filesystem are showed.

Typical output
---------------------------------------------------
$ btrfs fi info /mnt/btrfs-raid1
UUID:    b39b0b27-ff80-4cb4-bf48-0be939ff0788
LABEL:   raid1
devices:
        dev:      /dev/sda2
        UUID_SUB: 65a4f76b-abaa-4d98-a1d5-4c586ffdcbf0
        PARTUUID: fb9c0cc8-02
        major:    8
        minor:    2
        devid:    1

        dev:      /dev/sdb2
        UUID_SUB: 5f7f141f-0bdf-44fb-a910-fdc502a6fa7d
        PARTUUID:
        major:    8
        minor:    18
        devid:    2

mounts:
        dest:     /mnt/btrfs-raid1
        rootpath: /
        subvol:   /
        options:  rw,noatime,nodiratime,rw,space_cache,subvolid=5,subvol=/
        seq:      93
        over:     30

        dest:     /var/log
        rootpath: /@log
        subvol:   /@log
        options:  rw,noatime,nodiratime,rw,nossd,discard=async,space_cache,subvolid=447,subvol=/@log
        seq:      64
        over:     30

-----------------------------------------------------

Signed-off-by: Goffredo Baroncelli <kreijack@libero.it>
---
 cmds/filesystem.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/cmds/filesystem.c b/cmds/filesystem.c
index 1b444b8f..38a0787f 100644
--- a/cmds/filesystem.c
+++ b/cmds/filesystem.c
@@ -55,6 +55,7 @@
 #include "common/parse-utils.h"
 #include "common/filesystem-utils.h"
 #include "common/format-output.h"
+#include "common/btrfs-info.h"
 #include "cmds/commands.h"
 #include "cmds/filesystem-usage.h"
 
@@ -1503,6 +1504,48 @@ static int cmd_filesystem_label(const struct cmd_struct *cmd,
 }
 static DEFINE_SIMPLE_COMMAND(filesystem_label, "label");
 
+static const char * const cmd_filesystem_info_usage[] = {
+	"btrfs filesystem info [UUID=<uuid>|UUID_SUB=<subuuid>|PARTUUID=<partuuid>|LABEL=<label>|<device>|<mount_point>] ...",
+	"Show the information of a btrfs filesystem.",
+	NULL
+};
+
+static int cmd_filesystem_info(const struct cmd_struct *cmd,
+				int argc, char **argv)
+{
+	clean_args_no_options(cmd, argc, argv);
+
+	if (argc - optind == 0) {
+		const struct btrfs_info *bi;
+		int r = btrfs_info_get(&bi);
+		if (r < 0) {
+			error("Cannot get info: %d - %s\n", -r, strerror(-r));
+			return 2;
+		}
+		btrfs_info_dump(bi, stdout);
+	} else {
+		for (int i = 1 ; i < argc ; i++) {
+			printf("Target: %s\n", argv[i]);
+
+			const struct btrfs_info *bi;
+			int r = btrfs_info_find(argv[i], &bi);
+			if (r < 0) {
+				error("Cannot get info for '%s': %d - %s\n",
+				      argv[i], -r, strerror(-r));
+				return 2;
+			}
+
+			btrfs_info_dump_single_entry(bi, stdout);
+
+			if (i < argc - 1)
+				printf("\n");
+		}
+	}
+
+	return 0;
+}
+static DEFINE_SIMPLE_COMMAND(filesystem_info, "info");
+
 static const char * const cmd_filesystem_balance_usage[] = {
 	"btrfs filesystem balance [args...] (alias of \"btrfs balance\")",
 	"Please see \"btrfs balance --help\" for more information.",
@@ -1707,6 +1750,7 @@ static const struct cmd_group filesystem_cmd_group = {
 		&cmd_struct_filesystem_balance,
 		&cmd_struct_filesystem_resize,
 		&cmd_struct_filesystem_label,
+		&cmd_struct_filesystem_info,
 		&cmd_struct_filesystem_usage,
 		&cmd_struct_filesystem_mkswapfile,
 		NULL
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/4] new command: btrfs filesystem get-info ...
  2023-12-06 19:32 [PATCH 0/4] RFC: add the btrfs_info helper Goffredo Baroncelli
  2023-12-06 19:32 ` [PATCH 1/4] btrfs-info: some utility to query a filesystem info Goffredo Baroncelli
  2023-12-06 19:32 ` [PATCH 2/4] new command: btrfs filesystem info [<path>...] Goffredo Baroncelli
@ 2023-12-06 19:32 ` Goffredo Baroncelli
  2023-12-06 19:32 ` [PATCH 4/4] btrfs-progs: 'btrfs fi label' using a dev instead of a path Goffredo Baroncelli
  3 siblings, 0 replies; 5+ messages in thread
From: Goffredo Baroncelli @ 2023-12-06 19:32 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba, Goffredo Baroncelli

From: Goffredo Baroncelli <kreijack@inwind.it>

usage: btrfs filesystem get-info <options> <filesystemspec>

This new command returns specific info of a btrfs filesystem.

<filesystemspec> is in the form of:

- UUID=<uuid>
- UUID_SUB=<subuuid>
- PARTUUID=<partuuid>
- LABEL=<label>
- btrfs <device>
- a valid btrfs path

and <option> is one of the following:
    -d    show one accessible device of the filesystem passed as argument.
    -D    show all the devices of the filesystem passed as argument.
    -l    show the label of the filesystem passed as argument.
    -m    show one accessible mountpoint of the filesystem passed as argument.
    -M    show all the mountpoints of the filesystem passed as argument.
    -r    show the rootpath name of the argument (which must be a path).
    -s    show the subvolume name of the argument (which must be a path).
    -U    show the UUID of the filesystem passed as argument.
    --is-mounted    the 0 exit code is returned if the filesystem passed
                    as argument is mounted.

The intended use is as helper in the script.

Typical usage:
-----------------------------
$ ./btrfs fi get-info -D /
/dev/sdd3
-----------------------------

Signed-off-by: Goffredo Baroncelli <kreijack@libero.it>
---
 cmds/filesystem.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/cmds/filesystem.c b/cmds/filesystem.c
index 38a0787f..a6c29bf3 100644
--- a/cmds/filesystem.c
+++ b/cmds/filesystem.c
@@ -1546,6 +1546,102 @@ static int cmd_filesystem_info(const struct cmd_struct *cmd,
 }
 static DEFINE_SIMPLE_COMMAND(filesystem_info, "info");
 
+static const char * const cmd_filesystem_get_info_usage[] = {
+	"btrfs filesystem get-info -m|-M|-d|-s|-r|-D|-U|-l|--is-mounted UUID=<uuid>|UUID_SUB=<subuuid>|PARTUUID=<partuuid>|LABEL=<label>|<device>|<mount_point>",
+	"Get some information of a btrfs filesystem.",
+	"One of the following option must be passed:",
+	"-d    show one accessible device of the filesystem passed as argument.",
+	"-D    show all the devices of the filesystem passed as argument.",
+	"-l    show the label of the filesystem passed as argument.",
+	"-m    show one accessible mountpoint of the filesystem passed as argument.",
+	"-M    show all the mountpoints of the filesystem passed as argument.",
+	"-r    show the rootpath name of the argument (which must be a path).",
+	"-s    show the subvolume name of the argument (which must be a path).",
+	"-U    show the UUID of the filesystem passed as argument.",
+	"--is-mounted    the 0 exit code is returned if the filesystem passed",
+	"                as argument is mounted.",
+	NULL
+};
+
+static int cmd_filesystem_get_info(const struct cmd_struct *cmd,
+				int argc, char **argv)
+{
+	if (check_argc_min(argc - optind, 2) || check_argc_max(argc - optind, 2))
+		return 1;
+
+	const struct btrfs_info *bi;
+	int r = btrfs_info_find(argv[2], &bi);
+	if (r < 0) {
+		error("Cannot get info for '%s': %d - %s\n", argv[2],
+		      -r, strerror(-r));
+		return 2;
+	}
+
+	if (!strcmp(argv[1], "-m")) {
+		const char *p = btrfs_info_find_valid_path(bi);
+		if (!p)
+			return 100;
+		printf("%s\n", p);
+	} else if (!strcmp(argv[1], "-d")) {
+		const char *p = btrfs_info_find_valid_dev(bi);
+		if (!p)
+			return 100;
+		printf("%s\n", p);
+	} else if (!strcmp(argv[1], "-s") || !strcmp(argv[1], "-r")) {
+		const struct btrfs_info_mount *bm, *res = NULL;
+		const char *winner = NULL;
+		int winner_length = 0;
+		char *path = realpath(argv[2], NULL);
+		if (!path) {
+			fprintf(stderr, "ERROR: in realpath(%s)\n", argv[2]);
+			return -EINVAL;
+		}
+		int lpath = strlen(path);
+		for (bm = bi->mounts; bm; bm = bm->next) {
+			int l = strlen(bm->dest);
+			if (l > lpath)
+				continue;
+			if (strncmp(bm->dest, path, lpath))
+				continue;
+			if (!winner || l > winner_length) {
+				winner = bm->dest;
+				winner_length = l;
+				res = bm;
+			}
+		}
+		free(path);
+		if (!res)
+			return 100;
+		if (!strcmp(argv[1], "-s"))
+			printf("%s\n", res->subvol);
+		else /* -r */
+			printf("%s\n", res->rootpath);
+	} else if (!strcmp(argv[1], "-M")) {
+		const struct btrfs_info_mount *bm = bi->mounts;
+		while (bm) {
+			printf("%s\n", bm->dest);
+			bm = bm->next;
+		}
+	} else if (!strcmp(argv[1], "-D")) {
+		const struct btrfs_info_device *bd = bi->devices;
+		while (bd) {
+			printf("%s\n", bd->name);
+			bd = bd->next;
+		}
+	} else if (!strcmp(argv[1], "-U")) {
+		printf("%s\n", bi->uuid);
+	} else if (!strcmp(argv[1], "-l")) {
+		printf("%s\n", bi->label);
+	} else if (!strcmp(argv[1], "--is-mounted")) {
+		return btrfs_info_is_mounted(bi) ? 0 : 100;
+	} else {
+		error("Unknown parameter '%s'", argv[1]);
+	}
+
+	return 0;
+}
+static DEFINE_SIMPLE_COMMAND(filesystem_get_info, "get-info");
+
 static const char * const cmd_filesystem_balance_usage[] = {
 	"btrfs filesystem balance [args...] (alias of \"btrfs balance\")",
 	"Please see \"btrfs balance --help\" for more information.",
@@ -1751,6 +1847,7 @@ static const struct cmd_group filesystem_cmd_group = {
 		&cmd_struct_filesystem_resize,
 		&cmd_struct_filesystem_label,
 		&cmd_struct_filesystem_info,
+		&cmd_struct_filesystem_get_info,
 		&cmd_struct_filesystem_usage,
 		&cmd_struct_filesystem_mkswapfile,
 		NULL
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 4/4] btrfs-progs: 'btrfs fi label' using a dev instead of a path
  2023-12-06 19:32 [PATCH 0/4] RFC: add the btrfs_info helper Goffredo Baroncelli
                   ` (2 preceding siblings ...)
  2023-12-06 19:32 ` [PATCH 3/4] new command: btrfs filesystem get-info Goffredo Baroncelli
@ 2023-12-06 19:32 ` Goffredo Baroncelli
  3 siblings, 0 replies; 5+ messages in thread
From: Goffredo Baroncelli @ 2023-12-06 19:32 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba, Goffredo Baroncelli

From: Goffredo Baroncelli <kreijack@inwind.it>

'btrfs fi label' has a strange behaviour: when the filesystem
is unmounted, it has to be invoked against a device.
When the filesystem is mounted, it has to be invoked against
a filesystem path.

Even tough it has a technical explanation, this may confuses
the users.

This patch allow to pass a device even in the latter cases.
Alternatevely, it is possible to pass a UUID=xxx or a
LABEL=zzz identification.

Signed-off-by: Goffredo Baroncelli <kreijack@libero.it>
---
 common/filesystem-utils.c | 51 ++++++++++++++++++++++++++++++---------
 1 file changed, 39 insertions(+), 12 deletions(-)

diff --git a/common/filesystem-utils.c b/common/filesystem-utils.c
index 8c159365..655f4c72 100644
--- a/common/filesystem-utils.c
+++ b/common/filesystem-utils.c
@@ -29,6 +29,7 @@
 #include "common/messages.h"
 #include "common/open-utils.h"
 #include "common/path-utils.h"
+#include "common/btrfs-info.h"
 
 /*
  * For a given:
@@ -189,31 +190,57 @@ int get_label_mounted(const char *mount_path, char *labelp)
 	return 0;
 }
 
-int get_label(const char *btrfs_dev, char *label)
+int get_label(const char *path, char *label)
 {
 	int ret;
+	const struct btrfs_info *bi;
 
-	ret = path_is_reg_or_block_device(btrfs_dev);
-	if (!ret)
-		ret = get_label_mounted(btrfs_dev, label);
-	else if (ret > 0)
-		ret = get_label_unmounted(btrfs_dev, label);
+	ret = btrfs_info_find(path, &bi);
+	if (ret == -ENOENT) {
+		error("'%s' is not a btrfs filesystem", path);
+		return -ENOENT;
+	}
+	if (ret < 0) {
+		error("unable to access btrfs filesystem info");
+		return -EINVAL;
+	}
+
+	if (btrfs_info_is_mounted(bi)) {
+		path = btrfs_info_find_valid_path(bi);
+		ret = get_label_mounted(path, label);
+	} else {
+		path = btrfs_info_find_valid_dev(bi);
+		ret = get_label_unmounted(path, label);
+	}
 
 	return ret;
 }
 
-int set_label(const char *btrfs_dev, const char *label)
+int set_label(const char *path, const char *label)
 {
 	int ret;
+	const struct btrfs_info *bi;
 
 	if (check_label(label))
 		return -1;
 
-	ret = path_is_reg_or_block_device(btrfs_dev);
-	if (!ret)
-		ret = set_label_mounted(btrfs_dev, label);
-	else if (ret > 0)
-		ret = set_label_unmounted(btrfs_dev, label);
+	ret = btrfs_info_find(path, &bi);
+	if (ret == -ENOENT) {
+		error("'%s' is not a btrfs filesystem", path);
+		return -ENOENT;
+	}
+	if (ret < 0) {
+		error("unable to access btrfs filesystem info");
+		return -EINVAL;
+	}
+
+	if (btrfs_info_is_mounted(bi)) {
+		path = btrfs_info_find_valid_path(bi);
+		ret = set_label_mounted(path, label);
+	} else {
+		path = btrfs_info_find_valid_dev(bi);
+		ret = set_label_unmounted(path, label);
+	}
 
 	return ret;
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-12-06 19:52 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-12-06 19:32 [PATCH 0/4] RFC: add the btrfs_info helper Goffredo Baroncelli
2023-12-06 19:32 ` [PATCH 1/4] btrfs-info: some utility to query a filesystem info Goffredo Baroncelli
2023-12-06 19:32 ` [PATCH 2/4] new command: btrfs filesystem info [<path>...] Goffredo Baroncelli
2023-12-06 19:32 ` [PATCH 3/4] new command: btrfs filesystem get-info Goffredo Baroncelli
2023-12-06 19:32 ` [PATCH 4/4] btrfs-progs: 'btrfs fi label' using a dev instead of a path Goffredo Baroncelli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox