diff --git a/c_src/bcachefs.c b/c_src/bcachefs.c
index f488436e..aed9c261 100644
--- a/c_src/bcachefs.c
+++ b/c_src/bcachefs.c
@@ -33,6 +33,7 @@ void bcachefs_usage(void)
 	     "Superblock commands:\n"
 	     "  format                   Format a new filesystem\n"
 	     "  show-super               Dump superblock information to stdout\n"
+	     "  recover-super            Attempt to recover overwritten superblock from backups\n"
 	     "  set-fs-option            Set a filesystem option\n"
 	     "  reset-counters           Reset all counters on an unmounted device\n"
 	     "\n"
diff --git a/c_src/cmd_format.c b/c_src/cmd_format.c
index 730a544e..b103d2bd 100644
--- a/c_src/cmd_format.c
+++ b/c_src/cmd_format.c
@@ -433,3 +433,204 @@ int cmd_show_super(int argc, char *argv[])
 	printbuf_exit(&buf);
 	return 0;
 }
+
+#include "libbcachefs/super-io.h"
+#include "libbcachefs/sb-members.h"
+
+typedef DARRAY(struct bch_sb *) probed_sb_list;
+
+static void probe_one_super(int dev_fd, unsigned sb_size, u64 offset,
+			    probed_sb_list *sbs, bool verbose)
+{
+	darray_char sb_buf = {};
+	darray_resize(&sb_buf, sb_size);
+
+	xpread(dev_fd, sb_buf.data, sb_buf.size, offset);
+
+	struct printbuf err = PRINTBUF;
+	int ret = bch2_sb_validate((void *) sb_buf.data, offset >> 9, 0, &err);
+	printbuf_exit(&err);
+
+	if (!ret) {
+		if (verbose) {
+			struct printbuf buf = PRINTBUF;
+			prt_human_readable_u64(&buf, offset);
+			printf("found superblock at %s\n", buf.buf);
+			printbuf_exit(&buf);
+		}
+
+		darray_push(sbs, (void *) sb_buf.data);
+		sb_buf.data = NULL;
+	}
+
+	darray_exit(&sb_buf);
+}
+
+static void probe_sb_range(int dev_fd, u64 start_offset, u64 end_offset,
+			   probed_sb_list *sbs, bool verbose)
+{
+	start_offset	&= ~((u64) 511);
+	end_offset	&= ~((u64) 511);
+
+	size_t buflen = end_offset - start_offset;
+	void *buf = malloc(buflen);
+	xpread(dev_fd, buf, buflen, start_offset);
+
+	for (u64 offset = 0; offset < buflen; offset += 512) {
+		struct bch_sb *sb = buf + offset;
+
+		if (!uuid_equal(&sb->magic, &BCACHE_MAGIC) &&
+		    !uuid_equal(&sb->magic, &BCHFS_MAGIC))
+			continue;
+
+		size_t bytes = vstruct_bytes(sb);
+		if (offset + bytes > buflen) {
+			fprintf(stderr, "found sb %llu size %zu that overran buffer\n",
+				start_offset + offset, bytes);
+			continue;
+		}
+		struct printbuf err = PRINTBUF;
+		int ret = bch2_sb_validate(sb, (start_offset + offset) >> 9, 0, &err);
+		if (ret)
+			fprintf(stderr, "found sb %llu that failed to validate: %s\n",
+				start_offset + offset, err.buf);
+		printbuf_exit(&err);
+
+		if (ret)
+			continue;
+
+		if (verbose) {
+			struct printbuf buf = PRINTBUF;
+			prt_human_readable_u64(&buf, start_offset + offset);
+			printf("found superblock at %s\n", buf.buf);
+			printbuf_exit(&buf);
+		}
+
+		void *sb_copy = malloc(bytes);
+		memcpy(sb_copy, sb, bytes);
+		darray_push(sbs, sb_copy);
+	}
+
+	free(buf);
+}
+
+static u64 bch2_sb_last_mount_time(struct bch_sb *sb)
+{
+	u64 ret = 0;
+	for (unsigned i = 0; i < sb->nr_devices; i++)
+		ret = max(ret, le64_to_cpu(bch2_sb_member_get(sb, i).last_mount));
+	return ret;
+}
+
+static int bch2_sb_time_cmp(struct bch_sb *l, struct bch_sb *r)
+{
+	return cmp_int(bch2_sb_last_mount_time(l),
+		       bch2_sb_last_mount_time(r));
+}
+
+static void recover_super_usage(void)
+{
+	puts("bcachefs recover-super \n"
+	     "Usage: bcachefs recover-super [OPTION].. device\n"
+	     "\n"
+	     "Attempt to recover a filesystem on a device that has had the main superblock\n"
+	     "and superblock layout overwritten.\n"
+	     "All options will be guessed if not provided\n"
+	     "\n"
+	     "Options:\n"
+	     "  -d, --dev_size              size of filessytem on device, in bytes \n"
+	     "  -o, --offset                offset to probe, in bytes\n"
+	     "  -y, --yes                   Recover without prompting\n"
+	     "  -v, --verbose               Increase logging level\n"
+	     "  -h, --help                  display this help and exit\n"
+	     "Report bugs to <linux-bcachefs@vger.kernel.org>");
+	exit(EXIT_SUCCESS);
+}
+
+int cmd_recover_super(int argc, char *argv[])
+{
+	static const struct option longopts[] = {
+		{ "dev_size",			1, NULL, 'd' },
+		{ "offset",			1, NULL, 'o' },
+		{ "yes",			0, NULL, 'y' },
+		{ "verbose",			0, NULL, 'v' },
+		{ "help",			0, NULL, 'h' },
+		{ NULL }
+	};
+	u64 dev_size = 0, offset = 0;
+	bool yes = false, verbose = false;
+	int opt;
+
+	while ((opt = getopt_long(argc, argv, "d:o:yvh", longopts, NULL)) != -1)
+		switch (opt) {
+		case 'd':
+			if (bch2_strtoull_h(optarg, &dev_size))
+				die("invalid offset");
+			break;
+		case 'o':
+			if (bch2_strtoull_h(optarg, &offset))
+				die("invalid offset");
+
+			if (offset & 511)
+				die("offset must be a multiple of 512");
+			break;
+		case 'y':
+			yes = true;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		case 'h':
+			recover_super_usage();
+			break;
+		}
+	args_shift(optind);
+
+	char *dev_path = arg_pop();
+	if (!dev_path)
+		die("please supply a device");
+	if (argc)
+		die("too many arguments");
+
+	int dev_fd = xopen(dev_path, O_RDWR);
+
+	if (!dev_size)
+		dev_size = get_size(dev_fd);
+
+	probed_sb_list sbs = {};
+
+	if (offset) {
+		probe_one_super(dev_fd, SUPERBLOCK_SIZE_DEFAULT, offset, &sbs, verbose);
+	} else {
+		unsigned scan_len = 16 << 20; /* 16MB, start and end of device */
+
+		probe_sb_range(dev_fd, 4096, scan_len, &sbs, verbose);
+		probe_sb_range(dev_fd, dev_size - scan_len, dev_size, &sbs, verbose);
+	}
+
+	if (!sbs.nr) {
+		printf("Found no bcachefs superblocks\n");
+		exit(EXIT_FAILURE);
+	}
+
+	struct bch_sb *best = NULL;
+	darray_for_each(sbs, sb)
+		if (!best || bch2_sb_time_cmp(best, *sb) < 0)
+			best = *sb;
+
+	struct printbuf buf = PRINTBUF;
+	bch2_sb_to_text(&buf, best, true, BIT_ULL(BCH_SB_FIELD_members_v2));
+
+	printf("Found superblock:\n%s", buf.buf);
+	printf("Recover?");
+
+	if (yes || ask_yn())
+		bch2_super_write(dev_fd, best);
+
+	printbuf_exit(&buf);
+	darray_for_each(sbs, sb)
+		kfree(*sb);
+	darray_exit(&sbs);
+
+	return 0;
+}
diff --git a/c_src/cmds.h b/c_src/cmds.h
index 9de87a18..9502d539 100644
--- a/c_src/cmds.h
+++ b/c_src/cmds.h
@@ -11,6 +11,7 @@
 
 int cmd_format(int argc, char *argv[]);
 int cmd_show_super(int argc, char *argv[]);
+int cmd_recover_super(int argc, char *argv[]);
 int cmd_reset_counters(int argc, char *argv[]);
 int cmd_set_option(int argc, char *argv[]);
 
diff --git a/c_src/libbcachefs.c b/c_src/libbcachefs.c
index cdb5f7f6..c3af991b 100644
--- a/c_src/libbcachefs.c
+++ b/c_src/libbcachefs.c
@@ -31,10 +31,10 @@
 
 #define NSEC_PER_SEC	1000000000L
 
-static void init_layout(struct bch_sb_layout *l,
-			unsigned block_size,
-			unsigned sb_size,
-			u64 sb_start, u64 sb_end)
+void bch2_sb_layout_init(struct bch_sb_layout *l,
+			 unsigned block_size,
+			 unsigned sb_size,
+			 u64 sb_start, u64 sb_end)
 {
 	u64 sb_pos = sb_start;
 	unsigned i;
@@ -307,9 +307,9 @@ struct bch_sb *bch2_format(struct bch_opt_strs	fs_opt_strs,
 			i->sb_end	= size_sectors;
 		}
 
-		init_layout(&sb.sb->layout, fs_opts.block_size,
-			    opts.superblock_size,
-			    i->sb_offset, i->sb_end);
+		bch2_sb_layout_init(&sb.sb->layout, fs_opts.block_size,
+				    opts.superblock_size,
+				    i->sb_offset, i->sb_end);
 
 		/*
 		 * Also create a backup superblock at the end of the disk:
diff --git a/c_src/libbcachefs.h b/c_src/libbcachefs.h
index ff754c4f..026a3def 100644
--- a/c_src/libbcachefs.h
+++ b/c_src/libbcachefs.h
@@ -79,6 +79,9 @@ static inline struct dev_opts dev_opts_default()
 	};
 }
 
+void bch2_sb_layout_init(struct bch_sb_layout *,
+			 unsigned, unsigned, u64, u64);
+
 u64 bch2_pick_bucket_size(struct bch_opts, struct dev_opts *);
 void bch2_check_bucket_size(struct bch_opts, struct dev_opts *);
 
diff --git a/src/bcachefs.rs b/src/bcachefs.rs
index 417cb01e..548add0a 100644
--- a/src/bcachefs.rs
+++ b/src/bcachefs.rs
@@ -60,6 +60,7 @@ fn handle_c_command(mut argv: Vec<String>, symlink_cmd: Option<&str>) -> i32 {
             "set-passphrase" => c::cmd_set_passphrase(argc, argv),
             "set-file-option" => c::cmd_setattr(argc, argv),
             "show-super" => c::cmd_show_super(argc, argv),
+            "recover-super" => c::cmd_recover_super(argc, argv),
             "unlock" => c::cmd_unlock(argc, argv),
             "version" => c::cmd_version(argc, argv),