summaryrefslogtreecommitdiff
path: root/c_src/posix_to_bcachefs.c
diff options
context:
space:
mode:
Diffstat (limited to 'c_src/posix_to_bcachefs.c')
-rw-r--r--c_src/posix_to_bcachefs.c579
1 files changed, 503 insertions, 76 deletions
diff --git a/c_src/posix_to_bcachefs.c b/c_src/posix_to_bcachefs.c
index 68d7e992..62f10b3d 100644
--- a/c_src/posix_to_bcachefs.c
+++ b/c_src/posix_to_bcachefs.c
@@ -7,12 +7,40 @@
#include "posix_to_bcachefs.h"
#include "libbcachefs/alloc_foreground.h"
#include "libbcachefs/buckets.h"
+#include "libbcachefs/io_misc.h"
+#include "libbcachefs/io_read.h"
#include "libbcachefs/io_write.h"
#include "libbcachefs/namei.h"
#include "libbcachefs/str_hash.h"
#include "libbcachefs/xattr.h"
-void update_inode(struct bch_fs *c,
+static int unlink_and_rm(struct bch_fs *c,
+ subvol_inum dir_inum,
+ struct bch_inode_unpacked *dir,
+ const char *child_name)
+{
+ struct qstr child_name_q = QSTR_INIT(child_name, strlen(child_name));
+
+ struct bch_inode_unpacked child;
+ int ret = bch2_trans_commit_do(c, NULL, NULL,
+ BCH_TRANS_COMMIT_no_enospc,
+ bch2_unlink_trans(trans, dir_inum, dir, &child, &child_name_q, false));
+ bch_err_msg(c, ret, "unlinking %s", child_name);
+ if (ret)
+ return ret;
+
+ if (!(child.bi_flags & BCH_INODE_unlinked))
+ return 0;
+
+ subvol_inum child_inum = dir_inum;
+ child_inum.inum = child.bi_inum;
+
+ ret = bch2_inode_rm(c, child_inum);
+ bch_err_msg(c, ret, "deleting %s", child_name);
+ return ret;
+}
+
+static void update_inode(struct bch_fs *c,
struct bch_inode_unpacked *inode)
{
struct bkey_inode_buf packed;
@@ -26,43 +54,87 @@ void update_inode(struct bch_fs *c,
die("error updating inode: %s", bch2_err_str(ret));
}
-void create_link(struct bch_fs *c,
- struct bch_inode_unpacked *parent,
- const char *name, u64 inum, mode_t mode)
+static int create_or_update_link(struct bch_fs *c,
+ subvol_inum dir_inum,
+ struct bch_inode_unpacked *dir,
+ const char *name, subvol_inum inum, mode_t mode)
{
+ struct bch_hash_info dir_hash = bch2_hash_info_init(c, dir);
+
struct qstr qstr = QSTR(name);
- struct bch_inode_unpacked parent_u;
+ struct bch_inode_unpacked dir_u;
struct bch_inode_unpacked inode;
- int ret = bch2_trans_commit_do(c, NULL, NULL, 0,
- bch2_link_trans(trans,
- (subvol_inum) { 1, parent->bi_inum }, &parent_u,
- (subvol_inum) { 1, inum }, &inode, &qstr));
+ subvol_inum old_inum;
+ int ret = bch2_dirent_lookup(c, dir_inum, &dir_hash, &qstr, &old_inum);
+ if (bch2_err_matches(ret, ENOENT))
+ goto create;
if (ret)
- die("error creating hardlink: %s", bch2_err_str(ret));
+ return ret;
+
+ if (subvol_inum_eq(inum, old_inum))
+ return 0;
+
+ ret = unlink_and_rm(c, dir_inum, dir, name);
+ if (ret)
+ return ret;
+create:
+ ret = bch2_trans_commit_do(c, NULL, NULL, 0,
+ bch2_link_trans(trans,
+ dir_inum, &dir_u,
+ inum, &inode, &qstr));
+ bch_err_msg(c, ret, "error creating hardlink %s", name);
+ return ret;
}
-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)
+static struct bch_inode_unpacked create_or_update_file(struct bch_fs *c,
+ subvol_inum dir_inum,
+ struct bch_inode_unpacked *dir,
+ 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 bch_hash_info dir_hash = bch2_hash_info_init(c, dir);
- bch2_inode_init_early(c, &new_inode);
+ struct qstr qname = QSTR(name);
+ struct bch_inode_unpacked child_inode;
+ subvol_inum child_inum;
- int ret = bch2_trans_commit_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 %s: %s", name, bch2_err_str(ret));
+ int ret = bch2_dirent_lookup(c, dir_inum, &dir_hash,
+ &qname, &child_inum);
+ if (!ret) {
+ /* Already exists, update */
+
+ ret = bch2_inode_find_by_inum(c, child_inum, &child_inode);
+ bch_err_fn(c, ret);
+ if (ret)
+ die("error looking up %s: %s", name, bch2_err_str(ret));
- return new_inode;
+ BUG_ON(mode_to_type(child_inode.bi_mode) !=
+ mode_to_type(mode));
+
+ child_inode.bi_mode = mode;
+ child_inode.bi_uid = uid;
+ child_inode.bi_gid = gid;
+ child_inode.bi_dev = rdev;
+
+ ret = bch2_trans_run(c, bch2_fsck_write_inode(trans, &child_inode));
+ if (ret)
+ die("error updating up %s: %s", name, bch2_err_str(ret));
+ } else {
+ bch2_inode_init_early(c, &child_inode);
+
+ int ret = bch2_trans_commit_do(c, NULL, NULL, 0,
+ bch2_create_trans(trans,
+ dir_inum, dir,
+ &child_inode, &qname,
+ uid, gid, mode, rdev, NULL, NULL,
+ (subvol_inum) {}, 0));
+ if (ret)
+ die("error creating %s: %s", name, bch2_err_str(ret));
+ }
+
+ return child_inode;
}
#define for_each_xattr_handler(handlers, handler) \
@@ -93,7 +165,7 @@ static const struct xattr_handler *xattr_resolve_name(char **name)
return ERR_PTR(-EOPNOTSUPP);
}
-void copy_times(struct bch_fs *c, struct bch_inode_unpacked *dst,
+static void copy_times(struct bch_fs *c, struct bch_inode_unpacked *dst,
struct stat *src)
{
dst->bi_atime = timespec_to_bch2_time(c, src->st_atim);
@@ -101,7 +173,7 @@ void copy_times(struct bch_fs *c, struct bch_inode_unpacked *dst,
dst->bi_ctime = timespec_to_bch2_time(c, src->st_ctim);
}
-void copy_xattrs(struct bch_fs *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 = bch2_hash_info_init(c, dst);
@@ -139,11 +211,51 @@ void copy_xattrs(struct bch_fs *c, struct bch_inode_unpacked *dst,
#define WRITE_DATA_BUF (1 << 20)
-static char buf[WRITE_DATA_BUF] __aligned(PAGE_SIZE);
+static char src_buf[WRITE_DATA_BUF] __aligned(PAGE_SIZE);
+static char dst_buf[WRITE_DATA_BUF] __aligned(PAGE_SIZE);
+
+static void read_data_endio(struct bio *bio)
+{
+ closure_put(bio->bi_private);
+}
+
+static void read_data(struct bch_fs *c,
+ subvol_inum inum,
+ struct bch_inode_unpacked *inode,
+ u64 offset, void *buf, size_t len)
+{
+ BUG_ON(offset & (block_bytes(c) - 1));
+ BUG_ON(len & (block_bytes(c) - 1));
+ BUG_ON(len > WRITE_DATA_BUF);
+
+ struct closure cl;
+ closure_init_stack(&cl);
+
+ struct bch_read_bio rbio;
+ struct bio_vec bv[WRITE_DATA_BUF / PAGE_SIZE];
+
+ bio_init(&rbio.bio, NULL, bv, ARRAY_SIZE(bv), 0);
+ rbio.bio.bi_opf = REQ_OP_READ|REQ_SYNC;
+ rbio.bio.bi_iter.bi_sector = offset >> 9;
+ rbio.bio.bi_private = &cl;
+ bch2_bio_map(&rbio.bio, buf, len);
+
+ struct bch_io_opts opts;
+ bch2_inode_opts_get(&opts, c, inode);
+
+ rbio_init(&rbio.bio, c, opts, read_data_endio);
+
+ closure_get(&cl);
+ bch2_read(c, &rbio, inum);
+ closure_sync(&cl);
+
+ if (rbio.ret)
+ die("read error: %s", bch2_err_str(rbio.ret));
+}
static void write_data(struct bch_fs *c,
struct bch_inode_unpacked *dst_inode,
- u64 dst_offset, void *buf, size_t len)
+ u64 dst_offset, void *buf, size_t len, u64 new_i_size)
{
struct bch_write_op op;
struct bio_vec bv[WRITE_DATA_BUF / PAGE_SIZE];
@@ -161,6 +273,7 @@ static void write_data(struct bch_fs *c,
op.subvol = 1;
op.pos = SPOS(dst_inode->bi_inum, dst_offset >> 9, U32_MAX);
op.flags |= BCH_WRITE_sync|BCH_WRITE_only_specified_devs;
+ op.new_i_size = new_i_size;
int ret = bch2_disk_reservation_get(c, &op.res, len >> 9,
c->opts.data_replicas, 0);
@@ -170,7 +283,7 @@ static void write_data(struct bch_fs *c,
closure_call(&op.cl, bch2_write, NULL, NULL);
BUG_ON(!(op.flags & BCH_WRITE_submitted));
- dst_inode->bi_sectors += len >> 9;
+ dst_inode->bi_sectors += op.i_sectors_delta;
if (op.error)
die("write error: %s", bch2_err_str(op.error));
@@ -181,13 +294,13 @@ static void copy_data(struct bch_fs *c,
int src_fd, u64 start, u64 end)
{
while (start < end) {
- unsigned len = min_t(u64, end - start, sizeof(buf));
+ unsigned len = min_t(u64, end - start, sizeof(src_buf));
unsigned pad = round_up(len, block_bytes(c)) - len;
- xpread(src_fd, buf, len, start);
- memset(buf + len, 0, pad);
+ xpread(src_fd, src_buf, len, start);
+ memset(src_buf + len, 0, pad);
- write_data(c, dst_inode, start, buf, len + pad);
+ write_data(c, dst_inode, start, src_buf, len + pad, start + len);
start += len;
}
}
@@ -249,24 +362,32 @@ static void link_data(struct bch_fs *c, struct bch_inode_unpacked *dst,
}
}
-void copy_link(struct bch_fs *c, struct bch_inode_unpacked *dst,
+static void copy_link(struct bch_fs *c,
+ subvol_inum dst_inum,
+ struct bch_inode_unpacked *dst,
char *src)
{
- ssize_t i;
- ssize_t ret = readlink(src, buf, sizeof(buf));
+ s64 i_sectors_delta = 0;
+ int ret = bch2_fpunch(c, dst_inum, 0, U64_MAX, &i_sectors_delta);
+ if (ret)
+ die("bch2_fpunch error: %s", bch2_err_str(ret));
+
+ dst->bi_sectors += i_sectors_delta;
+
+ ret = readlink(src, src_buf, sizeof(src_buf));
if (ret < 0)
die("readlink error: %m");
- for (i = ret; i < round_up(ret, block_bytes(c)); i++)
- buf[i] = 0;
+ for (unsigned i = ret; i < round_up(ret, block_bytes(c)); i++)
+ src_buf[i] = 0;
- write_data(c, dst, 0, buf, round_up(ret, block_bytes(c)));
+ write_data(c, dst, 0, src_buf, round_up(ret, block_bytes(c)), ret);
}
-static void copy_file(struct bch_fs *c, struct bch_inode_unpacked *dst,
- int src_fd, u64 src_size,
- char *src_path, struct copy_fs_state *s,
- u64 reserve_start)
+static void link_file_data(struct bch_fs *c,
+ struct copy_fs_state *s,
+ struct bch_inode_unpacked *dst,
+ int src_fd, char *src_path, u64 src_size)
{
struct fiemap_iter iter;
struct fiemap_extent e;
@@ -279,10 +400,14 @@ static void copy_file(struct bch_fs *c, struct bch_inode_unpacked *dst,
fiemap_iter_exit(&iter);
fiemap_for_each(src_fd, iter, e) {
+ s->total_input += e.fe_length;
+
u64 src_max = roundup(src_size, block_bytes(c));
e.fe_length = min(e.fe_length, src_max - e.fe_logical);
+ unsigned visible_len = min(src_size - e.fe_logical, e.fe_length);
+
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);
@@ -292,16 +417,16 @@ static void copy_file(struct bch_fs *c, struct bch_inode_unpacked *dst,
FIEMAP_EXTENT_NOT_ALIGNED|
FIEMAP_EXTENT_DATA_INLINE))) {
copy_data(c, dst, src_fd, e.fe_logical,
- e.fe_logical + min(src_size - e.fe_logical,
- e.fe_length));
+ e.fe_logical + visible_len);
+ s->total_wrote += visible_len;
continue;
}
/* If the data is in bcachefs's superblock region, copy it: */
- if (e.fe_physical < reserve_start) {
+ if (e.fe_physical < s->reserve_start) {
copy_data(c, dst, src_fd, e.fe_logical,
- e.fe_logical + min(src_size - e.fe_logical,
- e.fe_length));
+ e.fe_logical + visible_len);
+ s->total_wrote += visible_len;
continue;
}
@@ -310,10 +435,146 @@ static void copy_file(struct bch_fs *c, struct bch_inode_unpacked *dst,
range_add(&s->extents, e.fe_physical, e.fe_length);
link_data(c, dst, e.fe_logical, e.fe_physical, e.fe_length);
+ s->total_linked += e.fe_length;
}
fiemap_iter_exit(&iter);
}
+static struct range align_range(struct range r, unsigned bs)
+{
+ r.start = round_down(r.start, bs);
+ r.end = round_up(r.end, bs);
+ return r;
+}
+
+struct range seek_data(int fd, u64 i_size, loff_t o)
+{
+ s64 s = lseek(fd, o, SEEK_DATA);
+ if (s < 0 && errno == ENXIO)
+ return (struct range) {};
+ if (s < 0)
+ die("lseek error: %m");
+
+ s64 e = lseek(fd, s, SEEK_HOLE);
+ if (e < 0 && errno == ENXIO)
+ e = i_size;
+ if (e < 0)
+ die("lseek error: %m");
+
+ return (struct range) { s, e };
+}
+
+static struct range seek_data_aligned(int fd, u64 i_size, loff_t o, unsigned bs)
+{
+ struct range r = align_range(seek_data(fd, i_size, o), bs);
+ if (!r.end)
+ return r;
+
+ while (true) {
+ struct range n = align_range(seek_data(fd, i_size, r.end), bs);
+ if (!n.end || r.end < n.start)
+ break;
+
+ r.end = n.end;
+ }
+
+ return r;
+}
+
+struct range seek_mismatch(const char *buf1, const char *buf2,
+ unsigned o, unsigned len)
+{
+ while (o < len && buf1[o] == buf2[o])
+ o++;
+
+ if (o == len)
+ return (struct range) {};
+
+ unsigned s = o;
+ while (o < len && buf1[o] != buf2[o])
+ o++;
+
+ return (struct range) { s, o };
+}
+
+static struct range seek_mismatch_aligned(const char *buf1, const char *buf2,
+ unsigned offset, unsigned len,
+ unsigned bs)
+{
+ struct range r = align_range(seek_mismatch(buf1, buf2, offset, len), bs);
+ if (r.end)
+ while (true) {
+ struct range n = align_range(seek_mismatch(buf1, buf2, r.end, len), bs);
+ if (!n.end || r.end < n.start)
+ break;
+
+ r.end = n.end;
+ }
+
+ return r;
+}
+
+static void copy_sync_file_range(struct bch_fs *c,
+ struct copy_fs_state *s,
+ subvol_inum dst_inum,
+ struct bch_inode_unpacked *dst,
+ int src_fd, u64 src_size,
+ struct range r)
+{
+ while (r.start != r.end) {
+ BUG_ON(r.start > r.end);
+
+ unsigned b = min(r.end - r.start, WRITE_DATA_BUF);
+
+ memset(src_buf, 0, b);
+ xpread(src_fd, src_buf, min(b, src_size - r.start), r.start);
+
+ read_data(c, dst_inum, dst, r.start, dst_buf, b);
+
+ struct range m = {};
+ while ((m = seek_mismatch_aligned(src_buf, dst_buf,
+ m.end, b, c->opts.block_size)).end) {
+ write_data(c, dst, r.start + m.start,
+ src_buf + m.start, m.end - m.start, min_t(u64, m.end, src_size));
+ s->total_wrote += m.end - m.start;
+ }
+
+ r.start += b;
+ }
+}
+
+static void copy_sync_file_data(struct bch_fs *c,
+ struct copy_fs_state *s,
+ subvol_inum dst_inum,
+ struct bch_inode_unpacked *dst,
+ int src_fd, u64 src_size)
+{
+ s64 i_sectors_delta = 0;
+
+ struct range next, prev = {};
+
+ while ((next = seek_data_aligned(src_fd, src_size, prev.end, c->opts.block_size)).end) {
+ if (next.start) {
+ BUG_ON(prev.end >= next.start);
+
+ int ret = bch2_fpunch(c, dst_inum, prev.end >> 9, next.start >> 9, &i_sectors_delta);
+ if (ret)
+ die("bch2_fpunch error: %s", bch2_err_str(ret));
+ }
+
+ copy_sync_file_range(c, s, dst_inum, dst, src_fd, src_size, next);
+
+ s->total_input += next.end - next.start;
+
+ prev = next;
+ }
+
+ /* end of file, truncate remaining */
+ int ret = bch2_fpunch(c, dst_inum, prev.end >> 9, U64_MAX, &i_sectors_delta);
+ if (ret)
+ die("bch2_fpunch error: %s", bch2_err_str(ret));
+}
+
static int dirent_cmp(const void *_l, const void *_r)
{
const struct dirent *l = _l;
@@ -323,15 +584,131 @@ static int dirent_cmp(const void *_l, const void *_r)
strcmp(l->d_name, r->d_name);
}
-static void copy_dir(struct copy_fs_state *s,
- struct bch_fs *c,
- struct bch_inode_unpacked *dst,
- int src_fd, const char *src_path,
- u64 reserve_start)
+typedef DARRAY(struct dirent) dirents;
+
+struct readdir_out {
+ struct dir_context ctx;
+ dirents *dirents;
+};
+
+static int readdir_actor(struct dir_context *ctx, const char *name, int name_len,
+ loff_t pos, u64 inum, unsigned type)
+{
+ struct readdir_out *out = container_of(ctx, struct readdir_out, ctx);
+
+ struct dirent d = {
+ .d_ino = inum,
+ .d_type = type,
+ };
+ memcpy(d.d_name, name, name_len);
+ d.d_name[name_len] = '\0';
+
+ return darray_push(out->dirents, d);
+}
+
+static int simple_readdir(struct bch_fs *c,
+ subvol_inum dir_inum,
+ struct bch_inode_unpacked *dir,
+ dirents *dirents)
+{
+ darray_init(dirents);
+
+ struct bch_hash_info hash_info = bch2_hash_info_init(c, dir);
+ struct readdir_out dst_dirents = { .ctx.actor = readdir_actor, .dirents = dirents };
+
+ int ret = bch2_readdir(c, dir_inum, &hash_info, &dst_dirents.ctx);
+ bch_err_fn(c, ret);
+ if (ret) {
+ darray_exit(dirents);
+ return ret;
+ }
+
+ sort(dirents->data, dirents->nr, sizeof(dirents->data[0]), dirent_cmp, NULL);
+ return 0;
+}
+
+static int recursive_remove(struct bch_fs *c,
+ subvol_inum dir_inum,
+ struct bch_inode_unpacked *dir,
+ struct dirent *d)
{
+ subvol_inum child_inum = dir_inum;
+ child_inum.inum = d->d_ino;
+
+ struct bch_inode_unpacked child;
+ int ret = bch2_inode_find_by_inum(c, child_inum, &child);
+ bch_err_msg(c, ret, "looking up inode for %s", d->d_name);
+ if (ret)
+ return ret;
+
+ if (S_ISDIR(child.bi_mode)) {
+ dirents child_dirents;
+ ret = simple_readdir(c, child_inum, &child, &child_dirents);
+ if (ret)
+ return ret;
+
+ darray_for_each(child_dirents, i) {
+ ret = recursive_remove(c, child_inum, &child, i);
+ if (ret) {
+ darray_exit(&child_dirents);
+ return ret;
+ }
+ }
+
+ darray_exit(&child_dirents);
+ }
+
+ return unlink_and_rm(c, dir_inum, dir, d->d_name);
+}
+
+static int delete_non_matching_dirents(struct bch_fs *c,
+ struct copy_fs_state *s,
+ subvol_inum dst_dir_inum,
+ struct bch_inode_unpacked *dst_dir,
+ dirents src_dirents)
+{
+ /* Assumes single subvolume */
+
+ dirents dst_dirents;
+ int ret = simple_readdir(c, dst_dir_inum, dst_dir, &dst_dirents);
+ if (ret)
+ return ret;
+
+ struct dirent *src_d = src_dirents.data;
+ darray_for_each(dst_dirents, dst_d) {
+ while (src_d < &darray_top(src_dirents) &&
+ dirent_cmp(src_d, dst_d) < 0)
+ src_d++;
+
+ if (src_d == &darray_top(src_dirents) ||
+ dirent_cmp(src_d, dst_d)) {
+ if (subvol_inum_eq(dst_dir_inum, BCACHEFS_ROOT_SUBVOL_INUM) &&
+ !strcmp(dst_d->d_name, "lost+found"))
+ continue;
+
+ if (s->verbosity > 1)
+ printf("deleting %s\n", dst_d->d_name);
+
+ ret = recursive_remove(c, dst_dir_inum, dst_dir, dst_d);
+ if (ret)
+ goto err;
+ }
+ }
+err:
+ darray_exit(&dst_dirents);
+ return ret;
+}
+
+static int copy_dir(struct bch_fs *c,
+ struct copy_fs_state *s,
+ struct bch_inode_unpacked *dst,
+ int src_fd, const char *src_path)
+{
+ lseek(src_fd, 0, SEEK_SET);
+
DIR *dir = fdopendir(src_fd);
struct dirent *d;
- DARRAY(struct dirent) dirents = {};
+ dirents dirents = {};
while ((errno = 0), (d = readdir(dir)))
darray_push(&dirents, *d);
@@ -341,6 +718,11 @@ static void copy_dir(struct copy_fs_state *s,
sort(dirents.data, dirents.nr, sizeof(dirents.data[0]), dirent_cmp, NULL);
+ subvol_inum dir_inum = { 1, dst->bi_inum };
+ int ret = delete_non_matching_dirents(c, s, dir_inum, dst, dirents);
+ if (ret)
+ goto err;
+
darray_for_each(dirents, d) {
struct bch_inode_unpacked inode;
int fd;
@@ -359,48 +741,61 @@ static void copy_dir(struct copy_fs_state *s,
if (BCH_MIGRATE_migrate == s->type && stat.st_ino == s->bcachefs_inum)
continue;
+ s->total_files++;
+
char *child_path = mprintf("%s/%s", src_path, d->d_name);
if (s->type == BCH_MIGRATE_migrate && stat.st_dev != s->dev)
die("%s does not have correct st_dev!", child_path);
- u64 *dst_inum = S_ISREG(stat.st_mode)
+ u64 *dst_inum_p = S_ISREG(stat.st_mode)
? genradix_ptr_alloc(&s->hardlinks, stat.st_ino, GFP_KERNEL)
: NULL;
- if (dst_inum && *dst_inum) {
- create_link(c, dst, d->d_name, *dst_inum, S_IFREG);
+ subvol_inum dst_dir_inum = { 1, dst->bi_inum };
+
+ if (dst_inum_p && *dst_inum_p) {
+ ret = create_or_update_link(c, dst_dir_inum, dst, d->d_name,
+ (subvol_inum) { 1, *dst_inum_p }, S_IFREG);
+ if (ret)
+ goto err;
goto next;
}
- inode = create_file(c, dst, d->d_name,
+ inode = create_or_update_file(c, dst_dir_inum, dst, d->d_name,
stat.st_uid, stat.st_gid,
stat.st_mode, stat.st_rdev);
- if (dst_inum)
- *dst_inum = inode.bi_inum;
+ subvol_inum dst_child_inum = { 1, inode.bi_inum };
- copy_xattrs(c, &inode, d->d_name);
+ if (dst_inum_p)
+ *dst_inum_p = inode.bi_inum;
- /* copy xattrs */
+ copy_xattrs(c, &inode, d->d_name);
switch (mode_to_type(stat.st_mode)) {
case DT_DIR:
fd = xopen(d->d_name, O_RDONLY|O_NOATIME);
- copy_dir(s, c, &inode, fd, child_path, reserve_start);
+ ret = copy_dir(c, s, &inode, fd, child_path);
+ if (ret)
+ goto err;
break;
case DT_REG:
inode.bi_size = stat.st_size;
fd = xopen(d->d_name, O_RDONLY|O_NOATIME);
- copy_file(c, &inode, fd, stat.st_size,
- child_path, s, reserve_start);
+ if (s->type == BCH_MIGRATE_migrate)
+ link_file_data(c, s, &inode,
+ fd, child_path, stat.st_size);
+ else
+ copy_sync_file_data(c, s, dst_child_inum, &inode,
+ fd, stat.st_size);
xclose(fd);
break;
case DT_LNK:
inode.bi_size = stat.st_size;
- copy_link(c, &inode, d->d_name);
+ copy_link(c, dst_child_inum, &inode, d->d_name);
break;
case DT_FIFO:
case DT_CHR:
@@ -418,9 +813,10 @@ static void copy_dir(struct copy_fs_state *s,
next:
free(child_path);
}
-
+err:
darray_exit(&dirents);
closedir(dir);
+ return ret;
}
static void reserve_old_fs_space(struct bch_fs *c,
@@ -433,7 +829,9 @@ static void reserve_old_fs_space(struct bch_fs *c,
struct hole_iter iter;
struct range i;
- dst = create_file(c, root_inode, "old_migrated_filesystem",
+ subvol_inum root_inum = { 1, root_inode->bi_inum };
+ dst = create_or_update_file(c, root_inum, root_inode,
+ "old_migrated_filesystem",
0, 0, S_IFREG|0400, 0);
dst.bi_size = bucket_to_sector(ca, ca->mi.nbuckets) << 9;
@@ -451,8 +849,8 @@ static void reserve_old_fs_space(struct bch_fs *c,
update_inode(c, &dst);
}
-void copy_fs(struct bch_fs *c, int src_fd, const char *src_path,
- struct copy_fs_state *s, u64 reserve_start)
+int copy_fs(struct bch_fs *c, struct copy_fs_state *s,
+ int src_fd, const char *src_path)
{
if (!S_ISDIR(xfstat(src_fd).st_mode))
die("%s is not a directory", src_path);
@@ -463,8 +861,9 @@ void copy_fs(struct bch_fs *c, int src_fd, const char *src_path,
struct bch_inode_unpacked root_inode;
int ret = bch2_inode_find_by_inum(c, (subvol_inum) { 1, BCACHEFS_ROOT_INO },
&root_inode);
+ bch_err_msg(c, ret, "looking up root directory");
if (ret)
- die("error looking up root directory: %s", bch2_err_str(ret));
+ return ret;
if (fchdir(src_fd))
die("fchdir error: %m");
@@ -474,13 +873,41 @@ void copy_fs(struct bch_fs *c, int src_fd, const char *src_path,
copy_xattrs(c, &root_inode, ".");
/* now, copy: */
- copy_dir(s, c, &root_inode, dup(src_fd), src_path, reserve_start);
+ ret = copy_dir(c, s, &root_inode, dup(src_fd), src_path);
+ bch_err_msg(c, ret, "copying filesystem");
+ if (ret)
+ return ret;
if (s->type == BCH_MIGRATE_migrate)
- reserve_old_fs_space(c, &root_inode, &s->extents, reserve_start);
+ reserve_old_fs_space(c, &root_inode, &s->extents, s->reserve_start);
update_inode(c, &root_inode);
darray_exit(&s->extents);
genradix_free(&s->hardlinks);
+
+ CLASS(printbuf, buf)();
+ printbuf_tabstop_push(&buf, 24);
+ printbuf_tabstop_push(&buf, 16);
+ prt_printf(&buf, "Total files:\t%llu\r\n", s->total_files);
+ prt_str_indented(&buf, "Total input:\t");
+ prt_human_readable_u64(&buf, s->total_input);
+ prt_printf(&buf, "\r\n");
+
+ if (s->total_wrote) {
+ prt_str_indented(&buf, "Wrote:\t");
+ prt_human_readable_u64(&buf, s->total_wrote);
+ prt_printf(&buf, "\r\n");
+ }
+
+ if (s->total_linked) {
+ prt_str(&buf, "Linked:\t");
+ prt_human_readable_u64(&buf, s->total_linked);
+ prt_printf(&buf, "\r\n");
+ }
+
+ prt_newline(&buf);
+
+ fputs(buf.buf, stdout);
+ return 0;
}