#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; }