aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am66
-rw-r--r--src/clone_data.c84
-rw-r--r--src/clone_ea.c70
-rw-r--r--src/clone_extents.c200
-rw-r--r--src/clone_fs.c52
-rw-r--r--src/clone_inode.c59
-rw-r--r--src/ea_map.c79
-rw-r--r--src/ext_clone.c99
-rw-r--r--src/project.h56
-rw-r--r--src/prototypes.h27
-rw-r--r--src/stats.c139
-rw-r--r--src/zap_fs.c52
-rw-r--r--src/zap_inode.c41
13 files changed, 1024 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..da2463f
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,66 @@
+
+CPROTO=cproto
+AM_CPPFLAGS = ${LIBE2P_INC} ${LIBEXT2FS_INC} ${LIBCOM_ERR_INC} ${LIBDB_INC}
+
+HSRCS=project.h prototypes.h
+
+noinst_HEADERS=${HSRCS}
+bin_PROGRAMS = ext_clone
+
+CSRCS= clone_data.c clone_ea.c clone_extents.c clone_inode.c ea_map.c ext_clone.c stats.c clone_fs.c zap_fs.c zap_inode.c
+
+ext_clone_SOURCES = ${CSRCS}
+ext_clone_LDADD = ${LIBE2P_LIB} ${LIBEXT2FS_LIB} ${LIBCOM_ERR_LIB} ${LIBDB_LIB} -lm
+
+AM_CFLAGS=-g -Wall
+
+ext_clone_LDFLAGS = --static
+
+BUILT_SOURCES =
+
+src.img:
+ umount src || true
+ mkdir -p src
+ rm -f src.img
+ truncate -s 128M src.img
+ mkfs.ext4 -b 4096 -O 64bit -E stride=16,stripe-width=112 src.img
+ mount src.img src
+ (cd src && git clone git://git.panaceas.org/tools/sympathy )
+ (cd src && truncate -s 8g holey && dd if=/dev/zero of=holey conv=notrunc,nocreat bs=1024k seek=4000 count=1 )
+ (cd src && ln holey linky)
+ (cd src && touch empty)
+ ls -lZR src
+ ls -lR src
+ umount src
+ rmdir src
+
+
+
+protos:
+ echo > prototypes.h
+ ${CPROTO} -v ${INCLUDES} ${CSRCS} > prototypes.tmp
+ mv -f prototypes.tmp prototypes.h
+
+test: ext_clone src.img
+ umount dst || true
+ umount src || true
+ truncate -s 256M dst.img
+ mkfs.ext4 -F dst.img -b 4096 -O 64bit -E stride=16,stripe-width=96
+ ext_clone -s src.img -d dst.img
+ e2fsck -f -y dst.img || true
+ echo "stat <123>" > cmd
+ debugfs -f cmd src.img > src.txt
+ debugfs -f cmd dst.img > dst.txt
+ diff -uN src.txt dst.txt || true
+ mkdir -p src
+ mkdir -p dst
+ mount -o ro src.img src
+ mount -o ro dst.img dst
+ rsync -varX -n src/ dst/
+ umount dst
+ umount src
+ rmdir src
+ rmdir dst
+
+tidy:
+ astyle -A3 -s2 --attach-extern-c -L -c -w -Y -m0 -f -p -H -U -k3 -xj -xd ${CSRCS} ${HSRCS}
diff --git a/src/clone_data.c b/src/clone_data.c
new file mode 100644
index 0000000..3083914
--- /dev/null
+++ b/src/clone_data.c
@@ -0,0 +1,84 @@
+#include "project.h"
+
+
+int clone_data (ext2_filsys src_fs, ext2_filsys dst_fs, ext2_ino_t i_num, struct ext2_inode *src_i, struct ext2_inode *dst_i, uint64_t offset, uint64_t len)
+{
+ int ret;
+ __u64 file_len;
+ unsigned got, written, fetch;
+ ext2_file_t src_f = NULL, dst_f = NULL;
+
+ do {
+
+ EXT2_MOAN_FAIL (ret, ext2fs_file_open2 (src_fs, i_num, src_i, 0, &src_f));
+
+ if (ret)
+ return -1;
+
+ EXT2_MOAN_FAIL (ret, ext2fs_file_open2 (dst_fs, i_num, dst_i, EXT2_FILE_WRITE, &dst_f));
+
+ if (ret)
+ return -1;
+
+
+
+ if (offset) {
+
+ EXT2_MOAN_FAIL (ret, ext2fs_file_lseek (src_f, offset, SEEK_SET, NULL));
+
+ if (ret)
+ return -1;
+
+ EXT2_MOAN_FAIL (ret, ext2fs_file_lseek (dst_f, offset, SEEK_SET, NULL));
+
+ if (ret)
+ return -1;
+ }
+
+ EXT2_MOAN_FAIL (ret, ext2fs_file_get_lsize (src_f, &file_len));
+
+ if (ret)
+ break;
+
+
+ file_len -= offset;
+
+
+ if (len > file_len)
+ len = file_len;
+
+ while (len) {
+
+ fetch = len > BUF_SZ ? BUF_SZ : len;
+
+ EXT2_MOAN_FAIL (ret, ext2fs_file_read (src_f, buf, fetch, &got));
+
+ if (ret)
+ break;
+
+ if (got) {
+ EXT2_MOAN_FAIL (ret, ext2fs_file_write (dst_f, buf, got, &written));
+
+ if (ret)
+ break;
+
+ if (written != got) {
+ fprintf (stderr, "Not all bytes written in inode %d\n", (int) i_num);
+ ret = 1;
+ break;
+ }
+ }
+
+ stats_bytes += written;
+ len -= written;
+ }
+
+ } while (0);
+
+ ext2fs_file_flush (dst_f);
+ ext2fs_file_close (dst_f);
+ ext2fs_file_close (src_f);
+ return ret;
+}
+
+
diff --git a/src/clone_ea.c b/src/clone_ea.c
new file mode 100644
index 0000000..d8287b7
--- /dev/null
+++ b/src/clone_ea.c
@@ -0,0 +1,70 @@
+#include "project.h"
+
+int clone_ea (ext2_filsys src_fs, ext2_filsys dst_fs, ext2_ino_t i_num, struct ext2_inode *src_i, struct ext2_inode *dst_i)
+{
+ int ret;
+ blk64_t src_blk = src_i->i_file_acl;
+ blk64_t goal, dst_blk, len;
+ dgrp_t group;
+
+ struct ext2_ext_attr_header *h;
+
+ if (!src_blk) return 0;
+
+ if (!ea_map_lookup (src_blk, &dst_blk, &len)) {
+ /* Easy case */
+
+ EXT2_MOAN_FAIL (ret, ext2fs_adjust_ea_refcount2 (dst_fs, dst_blk, buf, +1, NULL));
+
+ if (ret) return ret;
+
+ dst_i->i_file_acl = dst_blk;
+ dst_i->i_blocks += len * (dst_fs->blocksize / 512);
+ return 0;
+ }
+
+ EXT2_MOAN_FAIL (ret, io_channel_read_blk64 (src_fs->io, src_blk, 1, buf));
+
+ if (ret) return ret;
+
+ h = (struct ext2_ext_attr_header *) buf;
+ len = h->h_blocks;
+
+ if (len > 1) {
+ EXT2_MOAN_FAIL (ret, io_channel_read_blk64 (src_fs->io, src_blk + 1, len - 1, buf + src_fs->blocksize));
+
+ if (ret) return ret;
+ }
+
+ h->h_refcount = 0;
+
+ group = ext2fs_group_of_ino (dst_fs, i_num);
+ goal = ext2fs_group_first_block2 (dst_fs, group);
+
+ EXT2_MOAN_FAIL (ret, ext2fs_alloc_range (dst_fs, 0, goal, len, &dst_blk));
+
+ if (ret) return ret;
+
+ //printf ("EA block %d mapped to block %d len %d\n", (int)src_blk, (int) dst_blk, (int)len);
+ stats_ea_blocks++;
+
+ if (ea_map_set (src_blk, dst_blk, len))
+ return -1;
+
+
+ EXT2_MOAN_FAIL (ret, io_channel_write_blk64 (dst_fs->io, dst_blk, len, buf));
+
+ if (ret) return ret;
+
+ dst_i->i_file_acl = dst_blk & 0xffffffff;
+ dst_i->osd2.linux2.l_i_file_acl_high= dst_blk >> 32;
+ dst_i->i_blocks += len * (dst_fs->blocksize / 512);
+ EXT2_MOAN_FAIL (ret, ext2fs_adjust_ea_refcount2 (dst_fs, dst_blk, buf, +1, NULL));
+
+ if (ret) return ret;
+
+
+ return 0;
+}
+
+
diff --git a/src/clone_extents.c b/src/clone_extents.c
new file mode 100644
index 0000000..09c639b
--- /dev/null
+++ b/src/clone_extents.c
@@ -0,0 +1,200 @@
+#include "project.h"
+
+
+static int allocate_extent (ext2_filsys fs, ext2_ino_t i_num, ext2_extent_handle_t eh, blk64_t l_start, blk64_t len, __u32 flags)
+{
+ blk64_t goal, start, size, want,max;
+ dgrp_t group;
+ int ret;
+ struct ext2fs_extent e;
+
+ group = ext2fs_group_of_ino (fs, i_num);
+ goal = ext2fs_group_first_block2 (fs, group);
+
+
+ max = (flags & EXT2_EXTENT_FLAGS_UNINIT) ? EXT_UNINIT_MAX_LEN:EXT_INIT_MAX_LEN;
+
+ ret = 0;
+
+ while (len) {
+
+ want = (len > max) ? max : len;
+
+ EXT2_MOAN_FAIL (ret, ext2fs_new_range (fs, 0, goal, want,
+ fs->block_map, &start, &size));
+
+ if (ret)
+ break;
+
+ if (size > want) size = want;
+
+ ext2fs_block_alloc_stats_range (fs, start, size, +1);
+
+
+ e.e_pblk = start;
+ e.e_lblk = l_start;
+ e.e_len = size;
+ e.e_flags = flags;
+
+
+ EXT2_MOAN_FAIL (ret, ext2fs_extent_insert (eh, EXT2_EXTENT_INSERT_AFTER, &e));
+
+ if (ret) {
+
+ fprintf(stderr,"Failed extent was:\n");
+ fprintf(stderr," e.e_pblk = %lld\n",(long long int) e.e_pblk);
+ fprintf(stderr," e.e_lblk = %lld\n",(long long int) e.e_lblk);
+ fprintf(stderr," e.e_len = %lld\n",(long long int) e.e_len);
+ fprintf(stderr," e.e_flags = 0x%llx\n",(long long int) e.e_flags);
+ break;
+ }
+
+ len -= size;
+
+ }
+
+ return ret;
+}
+
+
+int clone_extents (ext2_filsys src_fs, ext2_filsys dst_fs, ext2_ino_t i_num, struct ext2_inode *src_i, struct ext2_inode *dst_i)
+{
+ ext2_extent_handle_t src_e = NULL, dst_e = NULL;
+ // struct ext2_extent_info info;
+ struct ext2fs_extent e;
+ int ret;
+ blk64_t l_start = 0;
+ blk64_t l_len = 0;
+ __u32 flags;
+
+
+ do {
+ EXT2_MOAN_FAIL (ret, ext2fs_extent_open2 (src_fs, i_num, src_i, &src_e));
+
+ if (ret)
+ break;
+
+
+ EXT2_MOAN_FAIL (ret, ext2fs_extent_open2 (dst_fs, i_num, dst_i, &dst_e));
+
+ if (ret)
+ break;
+
+
+ ret = ext2fs_extent_get (src_e, EXT2_EXTENT_ROOT, &e);
+
+ if (ret == EXT2_ET_EXTENT_NO_NEXT) {
+ EXT2_MOAN_FAIL (ret, ext2fs_write_inode (dst_fs, i_num, dst_i));
+ break;
+ }
+
+ if (ret) {
+ EXT2_MOAN (ret, "ext2fs_extent_get (src_e, EXT2_EXTENT_ROOT, &e)");
+ break;
+ }
+
+
+
+ for (;;) {
+
+ if (e.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
+
+ if (!l_len)
+ flags = e.e_flags;
+
+ if (((l_start + l_len) != e.e_lblk) || (flags != e.e_flags) ) {
+ ret = allocate_extent (dst_fs, i_num, dst_e, l_start, l_len, flags);
+
+ if (ret) break;
+
+ dst_i->i_blocks += l_len * (dst_fs->blocksize / 512);
+
+ l_start = e.e_lblk;
+ l_len = e.e_len;
+ flags = e.e_flags;
+ } else
+ l_len += e.e_len;
+
+ }
+
+ ret = ext2fs_extent_get (src_e, EXT2_EXTENT_NEXT_LEAF, &e);
+
+ if (ret == EXT2_ET_EXTENT_NO_NEXT) {
+ ret = 0;
+ break;
+ }
+
+ if (ret) {
+ EXT2_MOAN (ret, "ext2fs_extent_get (src_e, EXT2_EXTENT_NEXT_LEAF, &e)");
+ break;
+ }
+ }
+
+ if (l_len)
+ ret = allocate_extent (dst_fs, i_num, dst_e, l_start, l_len, flags);
+
+ if (ret) break;
+
+ dst_i->i_blocks += l_len * (dst_fs->blocksize / 512);
+
+ if (dst_e)
+ ext2fs_extent_free (dst_e);
+
+ dst_e = NULL;
+
+ EXT2_MOAN_FAIL (ret, ext2fs_write_inode (dst_fs, i_num, dst_i));
+
+ if (ret)
+ break;
+
+ EXT2_MOAN_FAIL (ret, ext2fs_extent_get (src_e, EXT2_EXTENT_ROOT, &e));
+
+ if (ret)
+ 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 (src_e, EXT2_EXTENT_NEXT_LEAF, &e);
+
+ if (ret == EXT2_ET_EXTENT_NO_NEXT) {
+ ret = 0;
+ break;
+ }
+
+ if (ret) {
+ EXT2_MOAN (ret, "ext2fs_extent_get (src_e, EXT2_EXTENT_NEXT_LEAF, &e)");
+ break;
+ }
+ }
+
+ } while (0);
+
+ if (dst_e)
+ ext2fs_extent_free (dst_e);
+
+ if (src_e)
+ ext2fs_extent_free (src_e);
+
+
+ return ret;
+}
+
+
+
diff --git a/src/clone_fs.c b/src/clone_fs.c
new file mode 100644
index 0000000..1269b30
--- /dev/null
+++ b/src/clone_fs.c
@@ -0,0 +1,52 @@
+#include "project.h"
+
+
+int clone_fs (ext2_filsys src_fs, ext2_filsys dst_fs)
+{
+ int skip;
+ int ret;
+ ext2_inode_scan scan;
+ ext2_ino_t i_num;
+ struct ext2_inode src_i;
+
+ EXT2_MOAN_FAIL (ret, ext2fs_open_inode_scan (src_fs, 0, &scan));
+
+ if (ret) return -1;
+
+ for (;;) {
+ EXT2_MOAN_FAIL (ret, ext2fs_get_next_inode (scan, &i_num, &src_i));
+
+ if (ret) break;
+
+ if (!i_num) break;
+
+ skip = 0;
+
+ switch (i_num) {
+ case EXT2_BAD_INO:
+ case EXT4_USR_QUOTA_INO:
+ case EXT4_GRP_QUOTA_INO:
+ case EXT2_BOOT_LOADER_INO:
+ case EXT2_UNDEL_DIR_INO:
+ case EXT2_RESIZE_INO:
+ case EXT2_EXCLUDE_INO:
+ case EXT4_REPLICA_INO:
+ skip = 1;
+ }
+
+ if (i_num == src_fs->super->s_journal_inum) skip = 1;
+
+ if (skip) continue;
+
+ if (clone_inode (src_fs, dst_fs, i_num, &src_i)) {
+ fprintf (stderr, "clone_inode(%d) failed\n", (int) i_num);
+ break;
+ }
+ }
+
+ ext2fs_close_inode_scan (scan);
+ return 0;
+}
+
+
+
diff --git a/src/clone_inode.c b/src/clone_inode.c
new file mode 100644
index 0000000..33d4647
--- /dev/null
+++ b/src/clone_inode.c
@@ -0,0 +1,59 @@
+#include "project.h"
+
+int clone_inode (ext2_filsys src_fs, ext2_filsys dst_fs, ext2_ino_t i_num, struct ext2_inode *src_i)
+{
+ struct ext2_inode dst_i;
+ int ret;
+
+ // printf ("cloning inode %d\n", (int) i_num);
+
+ EXT2_MOAN_FAIL (ret, ext2fs_read_inode (dst_fs, i_num, &dst_i));
+
+
+ memcpy (&dst_i, src_i, sizeof (*src_i));
+
+ dst_i.i_blocks = 0;
+ memset (&dst_i.i_block, 0, sizeof (dst_i.i_block));
+ dst_i.i_file_acl = 0;
+
+ dst_i.osd2.linux2.l_i_blocks_hi = 0;
+ dst_i.osd2.linux2.l_i_file_acl_high = 0;
+
+ if (src_i->i_file_acl)
+ if (clone_ea (src_fs, dst_fs, i_num, src_i, &dst_i))
+ return -1;
+
+ if (src_i->i_flags & EXT4_EXTENTS_FL) {
+ if (clone_extents (src_fs, dst_fs, i_num, src_i, &dst_i)) {
+ fprintf (stderr, "Cloning extents for inode %d failed\n", (int) i_num);
+ return -1;
+ }
+
+ } else {
+
+ EXT2_MOAN_FAIL (ret, ext2fs_write_inode (dst_fs, i_num, &dst_i));
+
+ if (clone_data (src_fs, dst_fs, i_num, src_i, &dst_i, 0, 0)) {
+ fprintf (stderr, "Cloning data for inode %d failed\n", (int) i_num);
+ return -1;
+ }
+ }
+
+ if (ret)
+ return -1;
+
+ if (dst_i.i_mode & LINUX_S_IFDIR)
+ ext2fs_inode_alloc_stats2 (dst_fs, i_num, +1, +1);
+ else
+ ext2fs_inode_alloc_stats2 (dst_fs, i_num, +1, 0);
+
+ if (ret)
+ return -1;
+
+ stats_inodes++;
+ stats (0);
+
+ return 0;
+}
+
+
diff --git a/src/ea_map.c b/src/ea_map.c
new file mode 100644
index 0000000..e8d103e
--- /dev/null
+++ b/src/ea_map.c
@@ -0,0 +1,79 @@
+#include "project.h"
+#include <db.h>
+
+static DB *ea_map_db = NULL;
+
+static int ea_map_init (void)
+{
+ int ret;
+
+ ret = db_create (&ea_map_db, NULL, 0);
+
+ if (ret) {
+ ea_map_db = NULL;
+ return -1;
+ }
+
+ ret = ea_map_db->open (ea_map_db, NULL, NULL, NULL, DB_BTREE, DB_CREATE, 0x600);
+
+ if (ret) {
+ ea_map_db->err (ea_map_db, ret, "ea_map_db");
+ ea_map_db->close (ea_map_db, 0);
+ ea_map_db = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int ea_map_lookup (blk64_t src_blk, blk64_t *dst_blk, blk64_t *len)
+{
+ DBT key = {0}, data = {0};
+ blk64_t *buf;
+
+ if (!ea_map_db)
+ return -1;
+
+ key.size = sizeof (src_blk);
+ key.data = &src_blk;
+
+ if (ea_map_db->get (ea_map_db, NULL, &key, &data, 0))
+ return -1;
+
+ if (data.size != (2 * sizeof (blk64_t)))
+ return -1;
+
+ buf = (blk64_t *) data.data;
+
+ *dst_blk = buf[0];
+ *len = buf[1];
+
+ return 0;
+}
+
+
+int ea_map_set (blk64_t src_blk, blk64_t dst_blk, blk64_t len)
+{
+ DBT key = {0}, data = {0};
+ blk64_t buf[2] = {dst_blk, len};
+ int ret;
+
+ if (!ea_map_db)
+ if (ea_map_init())
+ return -1;
+
+
+ key.size = sizeof (src_blk);
+ key.data = &src_blk;
+ data.size = 2 * sizeof (dst_blk);
+ data.data = buf;
+
+ ret = ea_map_db->put (ea_map_db, NULL, &key, &data, 0);
+
+ if (ret)
+ ea_map_db->err (ea_map_db, ret, "DB->put");
+
+ return ret;
+}
+
+
diff --git a/src/ext_clone.c b/src/ext_clone.c
new file mode 100644
index 0000000..1899455
--- /dev/null
+++ b/src/ext_clone.c
@@ -0,0 +1,99 @@
+#include "project.h"
+
+static void
+usage (void)
+{
+ fprintf (stderr, "ext_clone -s src_dev -d dest_dev\n");
+ exit (1);
+}
+
+
+char buf[BUF_SZ];
+
+
+
+int main (int argc, char *argv[])
+{
+ int ret;
+ ext2_filsys src_fs = NULL;
+ ext2_filsys dst_fs = NULL;
+ char *src_dev = NULL;
+ char *dst_dev = NULL;
+
+ int opt;
+
+ while ((opt = getopt (argc, argv, "d:s:")) != -1) {
+ switch (opt) {
+ case 'd':
+ dst_dev = optarg;
+ break;
+
+ case 's':
+ src_dev = optarg;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if ((!src_dev) || (!dst_dev)) usage();
+
+ do {
+ EXT2_MOAN_FAIL (ret, ext2fs_open (src_dev, EXT2_FLAG_64BITS, 0, 0, unix_io_manager, &src_fs));
+
+ if (ret)
+ break;
+
+ EXT2_MOAN_FAIL (ret, ext2fs_open (dst_dev, EXT2_FLAG_64BITS | EXT2_FLAG_RW, 0, 0, unix_io_manager, &dst_fs));
+
+ if (ret)
+ break;
+
+
+ /*make the hash seed the same */
+
+ memcpy (dst_fs->super->s_hash_seed, src_fs->super->s_hash_seed, sizeof (src_fs->super->s_hash_seed));
+ dst_fs->super->s_def_hash_version = src_fs->super->s_def_hash_version;
+
+ ext2fs_mark_super_dirty (dst_fs);
+
+
+
+
+ EXT2_MOAN_FAIL (ret, ext2fs_read_inode_bitmap (dst_fs));
+
+ if (ret)
+ break;
+
+ EXT2_MOAN_FAIL (ret, ext2fs_read_block_bitmap (dst_fs));
+
+ if (ret)
+ break;
+
+ ret = zap_fs (dst_fs);
+
+ if (ret)
+ break;
+
+
+ ret = clone_fs (src_fs, dst_fs);
+
+ } while (0);
+
+
+
+ if (dst_fs)
+ ext2fs_close (dst_fs);
+
+ if (src_fs)
+ ext2fs_close (src_fs);
+
+ stats (1);
+
+ return ret;
+}
+
+
+
+
diff --git a/src/project.h b/src/project.h
new file mode 100644
index 0000000..da07ded
--- /dev/null
+++ b/src/project.h
@@ -0,0 +1,56 @@
+#include "config.h"
+
+#include <math.h>
+
+#include <sys/time.h>
+#include <time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+#include <malloc.h>
+
+#include <string.h>
+#include <strings.h>
+
+#include <unistd.h>
+
+#include <stdint.h>
+
+
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <attr/xattr.h>
+#include <errno.h>
+
+
+#include <ext2fs/ext2fs.h>
+#include <e2p/e2p.h>
+
+#define BUF_SZ (16*1024*1024)
+
+#define EXT2_DO_MOAN(v,wot,file,function,line) \
+ if (v) { \
+ fprintf (stderr, "%s:%s:%d %s failed %d(%s)\n", file,function, line, wot, (v) ,error_message(v)); \
+ }
+
+
+#define EXT2_MOAN(v,wot) \
+ do { \
+ EXT2_DO_MOAN(v,wot,__FILE__,__FUNCTION__,__LINE__) \
+ } while (0)
+
+
+#define EXT2_MOAN_FAIL(v,wot) \
+ do { \
+ (v)= wot; \
+ EXT2_DO_MOAN(v,#wot,__FILE__,__FUNCTION__,__LINE__) \
+ } while (0)
+
+
+
+#include "prototypes.h"
+
diff --git a/src/prototypes.h b/src/prototypes.h
new file mode 100644
index 0000000..3ac93b7
--- /dev/null
+++ b/src/prototypes.h
@@ -0,0 +1,27 @@
+/* clone_data.c */
+int clone_data(ext2_filsys src_fs, ext2_filsys dst_fs, ext2_ino_t i_num, struct ext2_inode *src_i, struct ext2_inode *dst_i, uint64_t offset, uint64_t len);
+/* clone_ea.c */
+int clone_ea(ext2_filsys src_fs, ext2_filsys dst_fs, ext2_ino_t i_num, struct ext2_inode *src_i, struct ext2_inode *dst_i);
+/* clone_extents.c */
+int clone_extents(ext2_filsys src_fs, ext2_filsys dst_fs, ext2_ino_t i_num, struct ext2_inode *src_i, struct ext2_inode *dst_i);
+/* clone_inode.c */
+int clone_inode(ext2_filsys src_fs, ext2_filsys dst_fs, ext2_ino_t i_num, struct ext2_inode *src_i);
+/* ea_map.c */
+int ea_map_lookup(blk64_t src_blk, blk64_t *dst_blk, blk64_t *len);
+int ea_map_set(blk64_t src_blk, blk64_t dst_blk, blk64_t len);
+/* ext_clone.c */
+char buf[(16*1024*1024)];
+int main(int argc, char *argv[]);
+/* stats.c */
+uint64_t stats_inodes;
+uint64_t stats_ea_blocks;
+uint64_t stats_bytes;
+struct timeval start_time;
+struct timeval last_time;
+void stats(int force);
+/* clone_fs.c */
+int clone_fs(ext2_filsys src_fs, ext2_filsys dst_fs);
+/* zap_fs.c */
+int zap_fs(ext2_filsys fs);
+/* zap_inode.c */
+int zap_inode(ext2_filsys fs, ext2_ino_t i_num, struct ext2_inode *i);
diff --git a/src/stats.c b/src/stats.c
new file mode 100644
index 0000000..1fb7c03
--- /dev/null
+++ b/src/stats.c
@@ -0,0 +1,139 @@
+#include "project.h"
+
+uint64_t stats_inodes;
+uint64_t stats_ea_blocks;
+uint64_t stats_bytes;
+static uint64_t last_bytes;
+
+
+struct timeval start_time, last_time;
+
+static int first = 1;
+
+
+
+static int do_time (char *ptr, double t)
+{
+ double h, m, s;
+
+ if (t < 60.0)
+ return sprintf (ptr, "%.1fs", t);
+
+ if (t < 3600.0) {
+ m = floor (t / 60.0);
+ t -= 60 * m;
+ s = floor (t);
+ t -= s;
+ t *= 10;
+ return sprintf (ptr, "%d:%02d.%dm", (int) m, (int) s, (int) t);
+ }
+
+ h = floor (t / 3600.0);
+ t -= 3600 * h;
+ m = floor (t / 60.0);
+ t -= 60 * m;
+ s = floor (t);
+ t -= s;
+ t *= 10;
+ return sprintf (ptr, "%d:%02d:%02d.%dh", (int) h, (int) m, (int) s,
+ (int) t);
+}
+
+static int do_rate (char *ptr, double r)
+{
+ if (r < 1024.0)
+ return sprintf (ptr, "%.1f b", r);
+
+ if (r < 1048576.0)
+ return sprintf (ptr, "%.1f K", r / 1024.0);
+
+ if (r < 1073741824.0)
+ return sprintf (ptr, "%.1f M", r / 1048576.0);
+
+ return sprintf (ptr, "%.1f G", r / 1073741824.0);
+}
+
+
+void stats (int force)
+{
+ struct timeval now, diff1, diff2;
+ char charbuf[128], *ptr = charbuf;
+ double totaltime, totalbytes, intertime, interbytes;
+
+ gettimeofday (&now, NULL);
+
+
+ if (first) {
+ first = 0;
+ last_time = now;
+ start_time = now;
+ return;
+ }
+
+
+ timersub (&now, &last_time, &diff1);
+
+ if ((!force) && (diff1.tv_sec < 10))
+ return;
+
+ timersub (&now, &start_time, &diff2);
+
+ intertime = (double) diff1.tv_usec;
+ intertime /= 1000000.0;
+ intertime += (double) diff1.tv_sec;
+
+ totaltime = (double) diff2.tv_usec;
+ totaltime /= 1000000.0;
+ totaltime += (double) diff2.tv_sec;
+
+ totalbytes = (double) stats_bytes;
+ interbytes = stats_bytes - (double) last_bytes;
+
+ last_bytes = stats_bytes;
+ last_time = now;
+
+ intertime += 0.001;
+ totaltime += 0.001;
+
+ ptr += do_time (ptr, totaltime);
+ ptr += sprintf (ptr, " rate:");
+ ptr += do_rate (ptr, interbytes / intertime);
+ ptr += sprintf (ptr, "/");
+ ptr += do_rate (ptr, totalbytes / totaltime);
+ ptr += sprintf (ptr, " bytes:");
+ ptr += do_rate (ptr, totalbytes);
+ ptr += sprintf (ptr, " inodes %lld ea_blocks %lld\n",
+ (long long int) stats_inodes, (long long int) stats_ea_blocks);
+ fputs (charbuf, stderr);
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/zap_fs.c b/src/zap_fs.c
new file mode 100644
index 0000000..0cfd7ac
--- /dev/null
+++ b/src/zap_fs.c
@@ -0,0 +1,52 @@
+#include "project.h"
+
+
+int zap_fs (ext2_filsys fs)
+{
+ int skip;
+ int ret;
+ ext2_inode_scan scan;
+ ext2_ino_t i_num;
+ struct ext2_inode i;
+
+ EXT2_MOAN_FAIL (ret, ext2fs_open_inode_scan (fs, 0, &scan));
+
+ if (ret) return -1;
+
+ for (;;) {
+ EXT2_MOAN_FAIL (ret, ext2fs_get_next_inode (scan, &i_num, &i));
+
+ if (ret) break;
+
+ if (!i_num) break;
+
+ skip = 0;
+
+ switch (i_num) {
+ case EXT2_BAD_INO:
+ case EXT4_USR_QUOTA_INO:
+ case EXT4_GRP_QUOTA_INO:
+ case EXT2_BOOT_LOADER_INO:
+ case EXT2_UNDEL_DIR_INO:
+ case EXT2_RESIZE_INO:
+ case EXT2_EXCLUDE_INO:
+ case EXT4_REPLICA_INO:
+ skip = 1;
+ }
+
+ if (i_num == fs->super->s_journal_inum) skip = 1;
+
+ if (skip) continue;
+
+ if (zap_inode (fs, i_num, &i)) {
+ fprintf (stderr, "zap_inode(%d) failed\n", (int) i_num);
+ break;
+ }
+ }
+
+ ext2fs_close_inode_scan (scan);
+ return 0;
+}
+
+
+
diff --git a/src/zap_inode.c b/src/zap_inode.c
new file mode 100644
index 0000000..f40a16b
--- /dev/null
+++ b/src/zap_inode.c
@@ -0,0 +1,41 @@
+#include "project.h"
+
+static int clear_blocks (ext2_filsys fs, blk_t *b_num,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block,
+ int ref_offset,
+ void *priv_data)
+{
+ ext2fs_block_alloc_stats2 (fs, *b_num, -1);
+
+ *b_num = 0;
+ return BLOCK_CHANGED;
+}
+
+
+
+
+int zap_inode (ext2_filsys fs, ext2_ino_t i_num, struct ext2_inode *i)
+{
+ int ret;
+
+ printf ("clearing already used inode %d\n", (int) i_num);
+
+ EXT2_MOAN_FAIL (ret, ext2fs_block_iterate2 (fs, i_num, BLOCK_FLAG_DEPTH_TRAVERSE, 0, clear_blocks, NULL));
+
+ if (ret) return -1;
+
+ if (i->i_mode & LINUX_S_IFDIR)
+ ext2fs_inode_alloc_stats2 (fs, i_num, -1, -1);
+ else
+ ext2fs_inode_alloc_stats2 (fs, i_num, -1, 0);
+
+ memset (i, 0, sizeof (*i));
+
+ EXT2_MOAN_FAIL (ret, ext2fs_write_inode (fs, i_num, i));
+
+ return ret;
+}
+
+
+