]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - cmd_migrate.c
Update bcachefs sources to f638850417 bcachefs: bch2_trans_log_msg()
[bcachefs-tools-debian] / cmd_migrate.c
index 9a02cb9f0296d1aaa61bba9f337ae644fd377d11..08ec7de28004df6eca81c97b7fd933c9b502a434 100644 (file)
@@ -1,15 +1,15 @@
-#include </usr/include/dirent.h>
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <string.h>
+#include <sys/xattr.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <sys/vfs.h>
 #include <unistd.h>
-#include <attr/xattr.h>
 
 #include <linux/fiemap.h>
 #include <linux/fs.h>
 
 #include "cmds.h"
 #include "crypto.h"
-#include "libbcache.h"
-#include "linux/bcache.h"
+#include "libbcachefs.h"
 
 #include <linux/dcache.h>
 #include <linux/generic-radix-tree.h>
 #include <linux/xattr.h>
-#include "btree_update.h"
-#include "buckets.h"
-#include "dirent.h"
-#include "fs.h"
-#include "inode.h"
-#include "io.h"
-#include "str_hash.h"
-#include "super.h"
-#include "xattr.h"
+#include "libbcachefs/bcachefs.h"
+#include "libbcachefs/alloc_background.h"
+#include "libbcachefs/alloc_foreground.h"
+#include "libbcachefs/btree_update.h"
+#include "libbcachefs/buckets.h"
+#include "libbcachefs/dirent.h"
+#include "libbcachefs/fs-common.h"
+#include "libbcachefs/inode.h"
+#include "libbcachefs/io.h"
+#include "libbcachefs/replicas.h"
+#include "libbcachefs/str_hash.h"
+#include "libbcachefs/super.h"
+#include "libbcachefs/xattr.h"
+
+/* XXX cut and pasted from fsck.c */
+#define QSTR(n) { { { .len = strlen(n) } }, .name = n }
 
 static char *dev_t_to_path(dev_t dev)
 {
@@ -46,7 +52,7 @@ static char *dev_t_to_path(dev_t dev)
        free(sysfs_dev);
 
        if (ret < 0 || ret >= sizeof(link))
-               die("readlink error while looking up block device: %s", strerror(errno));
+               die("readlink error while looking up block device: %m");
 
        link[ret] = '\0';
 
@@ -58,7 +64,7 @@ static char *dev_t_to_path(dev_t dev)
        return mprintf("/dev/%s", p);
 }
 
-static bool path_is_fs_root(char *path)
+static bool path_is_fs_root(const char *path)
 {
        char *line = NULL, *p, *mount;
        size_t n = 0;
@@ -90,14 +96,13 @@ found:
        return ret;
 }
 
-static void mark_unreserved_space(struct cache_set *c, ranges extents)
+static void mark_unreserved_space(struct bch_fs *c, ranges extents)
 {
-       struct cache *ca = c->cache[0];
+       struct bch_dev *ca = c->devs[0];
        struct hole_iter iter;
        struct range i;
 
        for_each_hole(iter, extents, bucket_to_sector(ca, ca->mi.nbuckets) << 9, i) {
-               struct bucket_mark new;
                u64 b;
 
                if (i.start == i.end)
@@ -105,76 +110,61 @@ static void mark_unreserved_space(struct cache_set *c, ranges extents)
 
                b = sector_to_bucket(ca, i.start >> 9);
                do {
-                       bucket_cmpxchg(&ca->buckets[b], new, new.nouse = 1);
+                       set_bit(b, ca->buckets_nouse);
                        b++;
                } while (bucket_to_sector(ca, b) << 9 < i.end);
        }
 }
 
-static void update_inode(struct cache_set *c,
+static void update_inode(struct bch_fs *c,
                         struct bch_inode_unpacked *inode)
 {
        struct bkey_inode_buf packed;
        int ret;
 
-       bch_inode_pack(&packed, inode);
-       ret = bch_btree_update(c, BTREE_ID_INODES, &packed.inode.k_i, NULL);
+       bch2_inode_pack(c, &packed, inode);
+       packed.inode.k.p.snapshot = U32_MAX;
+       ret = bch2_btree_insert(c, BTREE_ID_inodes, &packed.inode.k_i,
+                               NULL, NULL, 0);
        if (ret)
-               die("error creating file: %s", strerror(-ret));
+               die("error updating inode: %s", strerror(-ret));
 }
 
-static void create_dirent(struct cache_set *c,
-                         struct bch_inode_unpacked *parent,
-                         const char *name, u64 inum, mode_t mode)
-{
-       struct bch_hash_info parent_hash_info = bch_hash_info_init(parent);
-       struct qstr qname = { { { .len = strlen(name), } }, .name = name };
-
-       int ret = bch_dirent_create(c, parent->inum, &parent_hash_info,
-                                   mode_to_type(mode), &qname,
-                                   inum, NULL, BCH_HASH_SET_MUST_CREATE);
-       if (ret)
-               die("error creating file: %s", strerror(-ret));
-
-       if (S_ISDIR(mode))
-               parent->i_nlink++;
-}
-
-static void create_link(struct cache_set *c,
+static void create_link(struct bch_fs *c,
                        struct bch_inode_unpacked *parent,
                        const char *name, u64 inum, mode_t mode)
 {
+       struct qstr qstr = QSTR(name);
+       struct bch_inode_unpacked parent_u;
        struct bch_inode_unpacked inode;
-       int ret = bch_inode_find_by_inum(c, inum, &inode);
-       if (ret)
-               die("error looking up hardlink: %s", strerror(-ret));
 
-       inode.i_nlink++;
-       update_inode(c, &inode);
-
-       create_dirent(c, parent, name, inum, mode);
+       int ret = bch2_trans_do(c, NULL, NULL, 0,
+               bch2_link_trans(&trans,
+                               (subvol_inum) { 1, parent->bi_inum }, &parent_u,
+                               (subvol_inum) { 1, inum }, &inode, &qstr));
+       if (ret)
+               die("error creating hardlink: %s", strerror(-ret));
 }
 
-static struct bch_inode_unpacked create_file(struct cache_set *c,
+static struct bch_inode_unpacked create_file(struct bch_fs *c,
                                             struct bch_inode_unpacked *parent,
                                             const char *name,
                                             uid_t uid, gid_t gid,
                                             mode_t mode, dev_t rdev)
 {
+       struct qstr qstr = QSTR(name);
        struct bch_inode_unpacked new_inode;
-       struct bkey_inode_buf packed;
-       int ret;
 
-       bch_inode_init(c, &new_inode, uid, gid, mode, rdev);
-       bch_inode_pack(&packed, &new_inode);
+       bch2_inode_init_early(c, &new_inode);
 
-       ret = bch_inode_create(c, &packed.inode.k_i, BLOCKDEV_INODE_MAX, 0,
-                              &c->unused_inode_hint);
+       int ret = bch2_trans_do(c, NULL, NULL, 0,
+               bch2_create_trans(&trans,
+                                 (subvol_inum) { 1, parent->bi_inum }, parent,
+                                 &new_inode, &qstr,
+                                 uid, gid, mode, rdev, NULL, NULL,
+                                 (subvol_inum) {}, 0));
        if (ret)
-               die("error creating file: %s", strerror(-ret));
-
-       new_inode.inum = packed.inode.k.p.inode;
-       create_dirent(c, parent, name, new_inode.inum, mode);
+               die("error creating %s: %s", name, strerror(-ret));
 
        return new_inode;
 }
@@ -185,13 +175,13 @@ static struct bch_inode_unpacked create_file(struct cache_set *c,
                        (handler) != NULL;                      \
                        (handler) = *(handlers)++)
 
-static const struct xattr_handler *xattr_resolve_name(const char **name)
+static const struct xattr_handler *xattr_resolve_name(char **name)
 {
-       const struct xattr_handler **handlers = bch_xattr_handlers;
+       const struct xattr_handler **handlers = bch2_xattr_handlers;
        const struct xattr_handler *handler;
 
        for_each_xattr_handler(handlers, handler) {
-               const char *n;
+               char *n;
 
                n = strcmp_prefix(*name, xattr_prefix(handler));
                if (n) {
@@ -207,105 +197,106 @@ static const struct xattr_handler *xattr_resolve_name(const char **name)
        return ERR_PTR(-EOPNOTSUPP);
 }
 
-static void copy_times(struct cache_set *c, struct bch_inode_unpacked *dst,
+static void copy_times(struct bch_fs *c, struct bch_inode_unpacked *dst,
                       struct stat *src)
 {
-       dst->i_atime = timespec_to_bch_time(c, src->st_atim);
-       dst->i_mtime = timespec_to_bch_time(c, src->st_mtim);
-       dst->i_ctime = timespec_to_bch_time(c, src->st_ctim);
+       dst->bi_atime = timespec_to_bch2_time(c, src->st_atim);
+       dst->bi_mtime = timespec_to_bch2_time(c, src->st_mtim);
+       dst->bi_ctime = timespec_to_bch2_time(c, src->st_ctim);
 }
 
-static void copy_xattrs(struct cache_set *c, struct bch_inode_unpacked *dst,
+static void copy_xattrs(struct bch_fs *c, struct bch_inode_unpacked *dst,
                        char *src)
 {
-       struct bch_hash_info hash_info = bch_hash_info_init(dst);
-       ssize_t size = llistxattr(src, NULL, 0);
-       if (size < 0)
-               die("listxattr error: %s", strerror(errno));
-
-       if (!size)
-               return;
+       struct bch_hash_info hash_info = bch2_hash_info_init(c, dst);
 
-       char *buf = malloc(size);
-       size = llistxattr(src, buf, size);
-       if (size < 0)
-               die("listxattr error: %s", strerror(errno));
+       char attrs[XATTR_LIST_MAX];
+       ssize_t attrs_size = llistxattr(src, attrs, sizeof(attrs));
+       if (attrs_size < 0)
+               die("listxattr error: %m");
 
-       for (const char *next, *attr = buf;
-            attr <= buf + size;
+       char *next, *attr;
+       for (attr = attrs;
+            attr < attrs + attrs_size;
             attr = next) {
                next = attr + strlen(attr) + 1;
 
-               /* max possible xattr val: */
-               static char val[64 << 10];
+               char val[XATTR_SIZE_MAX];
                ssize_t val_size = lgetxattr(src, attr, val, sizeof(val));
 
                if (val_size < 0)
-                       die("error getting xattr val: %s", strerror(errno));
+                       die("error getting xattr val: %m");
 
                const struct xattr_handler *h = xattr_resolve_name(&attr);
 
-               int ret = __bch_xattr_set(c, dst->inum, &hash_info, attr,
-                                         val, val_size, 0, h->flags, NULL);
+               int ret = bch2_trans_do(c, NULL, NULL, 0,
+                               bch2_xattr_set(&trans,
+                                              (subvol_inum) { 1, dst->bi_inum },
+                                              &hash_info, attr,
+                                              val, val_size, h->flags, 0));
                if (ret < 0)
                        die("error creating xattr: %s", strerror(-ret));
        }
-
-       free(buf);
 }
 
-static void write_data(struct cache_set *c,
+#define WRITE_DATA_BUF (1 << 20)
+
+static char buf[WRITE_DATA_BUF] __aligned(PAGE_SIZE);
+
+static void write_data(struct bch_fs *c,
                       struct bch_inode_unpacked *dst_inode,
                       u64 dst_offset, void *buf, size_t len)
 {
-       struct disk_reservation res;
        struct bch_write_op op;
-       struct bch_write_bio bio;
-       struct bio_vec bv;
+       struct bio_vec bv[WRITE_DATA_BUF / PAGE_SIZE];
        struct closure cl;
 
        BUG_ON(dst_offset       & (block_bytes(c) - 1));
        BUG_ON(len              & (block_bytes(c) - 1));
+       BUG_ON(len > WRITE_DATA_BUF);
 
        closure_init_stack(&cl);
 
-       bio_init(&bio.bio);
-       bio.bio.bi_max_vecs     = 1;
-       bio.bio.bi_io_vec       = &bv;
-       bio.bio.bi_iter.bi_size = len;
-       bch_bio_map(&bio.bio, buf);
+       bio_init(&op.wbio.bio, bv, ARRAY_SIZE(bv));
+       bch2_bio_map(&op.wbio.bio, buf, len);
 
-       int ret = bch_disk_reservation_get(c, &res, len >> 9, 0);
+       bch2_write_op_init(&op, c, bch2_opts_to_inode_opts(c->opts));
+       op.write_point  = writepoint_hashed(0);
+       op.nr_replicas  = 1;
+       op.subvol       = 1;
+       op.pos          = SPOS(dst_inode->bi_inum, dst_offset >> 9, U32_MAX);
+
+       int ret = bch2_disk_reservation_get(c, &op.res, len >> 9,
+                                           c->opts.data_replicas, 0);
        if (ret)
                die("error reserving space in new filesystem: %s", strerror(-ret));
 
-       bch_write_op_init(&op, c, &bio, res, c->write_points,
-                         POS(dst_inode->inum, dst_offset >> 9), NULL, 0);
-       closure_call(&op.cl, bch_write, NULL, &cl);
+       closure_call(&op.cl, bch2_write, NULL, &cl);
        closure_sync(&cl);
 
-       dst_inode->i_sectors += len >> 9;
+       dst_inode->bi_sectors += len >> 9;
 }
 
-static char buf[1 << 20] __aligned(PAGE_SIZE);
-
-static void copy_data(struct cache_set *c,
+static void copy_data(struct bch_fs *c,
                      struct bch_inode_unpacked *dst_inode,
                      int src_fd, u64 start, u64 end)
 {
        while (start < end) {
                unsigned len = min_t(u64, end - start, sizeof(buf));
+               unsigned pad = round_up(len, block_bytes(c)) - len;
 
                xpread(src_fd, buf, len, start);
-               write_data(c, dst_inode, start, buf, len);
+               memset(buf + len, 0, pad);
+
+               write_data(c, dst_inode, start, buf, len + pad);
                start += len;
        }
 }
 
-static void link_data(struct cache_set *c, struct bch_inode_unpacked *dst,
+static void link_data(struct bch_fs *c, struct bch_inode_unpacked *dst,
                      u64 logical, u64 physical, u64 length)
 {
-       struct cache *ca = c->cache[0];
+       struct bch_dev *ca = c->devs[0];
 
        BUG_ON(logical  & (block_bytes(c) - 1));
        BUG_ON(physical & (block_bytes(c) - 1));
@@ -319,8 +310,8 @@ static void link_data(struct cache_set *c, struct bch_inode_unpacked *dst,
 
        while (length) {
                struct bkey_i_extent *e;
-               BKEY_PADDED(k) k;
-               u64 b = sector_to_bucket(ca, physical >> 9);
+               __BKEY_PADDED(k, BKEY_EXTENT_VAL_U64s_MAX) k;
+               u64 b = sector_to_bucket(ca, physical);
                struct disk_reservation res;
                unsigned sectors;
                int ret;
@@ -330,58 +321,60 @@ static void link_data(struct cache_set *c, struct bch_inode_unpacked *dst,
                              length);
 
                e = bkey_extent_init(&k.k);
-               e->k.p.inode    = dst->inum;
+               e->k.p.inode    = dst->bi_inum;
                e->k.p.offset   = logical + sectors;
+               e->k.p.snapshot = U32_MAX;
                e->k.size       = sectors;
-               extent_ptr_append(e, (struct bch_extent_ptr) {
+               bch2_bkey_append_ptr(&e->k_i, (struct bch_extent_ptr) {
                                        .offset = physical,
                                        .dev = 0,
-                                       .gen = ca->buckets[b].mark.gen,
+                                       .gen = *bucket_gen(ca, b),
                                  });
 
-               ret = bch_disk_reservation_get(c, &res, sectors,
-                                              BCH_DISK_RESERVATION_NOFAIL);
+               ret = bch2_disk_reservation_get(c, &res, sectors, 1,
+                                               BCH_DISK_RESERVATION_NOFAIL);
                if (ret)
                        die("error reserving space in new filesystem: %s",
                            strerror(-ret));
 
-               ret = bch_btree_insert(c, BTREE_ID_EXTENTS, &e->k_i,
-                                      &res, NULL, NULL, 0);
+               ret = bch2_btree_insert(c, BTREE_ID_extents, &e->k_i,
+                                       &res, NULL, 0);
                if (ret)
                        die("btree insert error %s", strerror(-ret));
 
-               bch_disk_reservation_put(c, &res);
+               bch2_disk_reservation_put(c, &res);
 
-               dst->i_sectors  += sectors;
+               dst->bi_sectors += sectors;
                logical         += sectors;
                physical        += sectors;
                length          -= sectors;
        }
 }
 
-static void copy_link(struct cache_set *c, struct bch_inode_unpacked *dst,
+static void copy_link(struct bch_fs *c, struct bch_inode_unpacked *dst,
                      char *src)
 {
        ssize_t ret = readlink(src, buf, sizeof(buf));
        if (ret < 0)
-               die("readlink error: %s", strerror(errno));
+               die("readlink error: %m");
 
        write_data(c, dst, 0, buf, round_up(ret, block_bytes(c)));
 }
 
-static void copy_file(struct cache_set *c, struct bch_inode_unpacked *dst,
-                     int src, char *src_path, ranges *extents)
+static void copy_file(struct bch_fs *c, struct bch_inode_unpacked *dst,
+                     int src_fd, u64 src_size,
+                     char *src_path, ranges *extents)
 {
        struct fiemap_iter iter;
        struct fiemap_extent e;
 
-       fiemap_for_each(src, iter, e)
+       fiemap_for_each(src_fd, iter, e)
                if (e.fe_flags & FIEMAP_EXTENT_UNKNOWN) {
-                       fsync(src);
+                       fsync(src_fd);
                        break;
                }
 
-       fiemap_for_each(src, iter, e) {
+       fiemap_for_each(src_fd, iter, e) {
                if ((e.fe_logical       & (block_bytes(c) - 1)) ||
                    (e.fe_length        & (block_bytes(c) - 1)))
                        die("Unaligned extent in %s - can't handle", src_path);
@@ -390,11 +383,20 @@ static void copy_file(struct cache_set *c, struct bch_inode_unpacked *dst,
                                  FIEMAP_EXTENT_ENCODED|
                                  FIEMAP_EXTENT_NOT_ALIGNED|
                                  FIEMAP_EXTENT_DATA_INLINE)) {
-                       copy_data(c, dst,
-                                 src,
-                                 round_down(e.fe_logical, block_bytes(c)),
-                                 round_up(e.fe_logical + e.fe_length,
-                                          block_bytes(c)));
+                       copy_data(c, dst, src_fd, e.fe_logical,
+                                 min(src_size - e.fe_logical,
+                                     e.fe_length));
+                       continue;
+               }
+
+               /*
+                * if the data is below 1 MB, copy it so it doesn't conflict
+                * with bcachefs's potentially larger superblock:
+                */
+               if (e.fe_physical < 1 << 20) {
+                       copy_data(c, dst, src_fd, e.fe_logical,
+                                 min(src_size - e.fe_logical,
+                                     e.fe_length));
                        continue;
                }
 
@@ -415,7 +417,7 @@ struct copy_fs_state {
 };
 
 static void copy_dir(struct copy_fs_state *s,
-                    struct cache_set *c,
+                    struct bch_fs *c,
                     struct bch_inode_unpacked *dst,
                     int src_fd, const char *src_path)
 {
@@ -427,13 +429,14 @@ static void copy_dir(struct copy_fs_state *s,
                int fd;
 
                if (fchdir(src_fd))
-                       die("chdir error: %s", strerror(errno));
+                       die("chdir error: %m");
 
                struct stat stat =
                        xfstatat(src_fd, d->d_name, AT_SYMLINK_NOFOLLOW);
 
                if (!strcmp(d->d_name, ".") ||
                    !strcmp(d->d_name, "..") ||
+                   !strcmp(d->d_name, "lost+found") ||
                    stat.st_ino == s->bcachefs_inum)
                        continue;
 
@@ -456,7 +459,7 @@ static void copy_dir(struct copy_fs_state *s,
                                    stat.st_mode, stat.st_rdev);
 
                if (dst_inum)
-                       *dst_inum = inode.inum;
+                       *dst_inum = inode.bi_inum;
 
                copy_times(c, &inode, &stat);
                copy_xattrs(c, &inode, d->d_name);
@@ -470,14 +473,15 @@ static void copy_dir(struct copy_fs_state *s,
                        close(fd);
                        break;
                case DT_REG:
-                       inode.i_size = stat.st_size;
+                       inode.bi_size = stat.st_size;
 
                        fd = xopen(d->d_name, O_RDONLY|O_NOATIME);
-                       copy_file(c, &inode, fd, child_path, &s->extents);
+                       copy_file(c, &inode, fd, stat.st_size,
+                                 child_path, &s->extents);
                        close(fd);
                        break;
                case DT_LNK:
-                       inode.i_size = stat.st_size;
+                       inode.bi_size = stat.st_size;
 
                        copy_link(c, &inode, d->d_name);
                        break;
@@ -498,16 +502,19 @@ next:
        }
 
        if (errno)
-               die("readdir error: %s", strerror(errno));
+               die("readdir error: %m");
 }
 
 static ranges reserve_new_fs_space(const char *file_path, unsigned block_size,
-                                  u64 size, u64 *bcachefs_inum, dev_t dev)
+                                  u64 size, u64 *bcachefs_inum, dev_t dev,
+                                  bool force)
 {
-       int fd = open(file_path, O_RDWR|O_CREAT|O_EXCL, 0600);
+       int fd = force
+               ? open(file_path, O_RDWR|O_CREAT, 0600)
+               : open(file_path, O_RDWR|O_CREAT|O_EXCL, 0600);
        if (fd < 0)
-               die("Error creating %s for bcachefs metadata: %s",
-                   file_path, strerror(errno));
+               die("Error creating %s for bcachefs metadata: %m",
+                   file_path);
 
        struct stat statbuf = xfstat(fd);
 
@@ -517,14 +524,13 @@ static ranges reserve_new_fs_space(const char *file_path, unsigned block_size,
        *bcachefs_inum = statbuf.st_ino;
 
        if (fallocate(fd, 0, 0, size))
-               die("Error reserving space for bcachefs metadata: %s",
-                   strerror(errno));
+               die("Error reserving space for bcachefs metadata: %m");
 
        fsync(fd);
 
        struct fiemap_iter iter;
        struct fiemap_extent e;
-       ranges extents = { NULL };
+       ranges extents = { 0 };
 
        fiemap_for_each(fd, iter, e) {
                if (e.fe_flags & (FIEMAP_EXTENT_UNKNOWN|
@@ -545,18 +551,18 @@ static ranges reserve_new_fs_space(const char *file_path, unsigned block_size,
        return extents;
 }
 
-static void reserve_old_fs_space(struct cache_set *c,
+static void reserve_old_fs_space(struct bch_fs *c,
                                 struct bch_inode_unpacked *root_inode,
                                 ranges *extents)
 {
-       struct cache *ca = c->cache[0];
+       struct bch_dev *ca = c->devs[0];
        struct bch_inode_unpacked dst;
        struct hole_iter iter;
        struct range i;
 
        dst = create_file(c, root_inode, "old_migrated_filesystem",
                          0, 0, S_IFREG|0400, 0);
-       dst.i_size = bucket_to_sector(ca, ca->mi.nbuckets) << 9;
+       dst.bi_size = bucket_to_sector(ca, ca->mi.nbuckets) << 9;
 
        ranges_sort_merge(extents);
 
@@ -566,18 +572,19 @@ static void reserve_old_fs_space(struct cache_set *c,
        update_inode(c, &dst);
 }
 
-static void copy_fs(struct cache_set *c, int src_fd, const char *src_path,
+static void copy_fs(struct bch_fs *c, int src_fd, const char *src_path,
                    u64 bcachefs_inum, ranges *extents)
 {
        syncfs(src_fd);
 
        struct bch_inode_unpacked root_inode;
-       int ret = bch_inode_find_by_inum(c, BCACHE_ROOT_INO, &root_inode);
+       int ret = bch2_inode_find_by_inum(c, (subvol_inum) { 1, BCACHEFS_ROOT_INO },
+                                         &root_inode);
        if (ret)
                die("error looking up root directory: %s", strerror(-ret));
 
        if (fchdir(src_fd))
-               die("chdir error: %s", strerror(errno));
+               die("chdir error: %m");
 
        struct stat stat = xfstat(src_fd);
        copy_times(c, &root_inode, &stat);
@@ -596,19 +603,26 @@ static void copy_fs(struct cache_set *c, int src_fd, const char *src_path,
 
        update_inode(c, &root_inode);
 
-       darray_free(s.extents);
+       darray_exit(s.extents);
        genradix_free(&s.hardlinks);
 }
 
-static void find_superblock_space(ranges extents, struct dev_opts *dev)
+static void find_superblock_space(ranges extents,
+                                 struct format_opts opts,
+                                 struct dev_opts *dev)
 {
        struct range *i;
-       darray_foreach(i, extents) {
-               u64 offset = max(256ULL << 10, i->start);
 
-               if (offset + (128 << 10) <= i->end) {
-                       dev->sb_offset  = offset >> 9;
-                       dev->sb_end     = dev->sb_offset + 256;
+       darray_for_each(extents, i) {
+               u64 start = round_up(max(256ULL << 10, i->start),
+                                    dev->bucket_size << 9);
+               u64 end = round_down(i->end,
+                                    dev->bucket_size << 9);
+
+               /* Need space for two superblocks: */
+               if (start + (opts.superblock_size << 9) * 2 <= end) {
+                       dev->sb_offset  = start >> 9;
+                       dev->sb_end     = dev->sb_offset + opts.superblock_size * 2;
                        return;
                }
        }
@@ -618,13 +632,14 @@ static void find_superblock_space(ranges extents, struct dev_opts *dev)
 
 static void migrate_usage(void)
 {
-       puts("bcache migrate - migrate an existing filesystem to bcachefs\n"
-            "Usage: bcache migrate [OPTION]...\n"
+       puts("bcachefs migrate - migrate an existing filesystem to bcachefs\n"
+            "Usage: bcachefs migrate [OPTION]...\n"
             "\n"
             "Options:\n"
             "  -f fs                  Root of filesystem to migrate(s)\n"
             "      --encrypted        Enable whole filesystem encryption (chacha20/poly1305)\n"
             "      --no_passphrase    Don't encrypt master encryption key\n"
+            "  -F                     Force, even if metadata file already exists\n"
             "  -h                     Display this help and exit\n"
             "Report bugs to <linux-bcache@vger.kernel.org>");
 }
@@ -635,34 +650,12 @@ static const struct option migrate_opts[] = {
        { NULL }
 };
 
-int cmd_migrate(int argc, char *argv[])
+static int migrate_fs(const char               *fs_path,
+                     struct bch_opt_strs       fs_opt_strs,
+                     struct bch_opts           fs_opts,
+                     struct format_opts        format_opts,
+                     bool force)
 {
-       struct format_opts format_opts = format_opts_default();
-       char *fs_path = NULL;
-       unsigned block_size;
-       bool no_passphrase = false;
-       int opt;
-
-       while ((opt = getopt_long(argc, argv, "f:h",
-                                 migrate_opts, NULL)) != -1)
-               switch (opt) {
-               case 'f':
-                       fs_path = optarg;
-                       break;
-               case 'e':
-                       format_opts.encrypted = true;
-                       break;
-               case 'p':
-                       no_passphrase = true;
-                       break;
-               case 'h':
-                       migrate_usage();
-                       exit(EXIT_SUCCESS);
-               }
-
-       if (!fs_path)
-               die("Please specify a filesytem to migrate");
-
        if (!path_is_fs_root(fs_path))
                die("%s is not a filysestem root", fs_path);
 
@@ -672,110 +665,135 @@ int cmd_migrate(int argc, char *argv[])
        if (!S_ISDIR(stat.st_mode))
                die("%s is not a directory", fs_path);
 
-       struct dev_opts dev = { 0 };
+       struct dev_opts dev = dev_opts_default();
 
        dev.path = dev_t_to_path(stat.st_dev);
        dev.fd = xopen(dev.path, O_RDWR);
 
-       block_size = min_t(unsigned, stat.st_blksize,
-                          get_blocksize(dev.path, dev.fd) << 9);
-
-       BUG_ON(!is_power_of_2(block_size) || block_size < 512);
-       format_opts.block_size = block_size >> 9;
+       opt_set(fs_opts, block_size, get_blocksize(dev.path, dev.fd));
 
-       u64 bcachefs_inum;
        char *file_path = mprintf("%s/bcachefs", fs_path);
+       printf("Creating new filesystem on %s in space reserved at %s\n",
+              dev.path, file_path);
 
-       ranges extents = reserve_new_fs_space(file_path,
-                               block_size, get_size(dev.path, dev.fd) / 5,
-                               &bcachefs_inum, stat.st_dev);
-
-       find_superblock_space(extents, &dev);
+       bch2_pick_bucket_size(fs_opts, &dev);
 
-       if (format_opts.encrypted && !no_passphrase) {
-               format_opts.passphrase = read_passphrase("Enter passphrase: ");
-
-               if (isatty(STDIN_FILENO)) {
-                       char *pass2 =
-                               read_passphrase("Enter same passphrase again: ");
-
-                       if (strcmp(format_opts.passphrase, pass2)) {
-                               memzero_explicit(format_opts.passphrase,
-                                                strlen(format_opts.passphrase));
-                               memzero_explicit(pass2, strlen(pass2));
-                               die("Passphrases do not match");
-                       }
+       u64 bcachefs_inum;
+       ranges extents = reserve_new_fs_space(file_path,
+                               fs_opts.block_size >> 9,
+                               get_size(dev.path, dev.fd) / 5,
+                               &bcachefs_inum, stat.st_dev, force);
 
-                       memzero_explicit(pass2, strlen(pass2));
-                       free(pass2);
-               }
-       }
+       find_superblock_space(extents, format_opts, &dev);
 
-       struct bch_sb *sb = bcache_format(format_opts, &dev, 1);
+       struct bch_sb *sb = bch2_format(fs_opt_strs,
+                                       fs_opts,format_opts, &dev, 1);
        u64 sb_offset = le64_to_cpu(sb->layout.sb_offset[0]);
 
        if (format_opts.passphrase)
-               add_bcache_key(sb, format_opts.passphrase);
+               bch2_add_key(sb, format_opts.passphrase);
 
        free(sb);
 
-       printf("Creating new filesystem on %s in space reserved at %s\n"
-              "To mount, run\n"
-              "  mount -t bcache -o sb=%llu %s dir\n"
-              "\n"
-              "After verifying that the new filesystem is correct, to create a\n"
-              "superblock at the default offset and finish the migration run\n"
-              "  bcache migrate_superblock -d %s -o %llu\n"
-              "\n"
-              "The new filesystem will have a file at /old_migrated_filestem\n"
-              "referencing all disk space that might be used by the existing\n"
-              "filesystem. That file can be deleted once the old filesystem is\n"
-              "no longer needed (and should be deleted prior to running\n"
-              "bcache migrate_superblock)\n",
-              dev.path, file_path, sb_offset, dev.path,
-              dev.path, sb_offset);
-
-       struct bch_opts opts = bch_opts_empty();
-       struct cache_set *c = NULL;
+       struct bch_opts opts = bch2_opts_empty();
+       struct bch_fs *c = NULL;
        char *path[1] = { dev.path };
-       const char *err;
 
-       opts.sb         = sb_offset;
-       opts.nostart    = true;
-       opts.noexcl     = true;
+       opt_set(opts, sb,       sb_offset);
+       opt_set(opts, nostart,  true);
+       opt_set(opts, noexcl,   true);
+       opt_set(opts, buckets_nouse, true);
 
-       err = bch_fs_open(path, 1, opts, &c);
-       if (err)
-               die("Error opening new filesystem: %s", err);
+       c = bch2_fs_open(path, 1, opts);
+       if (IS_ERR(c))
+               die("Error opening new filesystem: %s", strerror(-PTR_ERR(c)));
 
        mark_unreserved_space(c, extents);
 
-       err = bch_fs_start(c);
-       if (err)
-               die("Error starting new filesystem: %s", err);
+       int ret = bch2_fs_start(c);
+       if (ret)
+               die("Error starting new filesystem: %s", strerror(-ret));
 
        copy_fs(c, fs_fd, fs_path, bcachefs_inum, &extents);
 
-       bch_fs_stop(c);
+       bch2_fs_stop(c);
 
        printf("Migrate complete, running fsck:\n");
-       opts.nostart    = false;
-       opts.nochanges  = true;
-       fsck_err_opt    = FSCK_ERR_NO;
+       opt_set(opts, nostart,  false);
+       opt_set(opts, nochanges, true);
 
-       err = bch_fs_open(path, 1, opts, &c);
-       if (err)
-               die("Error opening new filesystem: %s", err);
+       c = bch2_fs_open(path, 1, opts);
+       if (IS_ERR(c))
+               die("Error opening new filesystem: %s", strerror(-PTR_ERR(c)));
 
-       bch_fs_stop(c);
+       bch2_fs_stop(c);
        printf("fsck complete\n");
+
+       printf("To mount the new filesystem, run\n"
+              "  mount -t bcachefs -o sb=%llu %s dir\n"
+              "\n"
+              "After verifying that the new filesystem is correct, to create a\n"
+              "superblock at the default offset and finish the migration run\n"
+              "  bcachefs migrate-superblock -d %s -o %llu\n"
+              "\n"
+              "The new filesystem will have a file at /old_migrated_filestem\n"
+              "referencing all disk space that might be used by the existing\n"
+              "filesystem. That file can be deleted once the old filesystem is\n"
+              "no longer needed (and should be deleted prior to running\n"
+              "bcachefs migrate-superblock)\n",
+              sb_offset, dev.path, dev.path, sb_offset);
        return 0;
 }
 
+int cmd_migrate(int argc, char *argv[])
+{
+       struct format_opts format_opts = format_opts_default();
+       char *fs_path = NULL;
+       bool no_passphrase = false, force = false;
+       int opt;
+
+       struct bch_opt_strs fs_opt_strs =
+               bch2_cmdline_opts_get(&argc, argv, OPT_FORMAT);
+       struct bch_opts fs_opts = bch2_parse_opts(fs_opt_strs);
+
+       while ((opt = getopt_long(argc, argv, "f:Fh",
+                                 migrate_opts, NULL)) != -1)
+               switch (opt) {
+               case 'f':
+                       fs_path = optarg;
+                       break;
+               case 'e':
+                       format_opts.encrypted = true;
+                       break;
+               case 'p':
+                       no_passphrase = true;
+                       break;
+               case 'F':
+                       force = true;
+                       break;
+               case 'h':
+                       migrate_usage();
+                       exit(EXIT_SUCCESS);
+               }
+
+       if (!fs_path)
+               die("Please specify a filesystem to migrate");
+
+       if (format_opts.encrypted && !no_passphrase)
+               format_opts.passphrase = read_passphrase_twice("Enter passphrase: ");
+
+       int ret = migrate_fs(fs_path,
+                            fs_opt_strs,
+                            fs_opts,
+                            format_opts, force);
+       bch2_opt_strs_free(&fs_opt_strs);
+       return ret;
+}
+
 static void migrate_superblock_usage(void)
 {
-       puts("bcache migrate_superblock - create default superblock after migrating\n"
-            "Usage: bcache migrate_superblock [OPTION]...\n"
+       puts("bcachefs migrate-superblock - create default superblock after migrating\n"
+            "Usage: bcachefs migrate-superblock [OPTION]...\n"
             "\n"
             "Options:\n"
             "  -d device     Device to create superblock for\n"
@@ -812,12 +830,13 @@ int cmd_migrate_superblock(int argc, char *argv[])
                die("Please specify offset of existing superblock");
 
        int fd = xopen(dev, O_RDWR);
-       struct bch_sb *sb = __bcache_super_read(fd, offset);
+       struct bch_sb *sb = __bch2_super_read(fd, offset);
 
        if (sb->layout.nr_superblocks >= ARRAY_SIZE(sb->layout.sb_offset))
                die("Can't add superblock: no space left in superblock layout");
 
-       for (unsigned i = 0; i < sb->layout.nr_superblocks; i++)
+       unsigned i;
+       for (i = 0; i < sb->layout.nr_superblocks; i++)
                if (le64_to_cpu(sb->layout.sb_offset[i]) == BCH_SB_SECTOR)
                        die("Superblock layout already has default superblock");
 
@@ -828,7 +847,7 @@ int cmd_migrate_superblock(int argc, char *argv[])
 
        sb->layout.sb_offset[0] = cpu_to_le64(BCH_SB_SECTOR);
 
-       bcache_super_write(fd, sb);
+       bch2_super_write(fd, sb);
        close(fd);
 
        return 0;