diff options
| -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; +} + + + | 
