aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/clone_blocks.c148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/clone_blocks.c b/src/clone_blocks.c
new file mode 100644
index 0000000..362cc55
--- /dev/null
+++ b/src/clone_blocks.c
@@ -0,0 +1,148 @@
+#include "project.h"
+
+
+typedef struct {
+ ext2_filsys fs;
+ ext2_ino_t i_num;
+ struct ext2_inode *dst_i;
+ ext2_extent_handle_t eh;
+
+ blk64_t l_start;
+ blk64_t l_len;
+
+ int ret;
+
+} context_t;
+
+static int make_extents (ext2_filsys fs, blk64_t *b_num,
+ e2_blkcnt_t blockcnt,
+ blk64_t ref_block,
+ int ref_offset,
+ void *_dst)
+{
+ context_t *dst = (context_t *)_dst;
+
+ if (dst->ret) return BLOCK_ABORT;
+
+ if (!dst->l_len)
+ dst->l_start = blockcnt;
+
+
+ if ((dst->l_start + dst->l_len) != blockcnt) {
+ dst->ret = allocate_extent (dst->fs, dst->i_num, dst->eh, dst->l_start, dst->l_len, EXT2_EXTENT_FLAGS_LEAF);
+
+ if (dst->ret) return BLOCK_ABORT;
+
+ dst->dst_i->i_blocks += dst->l_len * (dst->fs->blocksize / 512);
+
+ dst->l_len = 1;
+ dst->l_start = blockcnt;
+ } else
+ dst->l_len++;
+
+ return 0;
+}
+
+
+
+int clone_blocks (ext2_filsys src_fs, ext2_filsys dst_fs, ext2_ino_t i_num, struct ext2_inode *src_i, struct ext2_inode *dst_i)
+{
+ context_t dst;
+ struct ext2fs_extent e;
+ int ret;
+
+
+ dst.fs = dst_fs;
+ dst.i_num = i_num;
+ dst.dst_i = dst_i;
+ dst.eh = NULL;
+ dst.l_start = 0;
+ dst.l_len = 0;
+ dst.ret = 0;
+
+ do {
+
+ dst_i->i_flags |= EXT4_EXTENTS_FL;
+
+ EXT2_MOAN_FAIL (ret, ext2fs_extent_open2 (dst_fs, i_num, dst_i, &dst.eh));
+
+ if (ret)
+ break;
+
+
+ EXT2_MOAN_FAIL (ret, ext2fs_block_iterate3 (src_fs, i_num, BLOCK_FLAG_DATA_ONLY | BLOCK_FLAG_READ_ONLY, 0, make_extents, &dst));
+
+ if (ret) break;
+
+ ret = dst.ret;
+
+ if (ret) break;
+
+ if (dst.l_len) {
+ ret = allocate_extent (dst_fs, i_num, dst.eh, dst.l_start, dst.l_len, EXT2_EXTENT_FLAGS_LEAF);
+ dst_i->i_blocks += dst.l_len * (dst_fs->blocksize / 512);
+
+ if (ret) break;
+ }
+
+ EXT2_MOAN_FAIL (ret, ext2fs_write_inode (dst_fs, i_num, dst_i));
+
+ if (ret)
+ break;
+
+ ret = ext2fs_extent_get (dst.eh, EXT2_EXTENT_ROOT, &e);
+
+ if (ret == EXT2_ET_EXTENT_NO_NEXT) {
+ ret = 0;
+ break;
+ }
+
+ if (ret) {
+ EXT2_MOAN (ret, "ext2fs_extent_get (dst.eh, EXT2_EXTENT_ROOT, &e)");
+ break;
+ }
+
+
+ for (;;) {
+ uint64_t file_offset, file_len;
+
+
+ if ((e.e_flags & EXT2_EXTENT_FLAGS_LEAF) && ! (e.e_flags & EXT2_EXTENT_FLAGS_UNINIT)) {
+
+ file_offset = e.e_lblk;
+ file_len = e.e_len;
+
+ file_offset *= src_fs->blocksize;
+ file_len *= src_fs->blocksize;
+
+
+ if (clone_data (src_fs, dst_fs, i_num, src_i, dst_i, file_offset, file_len)) {
+ ret = 1;
+ break;
+ }
+ }
+
+ ret = ext2fs_extent_get (dst.eh, EXT2_EXTENT_NEXT_LEAF, &e);
+
+ if (ret == EXT2_ET_EXTENT_NO_NEXT) {
+ ret = 0;
+ break;
+ }
+
+ if (ret) {
+ EXT2_MOAN (ret, "ext2fs_extent_get (dst.eh, EXT2_EXTENT_NEXT_LEAF, &e)");
+ break;
+ }
+ }
+
+ } while (0);
+
+ if (dst.eh)
+ ext2fs_extent_free (dst.eh);
+
+
+ return ret;
+}
+
+
+