* [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