-#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 <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/errcode.h"
+#include "libbcachefs/fs-common.h"
+#include "libbcachefs/inode.h"
+#include "libbcachefs/io_write.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)
{
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';
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;
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)
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);
}
int ret;
bch2_inode_pack(&packed, inode);
- ret = bch2_btree_update(c, BTREE_ID_INODES, &packed.inode.k_i, NULL);
+ packed.inode.k.p.snapshot = U32_MAX;
+ ret = bch2_btree_insert(c, BTREE_ID_inodes, &packed.inode.k_i,
+ NULL, 0);
if (ret)
- die("error creating file: %s", strerror(-ret));
-}
-
-static void create_dirent(struct bch_fs *c,
- struct bch_inode_unpacked *parent,
- const char *name, u64 inum, mode_t mode)
-{
- struct bch_hash_info parent_hash_info = bch2_hash_info_init(c, parent);
- struct qstr qname = { { { .len = strlen(name), } }, .name = name };
-
- int ret = bch2_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++;
+ die("error updating inode: %s", bch2_err_str(ret));
}
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 = bch2_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", bch2_err_str(ret));
}
static struct bch_inode_unpacked create_file(struct bch_fs *c,
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;
- bch2_inode_init(c, &new_inode, uid, gid, mode, rdev);
- bch2_inode_pack(&packed, &new_inode);
+ bch2_inode_init_early(c, &new_inode);
- ret = bch2_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, bch2_err_str(ret));
return new_inode;
}
(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 = 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) {
static void copy_times(struct bch_fs *c, struct bch_inode_unpacked *dst,
struct stat *src)
{
- dst->i_atime = timespec_to_bch2_time(c, src->st_atim);
- dst->i_mtime = timespec_to_bch2_time(c, src->st_mtim);
- dst->i_ctime = timespec_to_bch2_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 bch_fs *c, struct bch_inode_unpacked *dst,
char attrs[XATTR_LIST_MAX];
ssize_t attrs_size = llistxattr(src, attrs, sizeof(attrs));
if (attrs_size < 0)
- die("listxattr error: %s", strerror(errno));
+ die("listxattr error: %m");
- for (const char *next, *attr = attrs;
+ char *next, *attr;
+ for (attr = attrs;
attr < attrs + attrs_size;
attr = next) {
next = attr + strlen(attr) + 1;
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);
+ struct bch_inode_unpacked inode_u;
- int ret = __bch2_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 },
+ &inode_u, &hash_info, attr,
+ val, val_size, h->flags, 0));
if (ret < 0)
- die("error creating xattr: %s", strerror(-ret));
+ die("error creating xattr: %s", bch2_err_str(ret));
}
}
+#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 closure cl;
+ struct bio_vec bv[WRITE_DATA_BUF / PAGE_SIZE];
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(&op.wbio.bio, NULL, bv, ARRAY_SIZE(bv), 0);
+ bch2_bio_map(&op.wbio.bio, buf, len);
- bio_init(&bio.bio);
- bio.bio.bi_max_vecs = 1;
- bio.bio.bi_io_vec = &bv;
- bio.bio.bi_iter.bi_size = len;
- bch2_bio_map(&bio.bio, buf);
+ 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);
+ op.flags |= BCH_WRITE_SYNC;
- int ret = bch2_disk_reservation_get(c, &res, len >> 9, 0);
+ 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));
+ die("error reserving space in new filesystem: %s", bch2_err_str(ret));
- bch2_write_op_init(&op, c, &bio, res, c->write_points,
- POS(dst_inode->inum, dst_offset >> 9), NULL, 0);
- closure_call(&op.cl, bch2_write, NULL, &cl);
- closure_sync(&cl);
+ closure_call(&op.cl, bch2_write, NULL, NULL);
- dst_inode->i_sectors += len >> 9;
-}
+ BUG_ON(!(op.flags & BCH_WRITE_DONE));
+ dst_inode->bi_sectors += len >> 9;
-static char buf[1 << 20] __aligned(PAGE_SIZE);
+ if (op.error)
+ die("write error: %s", bch2_err_str(op.error));
+}
static void copy_data(struct bch_fs *c,
struct bch_inode_unpacked *dst_inode,
{
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;
}
}
while (length) {
struct bkey_i_extent *e;
- BKEY_PADDED(k) k;
- u64 b = sector_to_bucket(ca, physical >> 9);
+ BKEY_PADDED_ONSTACK(k, BKEY_EXTENT_VAL_U64s_MAX) k;
+ u64 b = sector_to_bucket(ca, physical);
struct disk_reservation res;
unsigned sectors;
int ret;
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 = bch2_disk_reservation_get(c, &res, sectors,
+ 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));
+ bch2_err_str(ret));
- bch2_check_mark_super(c, &e->k_i, false);
-
- ret = bch2_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, 0);
if (ret)
- die("btree insert error %s", strerror(-ret));
+ die("btree insert error %s", bch2_err_str(ret));
bch2_disk_reservation_put(c, &res);
- dst->i_sectors += sectors;
+ dst->bi_sectors += sectors;
logical += sectors;
physical += sectors;
length -= sectors;
{
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 bch_fs *c, struct bch_inode_unpacked *dst,
- int src, char *src_path, ranges *extents)
+ 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_iter_exit(&iter);
+
+ fiemap_for_each(src_fd, iter, e) {
+ u64 src_max = roundup(src_size, block_bytes(c));
+
+ e.fe_length = min(e.fe_length, src_max - e.fe_logical);
- fiemap_for_each(src, 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);
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,
- 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;
}
range_add(extents, e.fe_physical, e.fe_length);
link_data(c, dst, e.fe_logical, e.fe_physical, e.fe_length);
}
+ fiemap_iter_exit(&iter);
}
struct copy_fs_state {
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;
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);
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;
}
if (errno)
- die("readdir error: %s", strerror(errno));
+ die("readdir error: %m");
+ closedir(dir);
}
static ranges reserve_new_fs_space(const char *file_path, unsigned block_size,
? 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);
*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|
range_add(&extents, e.fe_physical, e.fe_length);
}
+ fiemap_iter_exit(&iter);
close(fd);
ranges_sort_merge(&extents);
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);
syncfs(src_fd);
struct bch_inode_unpacked root_inode;
- int ret = bch2_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));
+ die("error looking up root directory: %s", bch2_err_str(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);
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) {
+ 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);
- if (start + (128 << 10) <= end) {
+ /* Need space for two superblocks: */
+ if (start + (opts.superblock_size << 9) * 2 <= end) {
dev->sb_offset = start >> 9;
- dev->sb_end = dev->sb_offset + 256;
+ dev->sb_end = dev->sb_offset + opts.superblock_size * 2;
return;
}
}
" --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>");
+ "Report bugs to <linux-bcachefs@vger.kernel.org>");
}
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, force = false;
- int opt;
-
- 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 filesytem to migrate");
-
if (!path_is_fs_root(fs_path))
die("%s is not a filysestem root", fs_path);
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);
+ dev.bdev = blkdev_get_by_path(dev.path, BLK_OPEN_READ|BLK_OPEN_WRITE, &dev, NULL);
- 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.bdev->bd_buffered_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);
- bch2_pick_bucket_size(format_opts, &dev);
+ dev.size = get_size(dev.bdev->bd_buffered_fd);
+ dev.bucket_size = bch2_pick_bucket_size(fs_opts, &dev);
+ dev.nbuckets = dev.size / dev.bucket_size;
+ bch2_check_bucket_size(fs_opts, &dev);
+
+ u64 bcachefs_inum;
ranges extents = reserve_new_fs_space(file_path,
- block_size, get_size(dev.path, dev.fd) / 5,
+ fs_opts.block_size >> 9,
+ get_size(dev.bdev->bd_buffered_fd) / 5,
&bcachefs_inum, stat.st_dev, force);
- find_superblock_space(extents, &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: ");
+ find_superblock_space(extents, format_opts, &dev);
- 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");
- }
-
- memzero_explicit(pass2, strlen(pass2));
- free(pass2);
- }
- }
-
- struct bch_sb *sb = bch2_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)
- bch2_add_key(sb, format_opts.passphrase);
+ bch2_add_key(sb, "user", "user", format_opts.passphrase);
free(sb);
- printf("Creating new filesystem on %s in space reserved at %s\n"
- "To mount, 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",
- dev.path, file_path, sb_offset, dev.path,
- dev.path, sb_offset);
-
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 = bch2_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", bch2_err_str(PTR_ERR(c)));
mark_unreserved_space(c, extents);
- err = bch2_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", bch2_err_str(ret));
copy_fs(c, fs_fd, fs_path, bcachefs_inum, &extents);
bch2_fs_stop(c);
printf("Migrate complete, running fsck:\n");
- opts.nostart = false;
- opts.nochanges = true;
+ opt_set(opts, nostart, false);
+ opt_set(opts, nochanges, true);
- err = bch2_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", bch2_err_str(PTR_ERR(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("bcachefs migrate_superblock - create default superblock after migrating\n"
- "Usage: bcachefs 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"
" -o offset Offset of existing superblock\n"
" -h Display this help and exit\n"
- "Report bugs to <linux-bcache@vger.kernel.org>");
+ "Report bugs to <linux-bcachefs@vger.kernel.org>");
}
int cmd_migrate_superblock(int argc, char *argv[])
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");