From f46437f06e8f3b67c81c5e1648a62279aed5f525 Mon Sep 17 00:00:00 2001
From: Kent Overstreet <kent.overstreet@gmail.com>
Date: Fri, 2 Apr 2021 23:20:00 -0400
Subject: [PATCH] bcaachefs device set-state can now work by id

---
 cmd_device.c  | 108 +++++++++++++++++++++++++++++---------------------
 libbcachefs.c |  13 +++++-
 libbcachefs.h |   3 +-
 3 files changed, 77 insertions(+), 47 deletions(-)

diff --git a/cmd_device.c b/cmd_device.c
index 161e6692..f9e975ab 100644
--- a/cmd_device.c
+++ b/cmd_device.c
@@ -123,11 +123,9 @@ static void device_remove_usage(void)
 {
 	puts("bcachefs device_remove - remove a device from a filesystem\n"
 	     "Usage:\n"
-	     "  bcachefs device remove device\n"
-	     "  bcachefs device remove --by-id path devid\n"
+	     "  bcachefs device remove <device>|<devid> <path>\n"
 	     "\n"
 	     "Options:\n"
-	     "  -i, --by-id                 Remove device by device id\n"
 	     "  -f, --force		    Force removal, even if some data\n"
 	     "                              couldn't be migrated\n"
 	     "  -F, --force-metadata	    Force removal, even if some metadata\n"
@@ -148,14 +146,10 @@ int cmd_device_remove(int argc, char *argv[])
 	};
 	struct bchfs_handle fs;
 	bool by_id = false;
-	int opt, flags = BCH_FORCE_IF_DEGRADED;
-	unsigned dev_idx;
+	int opt, flags = BCH_FORCE_IF_DEGRADED, dev_idx;
 
 	while ((opt = getopt_long(argc, argv, "fh", longopts, NULL)) != -1)
 		switch (opt) {
-		case 'i':
-			by_id = true;
-			break;
 		case 'f':
 			flags |= BCH_FORCE_IF_DATA_LOST;
 			break;
@@ -167,32 +161,31 @@ int cmd_device_remove(int argc, char *argv[])
 		}
 	args_shift(optind);
 
-	if (by_id) {
-		char *path = arg_pop();
-		if (!path)
-			die("Please supply filesystem to remove device from");
+	char *dev_str = arg_pop();
+	if (!dev_str)
+		die("Please supply a device");
 
-		char *dev_str = arg_pop();
-		if (!dev_str)
-			die("Please supply device id");
+	char *end;
+	dev_idx = strtoul(dev_str, &end, 10);
+	if (*dev_str && !*end)
+		by_id = true;
 
-		errno = 0;
-		dev_idx = strtoul(dev_str, NULL, 10);
-		if (errno)
-			die("Error parsing device id: %m");
+	char *fs_path = arg_pop();
+	if (fs_path) {
+		fs = bcache_fs_open(fs_path);
 
-		fs = bcache_fs_open(path);
+		if (!by_id) {
+			dev_idx = bchu_dev_path_to_idx(fs, dev_str);
+			if (dev_idx < 0)
+				die("%s does not seem to be a member of %s",
+				    dev_str, fs_path);
+		}
+	} else if (!by_id) {
+		fs = bchu_fs_open_by_dev(dev_str, &dev_idx);
 	} else {
-		char *dev = arg_pop();
-		if (!dev)
-			die("Please supply a device to remove");
-
-		fs = bchu_fs_open_by_dev(dev, &dev_idx);
+		die("Filesystem path required when specifying device by id");
 	}
 
-	if (argc)
-		die("too many arguments");
-
 	bchu_disk_remove(fs, dev_idx, flags);
 	return 0;
 }
@@ -227,7 +220,7 @@ int cmd_device_online(int argc, char *argv[])
 	if (argc)
 		die("too many arguments");
 
-	unsigned dev_idx;
+	int dev_idx;
 	struct bchfs_handle fs = bchu_fs_open_by_dev(dev, &dev_idx);
 	bchu_disk_online(fs, dev);
 	return 0;
@@ -272,7 +265,7 @@ int cmd_device_offline(int argc, char *argv[])
 	if (argc)
 		die("too many arguments");
 
-	unsigned dev_idx;
+	int dev_idx;
 	struct bchfs_handle fs = bchu_fs_open_by_dev(dev, &dev_idx);
 	bchu_disk_offline(fs, dev_idx, flags);
 	return 0;
@@ -308,7 +301,7 @@ int cmd_device_evacuate(int argc, char *argv[])
 	if (argc)
 		die("too many arguments");
 
-	unsigned dev_idx;
+	int dev_idx;
 	struct bchfs_handle fs = bchu_fs_open_by_dev(dev_path, &dev_idx);
 
 	struct bch_ioctl_dev_usage u = bchu_dev_usage(fs, dev_idx);
@@ -331,7 +324,10 @@ int cmd_device_evacuate(int argc, char *argv[])
 static void device_set_state_usage(void)
 {
 	puts("bcachefs device set-state\n"
-	     "Usage: bcachefs device set-state device new-state\n"
+	     "Usage: bcachefs device set-state <new-state> <device>|<devid> <path>\n"
+	     "\n"
+	     "<new-state>: one of rw, ro, failed or spare\n"
+	     "<path>: path to mounted filesystem, optional unless specifying device by id\n"
 	     "\n"
 	     "Options:\n"
 	     "  -f, --force		    Force, if data redundancy will be degraded\n"
@@ -349,7 +345,9 @@ int cmd_device_set_state(int argc, char *argv[])
 		{ "help",			0, NULL, 'h' },
 		{ NULL }
 	};
-	int opt, flags = 0;
+	struct bchfs_handle fs;
+	bool by_id = false;
+	int opt, flags = 0, dev_idx;
 	bool offline = false;
 
 	while ((opt = getopt_long(argc, argv, "foh", longopts, NULL)) != -1)
@@ -365,10 +363,6 @@ int cmd_device_set_state(int argc, char *argv[])
 		}
 	args_shift(optind);
 
-	char *dev_path = arg_pop();
-	if (!dev_path)
-		die("Please supply a device");
-
 	char *new_state_str = arg_pop();
 	if (!new_state_str)
 		die("Please supply a device state");
@@ -376,20 +370,25 @@ int cmd_device_set_state(int argc, char *argv[])
 	unsigned new_state = read_string_list_or_die(new_state_str,
 					bch2_member_states, "device state");
 
-	if (!offline) {
-		unsigned dev_idx;
-		struct bchfs_handle fs = bchu_fs_open_by_dev(dev_path, &dev_idx);
+	char *dev_str = arg_pop();
+	if (!dev_str)
+		die("Please supply a device");
 
-		bchu_disk_set_state(fs, dev_idx, new_state, flags);
+	char *end;
+	dev_idx = strtoul(dev_str, &end, 10);
+	if (*dev_str && !*end)
+		by_id = true;
 
-		bcache_fs_close(fs);
-	} else {
+	if (offline) {
 		struct bch_opts opts = bch2_opts_empty();
 		struct bch_sb_handle sb = { NULL };
 
-		int ret = bch2_read_super(dev_path, &opts, &sb);
+		if (by_id)
+			die("Cannot specify offline device by id");
+
+		int ret = bch2_read_super(dev_str, &opts, &sb);
 		if (ret)
-			die("error opening %s: %s", dev_path, strerror(-ret));
+			die("error opening %s: %s", dev_str, strerror(-ret));
 
 		struct bch_member *m = bch2_sb_get_members(sb.sb)->members + sb.sb->dev_idx;
 
@@ -399,8 +398,27 @@ int cmd_device_set_state(int argc, char *argv[])
 
 		bch2_super_write(sb.bdev->bd_fd, sb.sb);
 		bch2_free_super(&sb);
+		return 0;
 	}
 
+	char *fs_path = arg_pop();
+	if (fs_path) {
+		fs = bcache_fs_open(fs_path);
+
+		if (!by_id) {
+			dev_idx = bchu_dev_path_to_idx(fs, dev_str);
+			if (dev_idx < 0)
+				die("%s does not seem to be a member of %s",
+				    dev_str, fs_path);
+		}
+	} else if (!by_id) {
+		fs = bchu_fs_open_by_dev(dev_str, &dev_idx);
+	} else {
+		die("Filesystem path required when specifying device by id");
+	}
+
+	bchu_disk_set_state(fs, dev_idx, new_state, flags);
+
 	return 0;
 }
 
diff --git a/libbcachefs.c b/libbcachefs.c
index 1ae9b9ab..e917c6ca 100644
--- a/libbcachefs.c
+++ b/libbcachefs.c
@@ -900,7 +900,7 @@ struct bchfs_handle bcache_fs_open(const char *path)
  * Given a path to a block device, open the filesystem it belongs to; also
  * return the device's idx:
  */
-struct bchfs_handle bchu_fs_open_by_dev(const char *path, unsigned *idx)
+struct bchfs_handle bchu_fs_open_by_dev(const char *path, int *idx)
 {
 	char buf[1024], *uuid_str;
 
@@ -944,6 +944,17 @@ struct bchfs_handle bchu_fs_open_by_dev(const char *path, unsigned *idx)
 	return bcache_fs_open(uuid_str);
 }
 
+int bchu_dev_path_to_idx(struct bchfs_handle fs, const char *dev_path)
+{
+	int idx;
+	struct bchfs_handle fs2 = bchu_fs_open_by_dev(dev_path, &idx);
+
+	if (memcmp(&fs.uuid, &fs2.uuid, sizeof(fs.uuid)))
+		idx = -1;
+	bcache_fs_close(fs2);
+	return idx;
+}
+
 int bchu_data(struct bchfs_handle fs, struct bch_ioctl_data cmd)
 {
 	int progress_fd = xioctl(fs.ioctl_fd, BCH_IOCTL_DATA, &cmd);
diff --git a/libbcachefs.h b/libbcachefs.h
index 59c62df3..45d2f874 100644
--- a/libbcachefs.h
+++ b/libbcachefs.h
@@ -94,7 +94,8 @@ struct bchfs_handle {
 
 void bcache_fs_close(struct bchfs_handle);
 struct bchfs_handle bcache_fs_open(const char *);
-struct bchfs_handle bchu_fs_open_by_dev(const char *, unsigned *);
+struct bchfs_handle bchu_fs_open_by_dev(const char *, int *);
+int bchu_dev_path_to_idx(struct bchfs_handle, const char *);
 
 static inline void bchu_disk_add(struct bchfs_handle fs, char *dev)
 {