diff options
author | fishsoupisgood <github@madingley.org> | 2018-05-14 02:43:47 +0100 |
---|---|---|
committer | fishsoupisgood <github@madingley.org> | 2018-05-14 02:43:47 +0100 |
commit | a3938099758121b6cb8c3606108c8e479b1272ec (patch) | |
tree | 30c5aee1b235cc354556f3970d10b1e447a3877c /src | |
parent | ec9eb54d2f36f9c98607b5fc8745f978cfebd63e (diff) | |
download | ext_clone-master.tar.gz ext_clone-master.tar.bz2 ext_clone-master.zip |
Diffstat (limited to 'src')
-rw-r--r-- | src/clone_blocks.c | 148 |
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; +} + + + |