summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--utils.c48
1 files changed, 44 insertions, 4 deletions
diff --git a/utils.c b/utils.c
index ca8efca2..7b977ee1 100644
--- a/utils.c
+++ b/utils.c
@@ -1708,6 +1708,25 @@ scan_again:
}
/*
+ * Unsafe subvolume check.
+ *
+ * This only checks ino == BTRFS_FIRST_FREE_OBJECTID, even it is not in a
+ * btrfs mount point.
+ * Must use together with other reliable method like btrfs ioctl.
+ */
+static int __is_subvol(const char *path)
+{
+ struct stat st;
+ int ret;
+
+ ret = lstat(path, &st);
+ if (ret < 0)
+ return ret;
+
+ return st.st_ino == BTRFS_FIRST_FREE_OBJECTID;
+}
+
+/*
* A not-so-good version fls64. No fascinating optimization since
* no one except parse_size use it
*/
@@ -1802,24 +1821,45 @@ u64 parse_qgroupid(const char *p)
char *ptr_parse_end = NULL;
u64 level;
u64 id;
+ int fd;
+ int ret = 0;
+
+ if (p[0] == '/')
+ goto path;
+ /* Numeric format like '0/257' is the primary case */
if (!s) {
id = strtoull(p, &ptr_parse_end, 10);
if (ptr_parse_end != ptr_src_end)
- goto err;
+ goto path;
return id;
}
level = strtoull(p, &ptr_parse_end, 10);
if (ptr_parse_end != s)
- goto err;
+ goto path;
id = strtoull(s + 1, &ptr_parse_end, 10);
if (ptr_parse_end != ptr_src_end)
- goto err;
+ goto path;
return (level << BTRFS_QGROUP_LEVEL_SHIFT) | id;
+
+path:
+ /* Path format like subv at 'my_subvol' is the fallback case */
+ ret = __is_subvol(p);
+ if (ret < 0 || !ret)
+ goto err;
+ fd = open(p, O_RDONLY);
+ if (fd < 0)
+ goto err;
+ ret = lookup_ino_rootid(fd, &id);
+ close(fd);
+ if (ret < 0)
+ goto err;
+ return id;
+
err:
- fprintf(stderr, "ERROR: invalid qgroupid %s\n", p);
+ fprintf(stderr, "ERROR: invalid qgroupid or subvolume path: %s\n", p);
exit(-1);
}