diff mbox series

[3/3] Read/change the allocation_hint prop when unmounted

Message ID 1c6a98da2f8c4a7400f0a7e8862f697b9e8993a1.1643313144.git.kreijack@inwind.it (mailing list archive)
State New, archived
Headers show
Series btrfs-progs: get/set allocation_hint for an unmounted filesystem | expand

Commit Message

Goffredo Baroncelli Jan. 27, 2022, 7:57 p.m. UTC
From: Goffredo Baroncelli <kreijack@inwind.it>

This patch enable the changing of the allocation_hint even
when the filesystem is unmounted.

Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it>
---
 cmds/property.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)
diff mbox series

Patch

diff --git a/cmds/property.c b/cmds/property.c
index a409f4e9..55de7e6b 100644
--- a/cmds/property.c
+++ b/cmds/property.c
@@ -28,6 +28,9 @@ 
 #include "cmds/commands.h"
 #include "cmds/props.h"
 #include "kernel-shared/ctree.h"
+#include "kernel-shared/volumes.h"
+#include "kernel-shared/disk-io.h"
+#include "kernel-shared/transaction.h"
 #include "common/open-utils.h"
 #include "common/utils.h"
 #include "common/help.h"
@@ -377,6 +380,115 @@  static struct ull_charp_pair_t {
 	{0, NULL}
 };
 
+static int find_device(const char *object, struct btrfs_device **device_ret)
+{
+	struct btrfs_fs_devices *fs_devices;
+	struct list_head *fs_uuids;
+	struct stat sttarget, st;
+
+	if (stat(object, &sttarget) < 0)
+		return -EACCES;
+
+	fs_uuids = btrfs_scanned_uuids();
+
+	list_for_each_entry(fs_devices, fs_uuids, list) {
+		struct btrfs_device *device;
+
+		list_for_each_entry(device, &fs_devices->devices, dev_list) {
+
+			if (stat(device->name, &st) < 0)
+				return -EACCES;
+
+			if (st.st_rdev == sttarget.st_rdev) {
+				*device_ret = device;
+				return 0;
+			}
+		}
+	}
+
+	return -ENODEV;
+}
+
+static int prop_allocation_hint_unmounted(const char *object,
+					  const char *name,
+					  const char *val)
+{
+
+	struct btrfs_device *device;
+	int ret;
+	struct btrfs_root *root = NULL;
+
+	root = open_ctree(object, btrfs_sb_offset(0), 0);
+	if (!root)
+		return -ENODEV;
+
+	ret = find_device(object, &device);
+	if (ret)
+		goto out;
+
+	if (!val) {
+		int i;
+		u64 v = device->flags & BTRFS_DEV_ALLOCATION_HINT_MASK;
+
+		for (i = 0 ; allocation_hint_description[i].descr ; i++)
+			if (v == allocation_hint_description[i].value)
+				break;
+
+		if (allocation_hint_description[i].descr)
+			printf("allocation_hint=%s\n",
+				allocation_hint_description[i].descr);
+		else
+			printf("allocation_hint=unknown:%llu\n", v);
+		ret = 0;
+	} else {
+		struct btrfs_trans_handle *trans;
+		int i;
+		u64 v;
+
+		for (i = 0 ; allocation_hint_description[i].descr ; i++)
+			if (!strcmp(val, allocation_hint_description[i].descr))
+				break;
+
+		if (allocation_hint_description[i].descr) {
+			v = allocation_hint_description[i].value;
+		} else if (sscanf(val, "%llu", &v) != 1) {
+			error("Invalid value '%s'\n", val);
+			ret = -3;
+			goto out;
+		}
+		if (v & ~BTRFS_DEV_ALLOCATION_HINT_MASK) {
+			error("Invalid value '%s'\n", val);
+			ret = -3;
+			goto out;
+		}
+
+		trans = btrfs_start_transaction(device->fs_info->chunk_root, 1);
+		if (IS_ERR(trans)) {
+			ret = PTR_ERR(trans);
+			goto out;
+		}
+
+		/* Manually update the device item in chunk tree */
+		ret = btrfs_update_device(trans, device);
+		if (ret < 0) {
+			errno = -ret;
+			goto out;
+		}
+
+		/*
+		 * Commit transaction not only to save the above change but also update
+		 * the device item in super block.
+		 */
+		ret = btrfs_commit_transaction(trans, device->fs_info->chunk_root);
+
+		}
+out:
+	if (root)
+		close_ctree(root);
+	return ret;
+
+}
+
 static int prop_allocation_hint(enum prop_object_type type,
 				const char *object,
 				const char *name,
@@ -394,6 +506,9 @@  static int prop_allocation_hint(enum prop_object_type type,
 	ret = btrfs_find_devid_uuid_by_dev(object, &devid,
 		sysfs_file + sizeof(BTRFSYSFS) - 1);
 
+	if (ret == -ENODEV)
+		return prop_allocation_hint_unmounted(object, name, val);
+
 	if (ret)
 		goto out;