diff --git a/Kconfig b/Kconfig
new file mode 100644
index 0000000..7564fcc
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,95 @@
+config SNAPSHOT_ON_DISK
+	bool "Snapshots on-disk format changes"
+	help
+	  Snapshots uses a few reserved fields and flags in the Ext2 super block,
+	  and inode structs.
+	  Snapshots require the compatible feature 'exclude_bitmap', meaning that
+	  the exclude bitmap was allocated.
+	  snapshots require the read-only compatible features 'has_snapshot',
+	  meaning that the file system may contain snapshots.
+
+config SNAPSHOT_BIG_JOURNAL
+	bool "Create a big journal for COW operations"
+	help
+	  COW transactions reserve up to 24 times more credits than traditional
+	  transactions for the same operation.  On mke2fs and tune2fs, if the
+	  '-J big' option is used to create a journal, increase the default
+	  journal size by a factor of 24.
+
+config SNAPSHOT_HAS_SNAPSHOT
+	bool "Avoid offline modifications to a file system with snapshots"
+	help
+	  Snapshots require the read-only compatible feature 'has_snapshot',
+	  so the file system will be mounted by old kernels in read-only mode
+	  to protect the snapshots.
+	  Fsck displays a warning about possible corruption of the snapshots
+	  in interactive mode and avoids freeing blocks in preen mode.
+
+config SNAPSHOT_EXCLUDE_BITMAP
+	bool "Create/check the exclude bitmap"
+	help
+	  Excluding snapshot blocks from COW is done by setting their bit in
+	  the exclude bitmap. There is one exclude bitmap block per block group.
+	  Mke2fs allocates the exclude bitmap and it cannot be removed.
+	  Fsck checks that all (and only) snapshot file blocks are excluded.
+
+config SNAPSHOT_CTL
+	bool "Snapshot control with chsnap/lssnap"
+	help
+	  Set/clear snapshots parent directory with chattr +/-x.
+	  Take/delete snapshot with chsnap +/-S.
+	  Enable/disable snapshot with chsnap +/-n.
+	  View snapshot status with lssnap.
+
+config SNAPSHOT_CHECK_LIST
+	bool "Check snapshot list on fsck"
+	help
+	  Check that all inodes on snapshot list are valid snapshots and
+	  prompt to terminate list when bad inode is found.
+
+config SNAPSHOT_FIX_SNAPSHOT
+	bool "Delete all snapshots on fsck -x"
+	help
+	  On fsck -x or if the 'fix_snapshot' flag is set, prompt to delete
+	  all snapshot inodes and reset exclude bitmap.
+
+config SNAPSHOT_HUGE_SNAPSHOT
+	bool "Map maximum filesystem size with a single snapshot file"
+	help
+	  To map 2^32 logical blocks, 4 triple indirect blocks are used instead
+	  of just one.  The extra 3 triple indirect blocks are stored in-place
+	  of direct blocks, which are not in use by snapshot files.
+	  Snapshots cannot be enabled on filesytem with block size < 4K.
+
+config SNAPSHOT_MESSAGE_BUFFER
+	bool "Dump message buffer on fsck"
+	help
+	  Error messages are recorded in a message buffer after the
+	  journal super block.  On journal recovery, the journal message buffer
+	  is copied to the file system message buffer.  On fsck, if the message
+	  buffer is not empty, the recorded messages are printed to stdout and
+	  the buffer is cleared.
+	  With a default block size of 4K, there is always 2K of free
+	  space for the message buffer after the 1K super block.
+
+config SNAPSHOT_CLEANUP
+	bool "Cleanup snapshot list when removing has_snapshot feature"
+	help
+	  Discard all snapshots by 'tune2fs -O ^has_snapshot'.
+	  Snapshot inodes are chained on a list starting at the super block.
+	  Delete all snapshot inodes on the list and reset exclude bitmap.
+
+config SNAPSHOT_CTL_OLD
+	bool "Old next3 snapshot control with chattr/lsattr -X"
+	help
+	  Take/delete snapshot with chattr -X +/-S.
+	  Enable/disable snapshot with chattr -X +/-n.
+	  View snapshot status with lsattr -X.
+
+config SNAPSHOT_EXCLUDE_INODE
+	bool "Migrate old exclude_inode feature to exclude_bitmap feature"
+	help
+	  Read exclude bitmap block locations from exclude inode, store them
+	  in the group descriptors, remove the exclude inode, clear the
+	  exclude_inode feature and set the exclude_bitmap feature.
+
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 4e7fc72..c3cae8f 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -156,6 +156,9 @@ struct resource_track {
 #define E2F_OPT_COMPRESS_DIRS	0x0400
 #define E2F_OPT_FRAGCHECK	0x0800
 #define E2F_OPT_JOURNAL_ONLY	0x1000 /* only replay the journal */
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+#define E2F_OPT_FIX_SNAPSHOT	0x2000
+#endif
 
 /*
  * E2fsck flags
@@ -178,6 +181,12 @@ struct resource_track {
 #define E2F_FLAG_GOT_DEVSIZE	0x0800 /* Device size has been fetched */
 #define E2F_FLAG_EXITING	0x1000 /* E2fsck exiting due to errors */
 #define E2F_FLAG_TIME_INSANE	0x2000 /* Time is insane */
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+#define E2F_FLAG_EXCLUDE_INODE	0x4000 /* Request to recreate exclude inode */
+#endif
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+#define E2F_FLAG_CLEAR_SNAPSHOTS	0x8000 /* Clear all snapshot inodes */
+#endif
 
 #define E2F_RESET_FLAGS (E2F_FLAG_TIME_INSANE)
 
@@ -234,6 +243,9 @@ struct e2fsck_struct {
 	ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/
 
 	ext2fs_block_bitmap block_found_map; /* Blocks which are in use */
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	ext2fs_block_bitmap block_excluded_map; /* Blocks which are excluded */
+#endif
 	ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */
 	ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */
 
@@ -476,6 +488,15 @@ void e2fsck_rehash_directories(e2fsck_t ctx);
 void check_super_block(e2fsck_t ctx);
 int check_backup_super_block(e2fsck_t ctx);
 void check_resize_inode(e2fsck_t ctx);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+void check_exclude_inode(e2fsck_t ctx);
+#endif
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+void check_snapshots(e2fsck_t ctx);
+#endif
+#ifdef EXT2FS_SNAPSHOT_MESSAGE_BUFFER
+void e2fsck_clear_message_buffer(e2fsck_t ctx);
+#endif
 
 /* util.c */
 extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
diff --git a/e2fsck/journal.c b/e2fsck/journal.c
index b741eb9..a1eefb4 100644
--- a/e2fsck/journal.c
+++ b/e2fsck/journal.c
@@ -837,6 +837,24 @@ static errcode_t recover_ext3_journal(e2fsck_t ctx)
 
 
 	if (journal->j_superblock->s_errno) {
+#ifdef EXT2FS_SNAPSHOT_MESSAGE_BUFFER
+		/* journal message buffer at journal super block + 1K */
+		char *buf = ((char *) journal->j_superblock) +
+			SUPERBLOCK_OFFSET;
+		int n, len = ctx->fs->blocksize - MSGBUF_OFFSET;
+
+		if (len >= MSGBUF_OFFSET && *buf) {
+			/* keep it simple - write in MSGBUF_OFFSET blocksize */
+			io_channel_set_blksize(ctx->fs->io, MSGBUF_OFFSET);
+			n = len / MSGBUF_OFFSET;
+			/* write journal message buffer to super block + 2K */
+			retval = io_channel_write_blk(ctx->fs->io, 1, n, buf);
+			io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize);
+			/* clear journal message buffer */
+			memset(buf, 0, len);
+		}
+
+#endif
 		ctx->fs->super->s_state |= EXT2_ERROR_FS;
 		ext2fs_mark_super_dirty(ctx->fs);
 		journal->j_superblock->s_errno = 0;
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 4bf80d2..f01f579 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -55,6 +55,11 @@
 #define _INLINE_ inline
 #endif
 
+#define ext2fs_file_acl_block(inode) \
+	(inode)->i_file_acl
+#define ext2fs_fast_mark_block_bitmap2(map, blk) \
+	ext2fs_fast_mark_block_bitmap(map, blk)
+
 static int process_block(ext2_filsys fs, blk_t	*blocknr,
 			 e2_blkcnt_t blockcnt, blk_t ref_blk,
 			 int ref_offset, void *priv_data);
@@ -79,13 +84,17 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
 struct process_block_struct {
 	ext2_ino_t	ino;
 	unsigned	is_dir:1, is_reg:1, clear:1, suppress:1,
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+			fragmented:1, compressed:1, bbcheck:1, snapfile:1;
+#else
 				fragmented:1, compressed:1, bbcheck:1;
+#endif
 	blk64_t		num_blocks;
-	blk_t		max_blocks;
+	blk64_t		max_blocks;
 	e2_blkcnt_t	last_block;
 	e2_blkcnt_t	last_db_block;
 	int		num_illegal_blocks;
-	blk_t		previous_block;
+	blk64_t		previous_block;
 	struct ext2_inode *inode;
 	struct problem_context *pctx;
 	ext2fs_block_bitmap fs_meta_blocks;
@@ -626,6 +635,18 @@ void e2fsck_pass1(e2fsck_t ctx)
 		ctx->flags |= E2F_FLAG_ABORT;
 		return;
 	}
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP)
+		pctx.errcode = ext2fs_allocate_block_bitmap(fs,
+				_("excluded block map"),
+				&ctx->block_excluded_map);
+	if (pctx.errcode) {
+		pctx.num = 1;
+		fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+#endif
 	e2fsck_setup_tdb_icount(ctx, 0, &ctx->inode_link_info);
 	if (!ctx->inode_link_info)
 		pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
@@ -891,7 +912,11 @@ void e2fsck_pass1(e2fsck_t ctx)
 			if (ino == EXT2_BOOT_LOADER_INO) {
 				if (LINUX_S_ISDIR(inode->i_mode))
 					problem = PR_1_RESERVED_BAD_MODE;
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+			} else if (ino == EXT2_RESIZE_INO || ino == EXT2_EXCLUDE_INO) {
+#else
 			} else if (ino == EXT2_RESIZE_INO) {
+#endif
 				if (inode->i_mode &&
 				    !LINUX_S_ISREG(inode->i_mode))
 					problem = PR_1_RESERVED_BAD_MODE;
@@ -1072,7 +1097,10 @@ void e2fsck_pass1(e2fsck_t ctx)
 		    (inode->i_block[EXT2_IND_BLOCK] ||
 		     inode->i_block[EXT2_DIND_BLOCK] ||
 		     inode->i_block[EXT2_TIND_BLOCK] ||
-		     inode->i_file_acl)) {
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+		     (inode->i_flags & EXT4_SNAPFILE_FL) ||
+#endif
+		     ext2fs_file_acl_block(inode))) {
 			inodes_to_process[process_inode_count].ino = ino;
 			inodes_to_process[process_inode_count].inode = *inode;
 			process_inode_count++;
@@ -1144,6 +1172,27 @@ void e2fsck_pass1(e2fsck_t ctx)
 		ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
 	}
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+	if (ctx->flags & E2F_FLAG_EXCLUDE_INODE) {
+		ext2fs_block_bitmap save_bmap;
+
+		save_bmap = fs->block_map;
+		fs->block_map = ctx->block_found_map;
+		clear_problem_context(&pctx);
+		pctx.errcode = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE);
+		if (pctx.errcode &&
+			fix_problem(ctx, PR_1_EXCLUDE_INODE_CREATE, &pctx)) {
+			memset(&inode, 0, sizeof(inode));
+			e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, inode,
+					   "clear_exclude");
+			fs->super->s_feature_compat &= ~EXT2_FEATURE_COMPAT_EXCLUDE_INODE;
+			ctx->flags |= E2F_FLAG_RESTART;
+		}
+		fs->block_map = save_bmap;
+		ctx->flags &= ~E2F_FLAG_EXCLUDE_INODE;
+	}
+
+#endif
 	if (ctx->flags & E2F_FLAG_RESTART) {
 		/*
 		 * Only the master copy of the superblock and block
@@ -1649,6 +1698,19 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino,
 			struct ext2_inode *inode, int restart_flag,
 			const char *source)
 {
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+	/* don't clear inode with blocks when preening volume with active snapshot */
+	if ((ctx->fs->super->s_feature_ro_compat &
+				EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) &&
+		 ctx->fs->super->s_snapshot_inum) {
+		int i;
+		for (i = 0; i < EXT2_N_BLOCKS; i++)
+			if (inode->i_block[i])
+				/* if we don't halt, inode blocks will be freed */
+				preenhalt(ctx);
+	}
+
+#endif
 	inode->i_flags = 0;
 	inode->i_links_count = 0;
 	ext2fs_icount_store(ctx->inode_link_info, ino, 0);
@@ -1892,6 +1954,9 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 	pb.previous_block = 0;
 	pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
 	pb.is_reg = LINUX_S_ISREG(inode->i_mode);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	pb.snapfile = (pb.is_reg && (inode->i_flags & EXT4_SNAPFILE_FL));
+#endif
 	pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
 	pb.inode = inode;
 	pb.pctx = pctx;
@@ -1899,6 +1964,15 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 	pctx->ino = ino;
 	pctx->errcode = 0;
 
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+	if (pb.snapfile && (ctx->flags & E2F_FLAG_CLEAR_SNAPSHOTS)) {
+		/* discarding all snapshot files */
+		e2fsck_clear_inode(ctx, ino, inode, E2F_FLAG_RESTART,
+				   "check_blocks");
+		return;
+	}
+
+#endif
 	extent_fs = (ctx->fs->super->s_feature_incompat &
                      EXT3_FEATURE_INCOMPAT_EXTENTS);
 
@@ -1967,8 +2041,15 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 		}
 	}
 
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+	if ((!(fs->super->s_feature_ro_compat &
+	       EXT4_FEATURE_RO_COMPAT_HUGE_FILE) &&
+	     /* snapshot file always supports the 'huge_file' flag */
+	     !(inode->i_flags & EXT4_SNAPFILE_FL)) ||
+#else
 	if (!(fs->super->s_feature_ro_compat &
 	      EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
+#endif
 	    !(inode->i_flags & EXT4_HUGE_FILE_FL))
 		pb.num_blocks *= (fs->blocksize / 512);
 #if 0
@@ -1999,6 +2080,9 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 		    !(inode->i_flags & EXT4_EOFBLOCKS_FL))
 			bad_size = 3;
 		else if (!(extent_fs && (inode->i_flags & EXT4_EXTENTS_FL)) &&
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+			 !(pb.is_reg && (inode->i_flags & EXT4_SNAPFILE_FL)) &&
+#endif
 			 size > ext2_max_sizes[fs->super->s_log_block_size])
 			/* too big for a direct/indirect-mapped file */
 			bad_size = 4;
@@ -2037,8 +2121,15 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 	    (inode->i_size_high || inode->i_size & 0x80000000UL))
 		ctx->large_files++;
 	if ((pb.num_blocks != ext2fs_inode_i_blocks(fs, inode)) ||
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+	    (((fs->super->s_feature_ro_compat &
+	       EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
+	      /* snapshot file always supports the 'huge_file' flag */
+	      (inode->i_flags & EXT4_SNAPFILE_FL)) &&
+#else
 	    ((fs->super->s_feature_ro_compat &
 	      EXT4_FEATURE_RO_COMPAT_HUGE_FILE) &&
+#endif
 	     (inode->i_flags & EXT4_HUGE_FILE_FL) &&
 	     (inode->osd2.linux2.l_i_blocks_hi != 0))) {
 		pctx->num = pb.num_blocks;
@@ -2236,6 +2327,11 @@ static int process_block(ext2_filsys fs,
 			mark_block_used(ctx, blk);
 	} else
 		mark_block_used(ctx, blk);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	/* mark snapshot file blocks excluded */
+	if (p->snapfile && ctx->block_excluded_map)
+		ext2fs_fast_mark_block_bitmap2(ctx->block_excluded_map, blk);
+#endif
 	p->num_blocks++;
 	if (blockcnt >= 0)
 		p->last_block = blockcnt;
diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index bc3bf02..b744f6a 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -14,6 +14,9 @@
 #include "problem.h"
 
 static void check_block_bitmaps(e2fsck_t ctx);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+static void check_exclude_bitmaps(e2fsck_t ctx);
+#endif
 static void check_inode_bitmaps(e2fsck_t ctx);
 static void check_inode_end(e2fsck_t ctx);
 static void check_block_end(e2fsck_t ctx);
@@ -44,6 +47,11 @@ void e2fsck_pass5(e2fsck_t ctx)
 	check_block_bitmaps(ctx);
 	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
 		return;
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	check_exclude_bitmaps(ctx);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		return;
+#endif
 	check_inode_bitmaps(ctx);
 	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
 		return;
@@ -60,6 +68,11 @@ void e2fsck_pass5(e2fsck_t ctx)
 	ctx->inode_dir_map = 0;
 	ext2fs_free_block_bitmap(ctx->block_found_map);
 	ctx->block_found_map = 0;
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	if (ctx->block_excluded_map)
+		ext2fs_free_block_bitmap(ctx->block_excluded_map);
+	ctx->block_excluded_map = 0;
+#endif
 
 	print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io);
 }
@@ -82,6 +95,20 @@ static void print_bitmap_problem(e2fsck_t ctx, int problem,
 		else
 			problem = PR_5_BLOCK_RANGE_USED;
 		break;
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	case PR_5_BLOCK_NOTEXCLUDED:
+		if (pctx->blk == pctx->blk2)
+			pctx->blk2 = 0;
+		else
+			problem = PR_5_BLOCK_RANGE_NOTEXCLUDED;
+		break;
+	case PR_5_BLOCK_EXCLUDED:
+		if (pctx->blk == pctx->blk2)
+			pctx->blk2 = 0;
+		else
+			problem = PR_5_BLOCK_RANGE_EXCLUDED;
+		break;
+#endif
 	case PR_5_INODE_UNUSED:
 		if (pctx->ino == pctx->ino2)
 			pctx->ino2 = 0;
@@ -294,6 +321,11 @@ redo_counts:
 		ext2fs_unmark_valid(fs);
 
 	for (i = 0; i < fs->group_desc_count; i++) {
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+		if (fs->super->s_flags & EXT2_FLAGS_IS_SNAPSHOT)
+			/* ignore group block counts in next3 snapshot image */
+			break;
+#endif
 		if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
 			pctx.group = i;
 			pctx.blk = fs->group_desc[i].bg_free_blocks_count;
@@ -323,6 +355,142 @@ errout:
 	ext2fs_free_mem(&free_array);
 }
 
+#define blk64_t blk_t
+#define ext2fs_bg_flags_test(fs, group, flag) \
+	(fs->group_desc[group].bg_flags & flag)
+#define ext2fs_bg_flags_clear(fs, group, flag) \
+	fs->group_desc[group].bg_flags &= ~flag
+#define ext2fs_blocks_count(sb) \
+	(sb)->s_blocks_count
+#define ext2fs_fast_test_block_bitmap2 ext2fs_fast_test_block_bitmap
+
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+static void check_exclude_bitmaps(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	blk64_t	i;
+	int	group = 0;
+	int	blocks = 0;
+	int	actual, bitmap;
+	struct problem_context	pctx;
+	int	problem, save_problem, fixit, had_problem;
+	errcode_t	retval;
+	int		csum_flag;
+	int		skip_group = 0;
+
+	clear_problem_context(&pctx);
+
+	if (!(fs->super->s_feature_compat &
+				EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP))
+		return;
+
+	csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+					       EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+redo_counts:
+	had_problem = 0;
+	save_problem = 0;
+	pctx.blk = pctx.blk2 = NO_BLK;
+	if (csum_flag &&
+	    (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT)))
+		skip_group++;
+	for (i = fs->super->s_first_data_block;
+	     i < ext2fs_blocks_count(fs->super);
+	     i++) {
+		actual = ext2fs_fast_test_block_bitmap2(ctx->block_excluded_map, i);
+
+		if (skip_group) {
+			bitmap = 0;
+			actual = (actual != 0);
+		} else
+			bitmap = ext2fs_fast_test_block_bitmap2(fs->exclude_map, i);
+
+		if (actual == bitmap)
+			goto do_counts;
+
+		if (!actual && bitmap) {
+			/*
+			 * Block not excluded, but marked in exclude bitmap.
+			 */
+			problem = PR_5_BLOCK_NOTEXCLUDED;
+		} else {
+			/*
+			 * Block excluded, but not marked in exclude bitmap.
+			 */
+			problem = PR_5_BLOCK_EXCLUDED;
+
+			if (skip_group) {
+				struct problem_context pctx2;
+				pctx2.blk = i;
+				pctx2.group = group;
+				if (fix_problem(ctx, PR_5_BLOCK_UNINIT,&pctx2)){
+					ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
+					skip_group = 0;
+				}
+			}
+		}
+		if (pctx.blk == NO_BLK) {
+			pctx.blk = pctx.blk2 = i;
+			save_problem = problem;
+		} else {
+			if ((problem == save_problem) &&
+			    (pctx.blk2 == i-1))
+				pctx.blk2++;
+			else {
+				print_bitmap_problem(ctx, save_problem, &pctx);
+				pctx.blk = pctx.blk2 = i;
+				save_problem = problem;
+			}
+		}
+		ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
+		had_problem++;
+
+	do_counts:
+		blocks ++;
+		if ((blocks == fs->super->s_blocks_per_group) ||
+		    (i == fs->super->s_blocks_count-1)) {
+			group ++;
+			blocks = 0;
+			skip_group = 0;
+			if (ctx->progress)
+				if ((ctx->progress)(ctx, 5, group,
+						    fs->group_desc_count*2))
+					return;
+			if (csum_flag &&
+			    (i != ext2fs_blocks_count(fs->super)-1) &&
+			    ext2fs_bg_flags_test(fs, group,
+				    		EXT2_BG_BLOCK_UNINIT))
+				skip_group++;
+		}
+	}
+	if (pctx.blk != NO_BLK)
+		print_bitmap_problem(ctx, save_problem, &pctx);
+	if (had_problem)
+		fixit = end_problem_latch(ctx, PR_LATCH_XBITMAP);
+	else
+		fixit = -1;
+	ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
+
+	if (fixit == 1) {
+		ext2fs_free_block_bitmap(fs->exclude_map);
+		retval = ext2fs_copy_bitmap(ctx->block_excluded_map,
+						  &fs->exclude_map);
+		if (retval) {
+			clear_problem_context(&pctx);
+			fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
+			ctx->flags |= E2F_FLAG_ABORT;
+			return;
+		}
+		ext2fs_mark_exclude_dirty(fs);
+		/* clear fix_exclude flag */
+		if (fs->super->s_flags & EXT2_FLAGS_FIX_EXCLUDE) {
+			fs->super->s_flags &= ~EXT2_FLAGS_FIX_EXCLUDE;
+			ext2fs_mark_super_dirty(fs);
+		}
+	} else if (fixit == 0)
+		ext2fs_unmark_valid(fs);
+}
+
+#endif
 static void check_inode_bitmaps(e2fsck_t ctx)
 {
 	ext2_filsys fs = ctx->fs;
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 8f0b211..420516e 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -40,7 +40,15 @@
 #define PROMPT_UNLINK	17
 #define PROMPT_CLEAR_HTREE 18
 #define PROMPT_RECREATE 19
+#ifdef EXT2FS_SNAPSHOT_CHECK_LIST
+#define PROMPT_TERMINATE_LIST 20
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+#define PROMPT_DISCARD_SNAPSHOTS 21
+#endif
+#define PROMPT_NULL	22
+#else
 #define PROMPT_NULL	20
+#endif
 
 /*
  * These are the prompts which are used to ask the user if they want
@@ -67,7 +75,15 @@ static const char *prompt[] = {
 	N_("Unlink"),		/* 17 */
 	N_("Clear HTree index"),/* 18 */
 	N_("Recreate"),		/* 19 */
+#ifdef EXT2FS_SNAPSHOT_CHECK_LIST
+	N_("Terminate list"),	/* 20 */
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+	N_("Discard snapshots"),/* 21 */
+#endif
+	"",			/* 22 */
+#else
 	"",			/* 20 */
+#endif
 };
 
 /*
@@ -332,6 +348,33 @@ static struct e2fsck_problem problem_table[] = {
 	  N_("Resize @i not valid.  "),
 	  PROMPT_RECREATE, 0 },
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+	/* Exclude not enabled, but exclude inode is non-zero */
+	{ PR_0_CLEAR_EXCLUDE_INODE,
+	  N_("Exclude_@i not enabled, but the exclude @i is non-zero.  "),
+	  PROMPT_CLEAR, 0 },
+
+	/* Exclude inode invalid */
+	{ PR_0_EXCLUDE_INODE_INVALID,
+	  N_("Exclude @i not valid.  "),
+	  PROMPT_RECREATE, 0 },
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_CHECK_LIST
+	/* Bad snapshot on list */
+	{ PR_0_BAD_SNAPSHOT,
+	  N_("Bad @i found on snapshot list.  "),
+	  PROMPT_TERMINATE_LIST, PR_PREEN_OK },
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+	/* Corrupted snapshot */
+	{ PR_0_FIX_SNAPSHOT,
+	  N_("@f may contain corrupted snapshots.\n"
+	     "This version of e2fsck does not support fixing snapshots.\n"),
+	  PROMPT_DISCARD_SNAPSHOTS, 0 },
+
+#endif
 	/* Last mount time is in the future */
 	{ PR_0_FUTURE_SB_LAST_MOUNT,
 	  N_("@S last mount time (%t,\n\tnow = %T) is in the future.\n"),
@@ -800,6 +843,13 @@ static struct e2fsck_problem problem_table[] = {
 	  N_("Resize @i (re)creation failed: %m."),
 	  PROMPT_CONTINUE, 0 },
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+	/* Exclude inode failed */
+	{ PR_1_EXCLUDE_INODE_CREATE,
+	  N_("Exclude @i (re)creation failed: %m."),
+	  PROMPT_CLEAR, 0 },
+
+#endif
 	/* invalid inode->i_extra_isize */
 	{ PR_1_EXTRA_ISIZE,
 	  N_("@i %i has a extra size (%IS) which is @n\n"),
@@ -1533,6 +1583,28 @@ static struct e2fsck_problem problem_table[] = {
 	  "\n",
 	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	/* Exclude bitmap differences header */
+	{ PR_5_EXCLUDE_BITMAP_HEADER,
+	  N_("Exclude @B differences: "),
+	  PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
+
+	/* Block not excluded, but marked in exclude bitmap */
+	{ PR_5_BLOCK_NOTEXCLUDED,
+	  " -%b",
+	  PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Block excluded, but not marked in exclude bitmap */
+	{ PR_5_BLOCK_EXCLUDED,
+	  " +%b",
+	  PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Exclude bitmap differences end */
+	{ PR_5_EXCLUDE_BITMAP_END,
+	  "\n",
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+#endif
 	/* Inode bitmap differences header */
 	{ PR_5_INODE_BITMAP_HEADER,
 	  N_("@i @B differences: "),
@@ -1609,6 +1681,18 @@ static struct e2fsck_problem problem_table[] = {
 	  " +(%b--%c)",
 	  PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	/* Block range not excluded, but marked in exclude bitmap */
+	{ PR_5_BLOCK_RANGE_NOTEXCLUDED,
+	  " -(%b--%c)",
+	  PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Block range excluded, but not marked in exclude bitmap */
+	{ PR_5_BLOCK_RANGE_EXCLUDED,
+	  " +(%b--%c)",
+	  PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+#endif
 	/* Inode range not used, but marked in bitmap */
 	{ PR_5_INODE_RANGE_UNUSED,
 	  " -(%i--%j)",
@@ -1650,6 +1734,9 @@ static struct latch_descr pr_latch_info[] = {
 	{ PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
 	{ PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
 	{ PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	{ PR_LATCH_XBITMAP, PR_5_EXCLUDE_BITMAP_HEADER, PR_5_EXCLUDE_BITMAP_END },
+#endif
 	{ PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
 	{ PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
 	{ PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 7c4c156..8c58e4d 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -39,6 +39,9 @@ struct problem_context {
 #define PR_LATCH_TOOBIG	0x0080	/* Latch for file to big errors */
 #define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */
 #define PR_LATCH_BG_CHECKSUM 0x00A0  /* Latch for block group checksums */
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+#define PR_LATCH_XBITMAP 0x00B0 /* Latch for pass 5 exclude bitmap proc. */
+#endif
 
 #define PR_LATCH(x)	((((x) & PR_LATCH_MASK) >> 4) - 1)
 
@@ -227,6 +230,24 @@ struct problem_context {
 /* Block group checksum (latch question) */
 #define PR_0_GDT_CSUM_LATCH			0x00003E
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+/* Exclude_inode not enabled, but exclude inode is non-zero */
+#define PR_0_CLEAR_EXCLUDE_INODE		0x000100
+
+/* Exclude inode invalid */
+#define PR_0_EXCLUDE_INODE_INVALID		0x000101
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_CHECK_LIST
+/* Bas snapshot on list */
+#define PR_0_BAD_SNAPSHOT			0x000102
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+/* Corrupted snapshot */
+#define PR_0_FIX_SNAPSHOT			0x000103
+
+#endif
 
 /*
  * Pass 1 errors
@@ -520,6 +541,11 @@ struct problem_context {
 /* EOFBLOCKS flag set when not necessary */
 #define PR_1_EOFBLOCKS_FL_SET		0x010060
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+/* Exclude inode failed */
+#define PR_1_EXCLUDE_INODE_CREATE	0x010100
+
+#endif
 /*
  * Pass 1b errors
  */
@@ -983,6 +1009,26 @@ struct problem_context {
 /* Inode in use but group is marked INODE_UNINIT */
 #define PR_5_INODE_UNINIT		0x050019
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+/* Exclude bitmap differences header */
+#define PR_5_EXCLUDE_BITMAP_HEADER	0x050100
+
+/* Block not excluded, but marked in exclude bitmap */
+#define PR_5_BLOCK_NOTEXCLUDED		0x050101
+
+/* Block excluded, but not marked in exclude bitmap */
+#define PR_5_BLOCK_EXCLUDED		0x050102
+
+/* Block range not excluded, but marked in exclude bitmap */
+#define PR_5_BLOCK_RANGE_NOTEXCLUDED	0x050103
+
+/* Block range excluded, but not marked in exclude bitmap */
+#define PR_5_BLOCK_RANGE_EXCLUDED	0x050104
+
+/* Exclude bitmap differences end */
+#define PR_5_EXCLUDE_BITMAP_END		0x050105
+
+#endif
 /*
  * Post-Pass 5 errors
  */
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index ceb8543..6db3f90 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -829,6 +829,15 @@ void e2fsck_rehash_directories(e2fsck_t ctx)
 	int			cur, max, all_dirs, dir_index, first = 1;
 
 	init_resource_track(&rtrack, ctx->fs->io);
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+
+ 	/* never rehash directories when scanning volume with active snapshot */
+ 	if ((ctx->fs->super->s_feature_ro_compat &
+				EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) &&
+ 		 ctx->fs->super->s_snapshot_inum)
+ 		return;
+
+#endif
 	all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
 
 	if (!ctx->dirs_to_hash && !all_dirs)
diff --git a/e2fsck/super.c b/e2fsck/super.c
index accc2f1..1f1f416 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -229,6 +229,14 @@ static int release_orphan_inodes(e2fsck_t ctx)
 	struct problem_context pctx;
 	char *block_buf;
 
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+	/* never release orphans when scanning volume with active snapshot */
+	if ((fs->super->s_feature_ro_compat &
+				EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) &&
+		 fs->super->s_snapshot_inum)
+		return 0;
+
+#endif
 	if ((ino = fs->super->s_last_orphan) == 0)
 		return 0;
 
@@ -423,6 +431,234 @@ cleanup:
 
  }
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+/*
+ * Check the exclude inode to make sure it is sane.  We check both for
+ * the case where exclude bitmap is not enabled (in which case the
+ * exclude inode should be cleared) as well as the case where exclude
+ * bitmap is enabled.
+ */
+void check_exclude_inode(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	struct ext2_inode inode;
+	struct problem_context	pctx;
+	int		i, flags = 0;
+	blk_t		blk;
+	errcode_t	retval;
+
+	clear_problem_context(&pctx);
+
+	/* Read the exclude inode */
+	pctx.ino = EXT2_EXCLUDE_INO;
+	retval = ext2fs_read_inode(fs, EXT2_EXCLUDE_INO, &inode);
+	if (retval) {
+		if (fs->super->s_feature_compat &
+		    EXT2_FEATURE_COMPAT_EXCLUDE_INODE)
+			ctx->flags |= E2F_FLAG_EXCLUDE_INODE;
+		return;
+	}
+
+	/*
+	 * If the exclude inode feature isn't set, check to make sure
+	 * the exclude inode is cleared; then we're done.
+	 */
+	if (!(fs->super->s_feature_compat &
+	      EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) {
+		for (i=0; i < EXT2_N_BLOCKS; i++) {
+			if (inode.i_block[i])
+				break;
+		}
+		if ((i < EXT2_N_BLOCKS) &&
+		    fix_problem(ctx, PR_0_CLEAR_EXCLUDE_INODE, &pctx)) {
+			memset(&inode, 0, sizeof(inode));
+			e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, &inode,
+					   "clear_exclude");
+		}
+		return;
+	}
+
+	/*
+	 * The exclude inode feature is enabled; check to make sure the
+	 * only block in use is the double indirect block
+	 */
+	blk = inode.i_block[EXT2_DIND_BLOCK];
+	for (i=0; i < EXT2_N_BLOCKS; i++) {
+		if (i != EXT2_DIND_BLOCK && inode.i_block[i])
+			break;
+	}
+	if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
+	    !(inode.i_mode & LINUX_S_IFREG) ||
+	    (blk < fs->super->s_first_data_block ||
+	     blk >= fs->super->s_blocks_count)) {
+		if (fix_problem(ctx, PR_0_EXCLUDE_INODE_INVALID, &pctx)) {
+			memset(&inode, 0, sizeof(inode));
+			e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, &inode,
+					   "clear_exclude");
+			ctx->flags |= E2F_FLAG_EXCLUDE_INODE;
+		}
+		return;
+	}
+
+	if (!(ctx->options & E2F_OPT_READONLY))
+		flags = EXCLUDE_ALLOC;
+	/*
+	 * create exclude inode and/or allocate missing exclude bitmap blocks.
+	 */
+	clear_problem_context(&pctx);
+	pctx.errcode = ext2fs_create_exclude_inode(fs, flags);
+	if (pctx.errcode &&
+			fix_problem(ctx, PR_1_EXCLUDE_INODE_CREATE, &pctx)) {
+		memset(&inode, 0, sizeof(inode));
+		e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, &inode,
+				"clear_exclude");
+		ctx->flags |= E2F_FLAG_EXCLUDE_INODE;
+	}
+}
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_CHECK_LIST
+/*
+ * Check that snapshot list contains valid snapshot files.
+ * Returns the number of valid snapshots on list.
+ *
+ * TODO: cleanup orphan snapshot files (not found on list)
+ */
+static int check_snapshot_list(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	struct ext2_super_block *sb = fs->super;
+	struct ext2_inode	inode;
+	struct ext2_inode	inode_prev;
+	ext2_ino_t		ino = sb->s_snapshot_list;
+	ext2_ino_t		ino_prev = 0;
+	errcode_t		retval = 0;
+	struct problem_context	pctx;
+	int i = 0;
+	
+	if (!ino && sb->s_snapshot_inum) {
+		/* reset snapshot list head to active snapshot */
+		ino = sb->s_snapshot_list = sb->s_snapshot_inum;
+		ext2fs_mark_super_dirty(fs);
+	}
+	if (ino)
+		fputs(_("Checking snapshots: "), stderr);
+
+	while (ino) {
+		retval = ext2fs_read_inode(fs, ino,  &inode);
+		if (retval || !(inode.i_flags & EXT4_SNAPFILE_FL) ||
+				!LINUX_S_ISREG(inode.i_mode) || 
+				inode.i_dtime) {
+			if (ino == sb->s_snapshot_list) {
+				/* reset list head */
+				sb->s_snapshot_list = 0;
+				ext2fs_mark_super_dirty(fs);
+				ino = sb->s_snapshot_inum;
+				continue;
+			}
+			clear_problem_context(&pctx);
+			if (!fix_problem(ctx, PR_0_BAD_SNAPSHOT, &pctx))
+				break;
+
+			/* disconnect inode from snapshot list */
+			if (ino == sb->s_snapshot_inum) {
+				/* reset active snapshot */
+				sb->s_snapshot_inum = 0;
+				ext2fs_mark_super_dirty(fs);
+			}
+			if (ino_prev) {
+				/* terminate list at prev inode */
+				inode_prev.i_next_snapshot = 0;
+				e2fsck_write_inode(ctx, ino_prev, &inode_prev,
+						"terminate snapshot list");
+			}
+			break;
+		}
+
+		fprintf(stderr, _("%u,"), inode.i_generation);
+		inode_prev = inode;
+		ino_prev = ino;
+		ino = inode.i_next_snapshot;
+		i++;
+	}
+	
+	if (ino_prev && !ino)
+		fputs(_("done\n"), stderr);
+	return i;
+}
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+/*
+ * This function checks if the file system has snapshots
+ */
+void check_snapshots(e2fsck_t ctx)
+{
+	struct ext2_super_block *sb = ctx->fs->super;
+	struct problem_context	pctx;
+	int cont;
+
+	if (!(sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT))
+		/* no snapshots */
+		return;
+
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+	if ((ctx->options & E2F_OPT_FIX_SNAPSHOT) ||
+			(sb->s_flags & EXT2_FLAGS_FIX_SNAPSHOT)) {
+		/* corrupted snapshot need to be fixed */
+		clear_problem_context(&pctx);
+		if (fix_problem(ctx, PR_0_FIX_SNAPSHOT, &pctx)) {
+			if (sb->s_snapshot_list || sb->s_snapshot_inum ||
+				(sb->s_flags & (EXT2_FLAGS_FIX_SNAPSHOT |
+						EXT2_FLAGS_IS_SNAPSHOT))) {
+				/* reset snapshot list head */
+				sb->s_snapshot_list = sb->s_snapshot_inum = 0;
+				sb->s_flags &= ~(EXT2_FLAGS_FIX_SNAPSHOT |
+						 EXT2_FLAGS_IS_SNAPSHOT);
+				ext2fs_mark_super_dirty(ctx->fs);
+			}
+			/* don't ask again after pass1 */
+			ctx->options &= ~E2F_OPT_FIX_SNAPSHOT;
+			/* clear all snapshot inodes (in pass1) */
+			ctx->flags |= E2F_FLAG_CLEAR_SNAPSHOTS;
+			if (sb->s_feature_compat &
+					EXT2_FEATURE_COMPAT_EXCLUDE_INODE)
+				/* and reset exclude bitmap */
+				ctx->flags |= E2F_FLAG_EXCLUDE_INODE;
+			return;
+		}
+	}
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_CHECK_LIST
+	if (!check_snapshot_list(ctx))
+		/* no valid snapshots on list */
+		return;
+
+#endif
+	if (!sb->s_snapshot_inum)
+		/* no active snapshot */
+		return;
+
+	if ((ctx->options & E2F_OPT_PREEN) ||
+		(ctx->options & E2F_OPT_NO))
+		/* preen and readonly modes are snapshot friendly */
+		return;
+
+	printf(_("%s has snapshots.  "), ctx->filesystem_name);
+	if (!ctx->interactive)
+		fatal_error(ctx, _("Cannot continue, aborting.\n\n"));
+	printf(_("\n\n\007\007\007\007WARNING!!!  "
+	       "Running e2fsck on filesystem with snapshots may\n"
+	       "damage the snapshots.\007\007\007\n\n"));
+	cont = ask_yn(_("Do you really want to continue"), -1);
+	if (!cont) {
+		printf (_("check aborted.\n"));
+		exit (0);
+	}
+}
+
+#endif
 /*
  * This function checks the dirhash signed/unsigned hint if necessary.
  */
@@ -450,6 +686,72 @@ static void e2fsck_fix_dirhash_hint(e2fsck_t ctx)
 	}
 }
 
+#ifdef EXT2FS_SNAPSHOT_MESSAGE_BUFFER
+/*
+ * This function prints the message buffer at the end of super block.
+ */
+static void e2fsck_print_message_buffer(e2fsck_t ctx)
+{
+	char *buf;
+	int n, len = ctx->fs->blocksize - MSGBUF_OFFSET;
+	unsigned offset = 0;
+	int retval;
+
+	if (len < MSGBUF_OFFSET)
+		/* 1K or 2K fs->blocksize */
+		return;
+
+	buf = (char *) e2fsck_allocate_memory(ctx, len, "message buffer");
+
+	/* keep it simple - write in MSGBUF_OFFSET blocksize */
+	io_channel_set_blksize(ctx->fs->io, MSGBUF_OFFSET);
+	n = len / MSGBUF_OFFSET;
+	/* read message buffer from super block + 2K */
+	retval = io_channel_read_blk(ctx->fs->io, 1, n, buf);
+	if (retval || !*buf)
+		goto out;
+
+	/* print messages in buffer */
+	puts("Error messages recorded in message buffer:");
+	while (offset < len && buf[offset]) {
+		puts(buf+offset);
+		offset += MSGBUF_RECLEN;
+	}
+	puts("End of message buffer.");
+out:
+	io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize);
+	ext2fs_free_mem(&buf);
+}
+
+/*
+ * This function clears the message buffer at the end of super block.
+ * It is called before clearing the EXT2_ERROR_FS flag from super block.
+ */
+void e2fsck_clear_message_buffer(e2fsck_t ctx)
+{
+	char *buf;
+	int n, len = ctx->fs->blocksize - MSGBUF_OFFSET;
+	unsigned offset = 0;
+	int retval;
+
+	if (len < MSGBUF_OFFSET)
+		/* 1K or 2K fs->blocksize */
+		return;
+
+	buf = (char *) e2fsck_allocate_memory(ctx, len, "message buffer");
+
+	/* keep it simple - write in MSGBUF_OFFSET blocksize */
+	io_channel_set_blksize(ctx->fs->io, MSGBUF_OFFSET);
+	n = len / MSGBUF_OFFSET;
+
+	/* clear message buffer at super block + 2K */
+	memset(buf, 0, len);
+	io_channel_write_blk(ctx->fs->io, 1, n, buf);
+	io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize);
+	ext2fs_free_mem(&buf);
+}
+
+#endif
 
 void check_super_block(e2fsck_t ctx)
 {
@@ -869,6 +1171,13 @@ void check_super_block(e2fsck_t ctx)
 	 */
 	e2fsck_fix_dirhash_hint(ctx);
 
+#ifdef EXT2FS_SNAPSHOT_MESSAGE_BUFFER
+	/*
+	 * Print message buffer if necessary
+	 */
+	e2fsck_print_message_buffer(ctx);
+
+#endif
 	return;
 }
 
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 624e11b..9b9ffd9 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -73,7 +73,11 @@ int journal_enable_debug = -1;
 static void usage(e2fsck_t ctx)
 {
 	fprintf(stderr,
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+		_("Usage: %s [-panyrcdfvtxDFV] [-b superblock] [-B blocksize]\n"
+#else
 		_("Usage: %s [-panyrcdfvtDFV] [-b superblock] [-B blocksize]\n"
+#endif
 		"\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
 		"\t\t[-l|-L bad_blocks_file] [-C fd] [-j external_journal]\n"
 		"\t\t[-E extended-options] device\n"),
@@ -92,6 +96,9 @@ static void usage(e2fsck_t ctx)
 		" -j external_journal  Set location of the external journal\n"
 		" -l bad_blocks_file   Add to badblocks list\n"
 		" -L bad_blocks_file   Set badblocks list\n"
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+		" -x                   Fix or discard snapshots\n"
+#endif
 		));
 
 	exit(FSCK_USAGE);
@@ -676,7 +683,11 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 		ctx->program_name = *argv;
 	else
 		ctx->program_name = "e2fsck";
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+	while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkx")) != EOF)
+#else
 	while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
+#endif
 		switch (c) {
 		case 'C':
 			ctx->progress = e2fsck_update_progress;
@@ -806,6 +817,11 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 		case 'k':
 			keep_bad_blocks++;
 			break;
+#ifdef EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+		case 'x':
+			ctx->options |= E2F_OPT_FIX_SNAPSHOT;
+			break;
+#endif
 		default:
 			usage(ctx);
 		}
@@ -1332,6 +1348,12 @@ print_unsupp_features:
 		fatal_error(ctx, 0);
 	check_if_skip(ctx);
 	check_resize_inode(ctx);
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+	check_snapshots(ctx);
+#endif
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+	check_exclude_inode(ctx);
+#endif
 	if (bad_blocks_file)
 		read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
 	else if (cflag)
@@ -1465,6 +1487,9 @@ no_journal:
 				sb->s_lastcheck = ctx->now;
 			memset(((char *) sb) + EXT4_S_ERR_START, 0,
 			       EXT4_S_ERR_LEN);
+#ifdef EXT2FS_SNAPSHOT_MESSAGE_BUFFER
+			e2fsck_clear_message_buffer(ctx);
+#endif
 			ext2fs_mark_super_dirty(fs);
 		}
 	}
diff --git a/install_next3.sh b/install_next3.sh
new file mode 100755
index 0000000..4955488
--- /dev/null
+++ b/install_next3.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Install next3 patched e2fsprogs
+
+# build e2fsprogs from e2fsprogs-snapshots git
+#./configure
+make
+# install progs with .next3 suffix
+install -T e2fsck/e2fsck /sbin/fsck.next3
+install -T misc/mke2fs /sbin/mkfs.next3
+install -T misc/tune2fs /sbin/tunefs.next3
+install -T misc/dumpe2fs /sbin/dumpfs.next3
+install -T misc/lsattr /sbin/lsattr.next3
+install -T misc/chattr /sbin/chattr.next3
+install -T resize/resize2fs /sbin/resize.next3
+install -T next3 /sbin/next3
+
diff --git a/install_next4.sh b/install_next4.sh
new file mode 100755
index 0000000..bbdae85
--- /dev/null
+++ b/install_next4.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+# Install next4 patched e2fsprogs
+
+# build e2fsprogs from e2fsprogs-snapshots git
+#./configure
+make
+# install progs with .next4 suffix
+install -T e2fsck/e2fsck /sbin/fsck.next4
+install -T misc/mke2fs /sbin/mkfs.next4
+install -T misc/tune2fs /sbin/tunefs.next4
+install -T misc/dumpe2fs /sbin/dumpfs.next4
+install -T misc/lsattr /sbin/lsattr.next4
+install -T misc/chattr /sbin/chattr.next4
+install -T misc/lsattr /sbin/lssnap
+install -T misc/chattr /sbin/chsnap
+install -T resize/resize2fs /sbin/resize.next4
+# next3 script is generic and chooses the FS type by it's own name
+install -T next3 /sbin/next4
diff --git a/lib/e2p/e2p.h b/lib/e2p/e2p.h
index 7a90c37..f3c9ab0 100644
--- a/lib/e2p/e2p.h
+++ b/lib/e2p/e2p.h
@@ -26,8 +26,18 @@
 /* `options' for print_flags() */
 
 #define PFOPT_LONG  1 /* Must be 1 for compatibility with `int long_format'. */
+#ifdef EXT2FS_SNAPSHOT_CTL
+#define PFOPT_SNAPSHOT  2 /* list/control snapshot flags */
+#define PFOPT_SNAPSHOT_X  4 /* for backward compatibility with next3 */
+#endif
 
 
+
+
+#ifdef EXT2FS_SNAPSHOT_CTL
+int fgetsnapflags(const char * name, unsigned long * flags);
+int fsetsnapflags(const char * name, unsigned long flags);
+#endif
 int fgetflags (const char * name, unsigned long * flags);
 int fgetversion (const char * name, unsigned long * version);
 int fsetflags (const char * name, unsigned long flags);
diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c
index c7f8a35..d1ef001 100644
--- a/lib/e2p/feature.c
+++ b/lib/e2p/feature.c
@@ -40,6 +40,14 @@ static struct feature feature_list[] = {
 			"resize_inode" },
 	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_LAZY_BG,
 			"lazy_bg" },
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_INODE,
+			"exclude_inode" },
+#endif
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP,
+			"exclude_bitmap" },
+#endif
 
 	{	E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER,
 			"sparse_super" },
@@ -55,6 +63,12 @@ static struct feature feature_list[] = {
 			"dir_nlink" },
 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE,
 			"extra_isize" },
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT,
+			"snapshots" },
+	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT,
+			"has_snapshot" },
+#endif
 
 	{	E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION,
 			"compression" },
diff --git a/lib/e2p/fgetflags.c b/lib/e2p/fgetflags.c
index d66f8e1..33037cf 100644
--- a/lib/e2p/fgetflags.c
+++ b/lib/e2p/fgetflags.c
@@ -40,7 +40,7 @@
 #define OPEN_FLAGS (O_RDONLY|O_NONBLOCK)
 #endif
 
-int fgetflags (const char * name, unsigned long * flags)
+static int fgetflags_ioctl(const char * name, unsigned long * flags, int ioc)
 {
 	struct stat buf;
 #if HAVE_STAT_FLAGS && !(APPLE_DARWIN && HAVE_EXT2_IOCTLS)
@@ -75,7 +75,7 @@ int fgetflags (const char * name, unsigned long * flags)
 	fd = open (name, OPEN_FLAGS);
 	if (fd == -1)
 		return -1;
-	r = ioctl (fd, EXT2_IOC_GETFLAGS, &f);
+	r = ioctl (fd, ioc, &f);
 	if (r == -1)
 		save_errno = errno;
 	*flags = f;
@@ -84,10 +84,10 @@ int fgetflags (const char * name, unsigned long * flags)
 		errno = save_errno;
 	return r;
 #else
-   f = -1;
-   save_errno = syscall(SYS_fsctl, name, EXT2_IOC_GETFLAGS, &f, 0);
-   *flags = f;
-   return (save_errno);
+	f = -1;
+	save_errno = syscall(SYS_fsctl, name, ioc, &f, 0);
+	*flags = f;
+	return (save_errno);
 #endif
 #endif /* HAVE_EXT2_IOCTLS */
 #endif
@@ -95,3 +95,16 @@ notsupp:
 	errno = EOPNOTSUPP;
 	return -1;
 }
+
+int fgetflags(const char * name, unsigned long * flags)
+{
+	return fgetflags_ioctl(name, flags, EXT2_IOC_GETFLAGS);
+}
+
+#ifdef EXT2FS_SNAPSHOT_CTL
+int fgetsnapflags(const char * name, unsigned long * flags)
+{
+	return fgetflags_ioctl(name, flags, EXT2_IOC_GETSNAPFLAGS);
+}
+
+#endif
diff --git a/lib/e2p/fsetflags.c b/lib/e2p/fsetflags.c
index 30437a2..e67f2e1 100644
--- a/lib/e2p/fsetflags.c
+++ b/lib/e2p/fsetflags.c
@@ -49,7 +49,7 @@
 #define OPEN_FLAGS (O_RDONLY|O_NONBLOCK)
 #endif
 
-int fsetflags (const char * name, unsigned long flags)
+int fsetflags_ioctl(const char * name, unsigned long flags, int ioc)
 {
 	struct stat buf;
 #if HAVE_CHFLAGS && !(APPLE_DARWIN && HAVE_EXT2_IOCTLS)
@@ -82,15 +82,15 @@ int fsetflags (const char * name, unsigned long flags)
 	if (fd == -1)
 		return -1;
 	f = (int) flags;
-	r = ioctl (fd, EXT2_IOC_SETFLAGS, &f);
+	r = ioctl (fd, ioc, &f);
 	if (r == -1)
 		save_errno = errno;
 	close (fd);
 	if (save_errno)
 		errno = save_errno;
 #else
-   f = (int) flags;
-   return syscall(SYS_fsctl, name, EXT2_IOC_SETFLAGS, &f, 0);
+	f = (int) flags;
+	return syscall(SYS_fsctl, name, ioc, &f, 0);
 #endif
 	return r;
 #endif /* HAVE_EXT2_IOCTLS */
@@ -99,3 +99,16 @@ notsupp:
 	errno = EOPNOTSUPP;
 	return -1;
 }
+
+int fsetflags(const char * name, unsigned long flags)
+{
+	return fsetflags_ioctl(name, flags, EXT2_IOC_SETFLAGS);
+}
+
+#ifdef EXT2FS_SNAPSHOT_CTL
+int fsetsnapflags(const char * name, unsigned long flags)
+{
+	return fsetflags_ioctl(name, flags, EXT2_IOC_SETSNAPFLAGS);
+}
+
+#endif
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index 5ef8098..3ebc07f 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -160,6 +160,20 @@ static void print_super_flags(struct ext2_super_block * s, FILE *f)
 		fputs("test_filesystem ", f);
 		flags_found++;
 	}
+#ifdef EXT2FS_SNAPSHOT_ON_DISK
+	if (s->s_flags & EXT2_FLAGS_IS_SNAPSHOT) {
+		fputs("is_snapshot ", f);
+		flags_found++;
+	}
+	if (s->s_flags & EXT2_FLAGS_FIX_SNAPSHOT) {
+		fputs("fix_snapshot ", f);
+		flags_found++;
+	}
+	if (s->s_flags & EXT2_FLAGS_FIX_EXCLUDE) {
+		fputs("fix_exclude ", f);
+		flags_found++;
+	}
+#endif
 	if (flags_found)
 		fputs("\n", f);
 	else
@@ -315,6 +329,17 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
 	if (sb->s_last_orphan)
 		fprintf(f, "First orphan inode:       %u\n",
 			sb->s_last_orphan);
+	if (sb->s_snapshot_inum) {
+		fprintf(f, "Snapshot inode:       %u\n",
+			sb->s_snapshot_inum);
+		fprintf(f, "Snapshot ID:       %u\n",
+			sb->s_snapshot_id);
+		fprintf(f, "Snapshot reserved blocks:       %llu\n",
+			sb->s_snapshot_r_blocks_count);
+	}
+	if (sb->s_snapshot_list)
+		fprintf(f, "Snapshot list first inode:       %u\n",
+			sb->s_snapshot_list);
 	if ((sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
 	    sb->s_def_hash_version)
 		fprintf(f, "Default directory hash:   %s\n",
diff --git a/lib/e2p/pf.c b/lib/e2p/pf.c
index f34a5cc..747edde 100644
--- a/lib/e2p/pf.c
+++ b/lib/e2p/pf.c
@@ -48,16 +48,59 @@ static struct flags_name flags_array[] = {
 	{ EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" },
 	{ EXT4_EXTENTS_FL, "e", "Extents" },
 	{ EXT4_HUGE_FILE_FL, "h", "Huge_file" },
+#ifdef EXT2FS_SNAPSHOT_CTL
+	{ EXT4_SNAPFILE_FL, "x", "Snapshot_File" },
+#endif
+	{ 0, NULL, NULL }
+};
+
+#ifdef EXT2FS_SNAPSHOT_CTL
+/* Snapshot dynamic state flags */
+static struct flags_name snapshot_flags_array[] = {
+	{ 1UL<<EXT4_SNAPSHOT_LIST, "S", "on_liSt" },
+	{ 1UL<<EXT4_SNAPSHOT_ENABLED, "n", "eNabled" },
+	{ 1UL<<EXT4_SNAPSHOT_ACTIVE, "a", "Active" },
+	{ 1UL<<EXT4_SNAPSHOT_INUSE, "p", "inuse_by_Previous" },
+	{ 1UL<<EXT4_SNAPSHOT_DELETED, "s", "Deleted" },
+	{ 1UL<<EXT4_SNAPSHOT_SHRUNK, "h", "sHrunk" },
+	{ 1UL<<EXT4_SNAPSHOT_OPEN, "o", "mOunted" },
+	{ 1UL<<EXT4_SNAPSHOT_TAGGED, "t", "Tagged" },
 	{ 0, NULL, NULL }
 };
 
+#ifdef EXT2FS_SNAPSHOT_CTL_OLD
+/* Old snapshot flags for backward compatibility with next3 */
+static struct flags_name snapshot_X_flags_array[] = {
+	{ NEXT3_SNAPFILE_LIST_FL, "S", "on_liSt" },
+	{ NEXT3_SNAPFILE_ENABLED_FL, "n", "eNabled" },
+	{ NEXT3_SNAPFILE_ACTIVE_FL, "a", "Active" },
+	{ NEXT3_SNAPFILE_INUSE_FL, "p", "inuse_by_Previous" },
+	{ NEXT3_SNAPFILE_DELETED_FL, "s", "Deleted" },
+	{ NEXT3_SNAPFILE_SHRUNK_FL, "h", "sHrunk" },
+	{ NEXT3_SNAPFILE_OPEN_FL, "o", "mOunted" },
+	{ NEXT3_SNAPFILE_TAGGED_FL, "t", "Tagged" },
+	{ 0, NULL, NULL }
+};
+
+#endif
+#endif
 void print_flags (FILE * f, unsigned long flags, unsigned options)
 {
+#ifdef EXT2FS_SNAPSHOT_CTL
+	struct flags_name *array = ((options & PFOPT_SNAPSHOT_X) ?
+			snapshot_X_flags_array :
+			((options & PFOPT_SNAPSHOT) ?
+			 snapshot_flags_array : flags_array));
+#endif
 	int long_opt = (options & PFOPT_LONG);
 	struct flags_name *fp;
 	int	first = 1;
 
+#ifdef EXT2FS_SNAPSHOT_CTL
+	for (fp = array; fp->flag != 0; fp++) {
+#else
 	for (fp = flags_array; fp->flag != 0; fp++) {
+#endif
 		if (flags & fp->flag) {
 			if (long_opt) {
 				if (first)
diff --git a/lib/ext2fs/block.c b/lib/ext2fs/block.c
index 4054a07..89a1e2b 100644
--- a/lib/ext2fs/block.c
+++ b/lib/ext2fs/block.c
@@ -310,6 +310,7 @@ errcode_t ext2fs_block_iterate2(ext2_filsys fs,
 	errcode_t	retval;
 	struct block_context ctx;
 	int	limit;
+	blk_t	blk64;
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -459,9 +460,19 @@ errcode_t ext2fs_block_iterate2(ext2_filsys fs,
 	 * Iterate over normal data blocks
 	 */
 	for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+		if ((inode.i_flags & EXT4_SNAPFILE_FL) &&
+				LINUX_S_ISREG(inode.i_mode) &&
+				i < NEXT3_EXTRA_TIND_BLOCKS)
+			/* snapshot file extra triple indirect blocks */
+			continue;
+
+#endif
 		if (inode.i_block[i] || (flags & BLOCK_FLAG_APPEND)) {
-			ret |= (*ctx.func)(fs, &inode.i_block[i],
-					    ctx.bcount, 0, i, priv_data);
+			blk64 = inode.i_block[i];
+			ret |= (*ctx.func)(fs, &blk64, ctx.bcount, 0, i, 
+					   priv_data);
+			inode.i_block[i] = (blk_t) blk64;
 			if (ret & BLOCK_ABORT)
 				goto abort_exit;
 		}
@@ -487,6 +498,19 @@ errcode_t ext2fs_block_iterate2(ext2_filsys fs,
 		if (ret & BLOCK_ABORT)
 			goto abort_exit;
 	}
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+	if ((inode.i_flags & EXT4_SNAPFILE_FL) && LINUX_S_ISREG(inode.i_mode)) {
+		/* iterate snapshot file extra triple indirect blocks */
+		for (i = 0; i < NEXT3_EXTRA_TIND_BLOCKS; i++) {
+			if (!inode.i_block[i] && !(flags & BLOCK_FLAG_APPEND))
+				continue;
+			ret |= block_iterate_tind(&inode.i_block[i],
+					0, EXT2_N_BLOCKS+i, &ctx);
+			if (ret & BLOCK_ABORT)
+				goto abort_exit;
+		}
+	}
+#endif
 
 abort_exit:
 	if (ret & BLOCK_CHANGED) {
diff --git a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c
index fbcb375..1385770 100644
--- a/lib/ext2fs/bmap.c
+++ b/lib/ext2fs/bmap.c
@@ -136,6 +136,10 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
 	struct ext2_inode inode_buf;
 	ext2_extent_handle_t handle = 0;
 	blk_t addr_per_block;
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+	blk64_t addr_per_tind_block;
+	int	tind;
+#endif
 	blk_t	b, blk32;
 	char	*buf = 0;
 	errcode_t	retval = 0;
@@ -286,7 +290,24 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
 
 	/* Triply indirect block */
 	block -= addr_per_block * addr_per_block;
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+	tind = EXT2_TIND_BLOCK;
+	addr_per_tind_block = addr_per_block * addr_per_block * addr_per_block;
+	if (block > addr_per_tind_block) {
+		/* use direct blocks as extra triple indirect blocks? */
+		tind = block / addr_per_tind_block;
+		block -= tind * addr_per_tind_block;
+		if (!(inode->i_flags & EXT4_SNAPFILE_FL) ||
+				!LINUX_S_ISREG(inode->i_mode) ||
+				tind >= NEXT3_EXTRA_TIND_BLOCKS) {
+			retval = EXT2_ET_BAD_BLOCK_NUM;
+			goto done;
+		}
+	}
+	b = inode_bmap(inode, tind);
+#else
 	b = inode_bmap(inode, EXT2_TIND_BLOCK);
+#endif
 	if (!b) {
 		if (!(bmap_flags & BMAP_ALLOC)) {
 			if (bmap_flags & BMAP_SET)
@@ -298,7 +319,11 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
 		retval = ext2fs_alloc_block(fs, b, block_buf, &b);
 		if (retval)
 			goto done;
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+		inode_bmap(inode, tind) = b;
+#else
 		inode_bmap(inode, EXT2_TIND_BLOCK) = b;
+#endif
 		blocks_alloc++;
 	}
 	retval = block_tind_bmap(fs, bmap_flags, b, block_buf,
diff --git a/lib/ext2fs/dupfs.c b/lib/ext2fs/dupfs.c
index a9e2a97..984fb5e 100644
--- a/lib/ext2fs/dupfs.c
+++ b/lib/ext2fs/dupfs.c
@@ -76,6 +76,13 @@ errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest)
 		if (retval)
 			goto errout;
 	}
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	if (src->exclude_map) {
+		retval = ext2fs_copy_bitmap(src->exclude_map, &fs->exclude_map);
+		if (retval)
+			goto errout;
+	}
+#endif
 	if (src->badblocks) {
 		retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks);
 		if (retval)
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index afd33a9..d7a4187 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -26,6 +26,18 @@
  * Define EXT2FS_DEBUG to produce debug messages
  */
 #undef EXT2FS_DEBUG
+#define EXT2FS_SNAPSHOT_ON_DISK
+#define EXT2FS_SNAPSHOT_BIG_JOURNAL
+#define EXT2FS_SNAPSHOT_EXCLUDE_INODE
+#define EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+#define EXT2FS_SNAPSHOT_CTL
+#define EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+#define EXT2FS_SNAPSHOT_CHECK_LIST
+#define EXT2FS_SNAPSHOT_FIX_SNAPSHOT
+#define EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+#define EXT2FS_SNAPSHOT_MESSAGE_BUFFER
+#define EXT2FS_SNAPSHOT_CTL_OLD
+#define EXT2FS_SNAPSHOT_CLEANUP
 
 /*
  * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files
@@ -145,11 +157,19 @@ struct ext2_group_desc
 	__u16	bg_free_inodes_count;	/* Free inodes count */
 	__u16	bg_used_dirs_count;	/* Directories count */
 	__u16	bg_flags;
+#ifdef EXT2FS_SNAPSHOT_ON_DISK
+	__u32	bg_exclude_bitmap;	/* Exclude bitmap block */
+	__u32	bg_reserved[1];
+#else
 	__u32	bg_reserved[2];
+#endif
 	__u16	bg_itable_unused;	/* Unused inodes count */
 	__u16	bg_checksum;		/* crc16(s_uuid+grouo_num+group_desc)*/
 };
 
+/*
+ * Structure of a blocks group descriptor
+ */
 struct ext4_group_desc
 {
 	__u32	bg_block_bitmap;	/* Blocks bitmap block */
@@ -159,7 +179,12 @@ struct ext4_group_desc
 	__u16	bg_free_inodes_count;	/* Free inodes count */
 	__u16	bg_used_dirs_count;	/* Directories count */
 	__u16	bg_flags;
+#ifdef EXT2FS_SNAPSHOT_ON_DISK
+	__u32	bg_exclude_bitmap;	/* Exclude bitmap block */
+	__u32	bg_reserved[1];
+#else
 	__u32	bg_reserved[2];
+#endif
 	__u16	bg_itable_unused;	/* Unused inodes count */
 	__u16	bg_checksum;		/* crc16(s_uuid+grouo_num+group_desc)*/
 	__u32	bg_block_bitmap_hi;	/* Blocks bitmap block MSB */
@@ -169,12 +194,20 @@ struct ext4_group_desc
 	__u16	bg_free_inodes_count_hi;/* Free inodes count MSB */
 	__u16	bg_used_dirs_count_hi;	/* Directories count MSB */
 	__u16   bg_pad;
+#ifdef EXT2FS_SNAPSHOT_ON_DISK
+	__u32	bg_exclude_bitmap_hi;	/* Exclude bitmap block MSB */
+	__u32	bg_reserved2[2];
+#else
 	__u32	bg_reserved2[3];
+#endif
 };
 
 #define EXT2_BG_INODE_UNINIT	0x0001 /* Inode table/bitmap not initialized */
 #define EXT2_BG_BLOCK_UNINIT	0x0002 /* Block bitmap not initialized */
 #define EXT2_BG_INODE_ZEROED	0x0004 /* On-disk itable initialized to zero */
+#ifdef EXT2FS_SNAPSHOT_ON_DISK
+#define EXT2_BG_EXCLUDE_UNINIT	0x0008 /* Exclude bitmap not initialized */
+#endif
 
 /*
  * Data structures used by the directory indexing feature
@@ -247,6 +280,19 @@ struct ext2_dx_countlimit {
 #define EXT2_DIND_BLOCK			(EXT2_IND_BLOCK + 1)
 #define EXT2_TIND_BLOCK			(EXT2_DIND_BLOCK + 1)
 #define EXT2_N_BLOCKS			(EXT2_TIND_BLOCK + 1)
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+/*
+ * Snapshot files have different indirection mapping that can map up to 2^32
+ * logical blocks, so they can cover the mapped filesystem block address space.
+ * Next3 must use either 4K or 8K blocks (depending on PAGE_SIZE).
+ * With 8K blocks, 1 triple indirect block maps 2^33 logical blocks.
+ * With 4K blocks (the system default), each triple indirect block maps 2^30
+ * logical blocks, so 4 triple indirect blocks map 2^32 logical blocks.
+ * Snapshot files in small filesystems (<= 4G), use only 1 double indirect
+ * block to map the entire filesystem.
+ */
+#define	NEXT3_EXTRA_TIND_BLOCKS		3
+#endif
 
 /*
  * Inode flags
@@ -322,6 +368,40 @@ struct ext4_new_group_input {
 #define EXT2_IOC_GROUP_EXTEND		_IOW('f', 7, unsigned long)
 #define EXT2_IOC_GROUP_ADD		_IOW('f', 8,struct ext2_new_group_input)
 #define EXT4_IOC_GROUP_ADD		_IOW('f', 8,struct ext4_new_group_input)
+#ifdef EXT2FS_SNAPSHOT_CTL
+#define EXT2_IOC_GETSNAPFLAGS		_IOR('f', 13, long)
+#define EXT2_IOC_SETSNAPFLAGS		_IOW('f', 14, long)
+
+/*
+ * Snapshot status/control flags for lssnap/chsnap.
+ * The flags below must appear in the same order as they do in ext4 dynamic
+ * inode state flags enum. GETSNAPFLAGS ioctl shifts them down to lower 8 bits.
+ */
+enum {
+	EXT4_SNAPSHOT_LIST,		/* snapshot is on list (S) */
+	EXT4_SNAPSHOT_ENABLED,		/* snapshot is enabled (n) */
+	EXT4_SNAPSHOT_ACTIVE,		/* snapshot is active  (a) */
+	EXT4_SNAPSHOT_INUSE,		/* snapshot is in-use  (p) */
+	EXT4_SNAPSHOT_DELETED,		/* snapshot is deleted (s) */
+	EXT4_SNAPSHOT_SHRUNK,		/* snapshot was shrunk (h) */
+	EXT4_SNAPSHOT_OPEN,		/* snapshot is mounted (o) */
+	EXT4_SNAPSHOT_TAGGED,		/* snapshot is tagged  (t) */
+};
+
+
+#ifdef EXT2FS_SNAPSHOT_CTL_OLD
+/* Old snapshot flags for backward compatibility with next3 */
+#define NEXT3_SNAPFILE_LIST_FL		0x00000100 /* snapshot is on list */
+#define NEXT3_SNAPFILE_ENABLED_FL	0x00000200 /* snapshot is enabled */
+#define NEXT3_SNAPFILE_ACTIVE_FL	0x00000400 /* snapshot is active */
+#define NEXT3_SNAPFILE_INUSE_FL		0x00000800 /* snapshot is in-use */
+#define NEXT3_SNAPFILE_DELETED_FL	0x04000000 /* snapshot is deleted */
+#define NEXT3_SNAPFILE_SHRUNK_FL	0x08000000 /* snapshot is shrunk */
+#define NEXT3_SNAPFILE_OPEN_FL		0x10000000 /* snapshot is mounted */
+#define NEXT3_SNAPFILE_TAGGED_FL	0x20000000 /* snapshot is tagged */
+
+#endif
+#endif
 
 /*
  * Structure of an inode on the disk
@@ -428,7 +508,11 @@ struct ext2_inode_large {
 #define i_size_high	i_dir_acl
 
 #if defined(__KERNEL__) || defined(__linux__)
+#ifdef EXT2FS_SNAPSHOT_ON_DISK
+#define i_next_snapshot	osd1.linux1.l_i_version
+#else
 #define i_reserved1	osd1.linux1.l_i_reserved1
+#endif
 #define i_frag		osd2.linux2.l_i_frag
 #define i_fsize		osd2.linux2.l_i_fsize
 #define i_uid_low	i_uid
@@ -644,6 +728,19 @@ struct ext2_super_block {
  */
 #define EXT3_JNL_BACKUP_BLOCKS	1
 
+#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL
+/*
+ * A Next3 'big' journal needs to accomodate extra snapshot COW credits.
+ * Default journal size accomodates maximum possible COW credits.
+ * Minimum required journal size accomodates the avarage COW credits.
+ */
+#define EXT3_DEF_JOURNAL_BLOCKS		32768
+#define NEXT3_AVG_COW_CREDITS		16
+#define NEXT3_MAX_COW_CREDITS		24
+#define NEXT3_MIN_JOURNAL_BLOCKS	(EXT3_DEF_JOURNAL_BLOCKS*NEXT3_AVG_COW_CREDITS)
+#define NEXT3_DEF_JOURNAL_BLOCKS	(EXT3_DEF_JOURNAL_BLOCKS*NEXT3_MAX_COW_CREDITS)
+
+#endif
 /*
  * Feature set definitions
  */
@@ -663,6 +760,9 @@ struct ext2_super_block {
 #define EXT2_FEATURE_COMPAT_DIR_INDEX		0x0020
 #define EXT2_FEATURE_COMPAT_LAZY_BG		0x0040
 #define EXT2_FEATURE_COMPAT_EXCLUDE_INODE	0x0080
+#ifdef EXT2FS_SNAPSHOT_ON_DISK
+#define EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP	0x0100
+#endif
 
 #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER	0x0001
 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE	0x0002
@@ -688,10 +788,18 @@ struct ext2_super_block {
 
 #define EXT2_FEATURE_COMPAT_SUPP	0
 #define EXT2_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE)
+#ifdef EXT2FS_SNAPSHOT_ON_DISK
 #define EXT2_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
 					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+					 EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\
 					 EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \
 					 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+#else
+#define EXT2_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+					 EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \
+					 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+#endif
 
 /*
  * Default values for user and/or group using reserved blocks
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index cf76562..f3a1119 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -38,6 +38,12 @@ extern "C" {
  */
 #define SUPERBLOCK_OFFSET	1024
 #define SUPERBLOCK_SIZE 	1024
+/*
+ * When blocksize > 2K, the space after the superblock is used as 
+ * a buffer to record error messages (in 256 bytes records).
+ */
+#define MSGBUF_OFFSET 		(SUPERBLOCK_OFFSET+SUPERBLOCK_SIZE)
+#define MSGBUF_RECLEN		256
 
 /*
  * The last ext2fs revision level that this version of the library is
@@ -173,7 +179,12 @@ typedef struct ext2_file *ext2_file_t;
 #define EXT2_FLAG_EXCLUSIVE		0x4000
 #define EXT2_FLAG_SOFTSUPP_FEATURES	0x8000
 #define EXT2_FLAG_NOFREE_ON_ERROR	0x10000
+#define EXT2_FLAG_64BITS		0x20000
+#define EXT2_FLAG_PRINT_PROGRESS	0x40000
 #define EXT2_FLAG_DIRECT_IO		0x80000
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+#define EXT2_FLAG_EXCLUDE_DIRTY		0x100000
+#endif
 
 /*
  * Special flag in the ext2 inode i_flag field that means that this is
@@ -188,6 +199,8 @@ typedef struct ext2_file *ext2_file_t;
  */
 #define EXT2_MKJOURNAL_V1_SUPER	0x0000001
 
+#define opaque_ext2_group_desc ext2_group_desc
+
 struct struct_ext2_filsys {
 	errcode_t			magic;
 	io_channel			io;
@@ -198,10 +211,14 @@ struct struct_ext2_filsys {
 	int				fragsize;
 	dgrp_t				group_desc_count;
 	unsigned long			desc_blocks;
-	struct ext2_group_desc *	group_desc;
+	struct opaque_ext2_group_desc *	group_desc;
 	int				inode_blocks_per_group;
 	ext2fs_inode_bitmap		inode_map;
 	ext2fs_block_bitmap		block_map;
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	ext2fs_block_bitmap		exclude_map;
+#endif
+	/* XXX FIXME-64: not 64-bit safe, but not used? */
 	errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks);
 	errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino);
 	errcode_t (*write_bitmaps)(ext2_filsys fs);
@@ -503,12 +520,23 @@ typedef struct ext2_icount *ext2_icount_t;
 /*
  * Features supported by this version of the library
  */
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
 #define EXT2_LIB_FEATURE_COMPAT_SUPP	(EXT2_FEATURE_COMPAT_DIR_PREALLOC|\
 					 EXT2_FEATURE_COMPAT_IMAGIC_INODES|\
 					 EXT3_FEATURE_COMPAT_HAS_JOURNAL|\
+					 EXT2_FEATURE_COMPAT_EXCLUDE_INODE|\
+					 EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP|\
 					 EXT2_FEATURE_COMPAT_RESIZE_INODE|\
 					 EXT2_FEATURE_COMPAT_DIR_INDEX|\
 					 EXT2_FEATURE_COMPAT_EXT_ATTR)
+#else
+#define EXT2_LIB_FEATURE_COMPAT_SUPP	(EXT2_FEATURE_COMPAT_DIR_PREALLOC|\
+					 EXT2_FEATURE_COMPAT_IMAGIC_INODES|\
+					 EXT3_FEATURE_COMPAT_HAS_JOURNAL|\
+					 EXT2_FEATURE_COMPAT_RESIZE_INODE|\
+					 EXT2_FEATURE_COMPAT_DIR_INDEX|\
+					 EXT2_FEATURE_COMPAT_EXT_ATTR)
+#endif
 
 /* This #ifdef is temporary until compression is fully supported */
 #ifdef ENABLE_COMPRESSION
@@ -533,12 +561,22 @@ typedef struct ext2_icount *ext2_icount_t;
 					 EXT3_FEATURE_INCOMPAT_EXTENTS|\
 					 EXT4_FEATURE_INCOMPAT_FLEX_BG)
 #endif
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
 #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
 					 EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
 					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\
+					 EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\
 					 EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\
 					 EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\
 					 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)
+#else
+#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
+					 EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
+					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\
+					 EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\
+					 EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\
+					 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)
+#endif
 
 /*
  * These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed
@@ -646,8 +684,14 @@ extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
 				    ext2fs_generic_bitmap *dest);
 extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs);
 extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+extern errcode_t ext2fs_write_exclude_bitmap (ext2_filsys fs);
+#endif
 extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs);
 extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+extern errcode_t ext2fs_read_exclude_bitmap (ext2_filsys fs);
+#endif
 extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
 					      const char *descr,
 					      ext2fs_block_bitmap *ret);
@@ -1073,6 +1117,10 @@ extern errcode_t ext2fs_add_journal_device(ext2_filsys fs,
 extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size,
 					  int flags);
 extern int ext2fs_default_journal_size(__u64 blocks);
+#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL
+extern int ext2fs_big_journal_size(int factor, __u64 blocks);
+extern int ext2fs_check_journal_size(ext2_filsys fs);
+#endif
 
 /* openfs.c */
 extern errcode_t ext2fs_open(const char *name, int flags, int superblock,
@@ -1117,6 +1165,15 @@ extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
 
 /* res_gdt.c */
 extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+extern errcode_t ext2fs_create_exclude_inode(ext2_filsys fs, int flags);
+
+/* exclude inode creation flags */
+#define EXCLUDE_READONLY 0 /* only read exclude bitmap blocks */
+#define EXCLUDE_ALLOC	 1 /* allocate missing exclude bitmap blocks */
+#define EXCLUDE_RESET	 2 /* reset exclude bitmap blocks to zero */
+#define EXCLUDE_CREATE	 3 /* alloc and/or reset exclude bitmap blocks */
+#endif
 
 /* swapfs.c */
 extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
@@ -1212,7 +1269,8 @@ _INLINE_ errcode_t ext2fs_get_memalign(unsigned long size,
 
 	if (align == 0)
 		align = 8;
-	if (retval = posix_memalign((void **) ptr, align, size)) {
+	retval = posix_memalign((void **) ptr, align, size);
+	if (retval) {
 		if (retval == ENOMEM)
 			return EXT2_ET_NO_MEMORY;
 		return retval;
@@ -1324,6 +1382,16 @@ _INLINE_ void ext2fs_mark_bb_dirty(ext2_filsys fs)
 	fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED;
 }
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+/*
+ * Mark the exclude bitmap as dirty
+ */
+_INLINE_ void ext2fs_mark_exclude_dirty(ext2_filsys fs)
+{
+	fs->flags |= EXT2_FLAG_EXCLUDE_DIRTY | EXT2_FLAG_CHANGED;
+}
+
+#endif
 /*
  * Check to see if a filesystem's inode bitmap is dirty
  */
@@ -1340,6 +1408,16 @@ _INLINE_ int ext2fs_test_bb_dirty(ext2_filsys fs)
 	return (fs->flags & EXT2_FLAG_BB_DIRTY);
 }
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+/*
+ * Check to see if a filesystem's exclude bitmap is dirty
+ */
+_INLINE_ int ext2fs_test_exclude_dirty(ext2_filsys fs)
+{
+	return (fs->flags & EXT2_FLAG_EXCLUDE_DIRTY);
+}
+
+#endif
 /*
  * Return the group # of a block
  */
diff --git a/lib/ext2fs/freefs.c b/lib/ext2fs/freefs.c
index 5c35bb6..9055b09 100644
--- a/lib/ext2fs/freefs.c
+++ b/lib/ext2fs/freefs.c
@@ -42,6 +42,10 @@ void ext2fs_free(ext2_filsys fs)
 		ext2fs_free_block_bitmap(fs->block_map);
 	if (fs->inode_map)
 		ext2fs_free_inode_bitmap(fs->inode_map);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	if (fs->exclude_map)
+		ext2fs_free_inode_bitmap(fs->exclude_map);
+#endif
 
 	if (fs->badblocks)
 		ext2fs_badblocks_list_free(fs->badblocks);
diff --git a/lib/ext2fs/i_block.c b/lib/ext2fs/i_block.c
index 822776d..f96f200 100644
--- a/lib/ext2fs/i_block.c
+++ b/lib/ext2fs/i_block.c
@@ -31,8 +31,15 @@ errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode,
 {
 	unsigned long long b = inode->i_blocks;
 
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+	if (!((fs->super->s_feature_ro_compat &
+	      EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
+	     /* snapshot file always supports the 'huge_file' flag */
+	     (inode->i_flags & EXT4_SNAPFILE_FL)) ||
+#else
 	if (!(fs->super->s_feature_ro_compat &
 	      EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
+#endif
 	    !(inode->i_flags & EXT4_HUGE_FILE_FL))
 	    num_blocks *= fs->blocksize / 512;
 
@@ -53,8 +60,15 @@ errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode,
 {
 	unsigned long long b = inode->i_blocks;
 
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+	if (!((fs->super->s_feature_ro_compat &
+	      EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
+	     /* snapshot file always supports the 'huge_file' flag */
+	     (inode->i_flags & EXT4_SNAPFILE_FL)) ||
+#else
 	if (!(fs->super->s_feature_ro_compat &
 	      EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
+#endif
 	    !(inode->i_flags & EXT4_HUGE_FILE_FL))
 	    num_blocks *= fs->blocksize / 512;
 
@@ -74,8 +88,15 @@ errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode,
 
 errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b)
 {
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+	if (!((fs->super->s_feature_ro_compat &
+	      EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
+	     /* snapshot file always supports the 'huge_file' flag */
+	     (inode->i_flags & EXT4_SNAPFILE_FL)) ||
+#else
 	if (!(fs->super->s_feature_ro_compat &
 	      EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
+#endif
 	    !(inode->i_flags & EXT4_HUGE_FILE_FL))
 		b *= fs->blocksize / 512;
 
diff --git a/lib/ext2fs/mkjournal.c b/lib/ext2fs/mkjournal.c
index 4a81f4b..c0c86c0 100644
--- a/lib/ext2fs/mkjournal.c
+++ b/lib/ext2fs/mkjournal.c
@@ -388,6 +388,49 @@ int ext2fs_default_journal_size(__u64 blocks)
 	return 32768;
 }
 
+#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL
+/* 
+ * Big journal is up to X times bigger than the default journal
+ * to accomodate snapshot COW credits in transactions.
+ * journal size is restricted to 1/32 of the filesystem size
+ */
+int ext2fs_big_journal_size(int factor, __u64 blocks)
+{
+	int mega_blocks = blocks >> 20;
+	if (!mega_blocks)
+		return ext2fs_default_journal_size(blocks);
+
+	if (mega_blocks < factor)
+		/* 32K/1M = 1/32 of filesystem size */
+		return EXT3_DEF_JOURNAL_BLOCKS*mega_blocks;
+	
+	/* X times bigger than the default journal */
+	return EXT3_DEF_JOURNAL_BLOCKS*factor;
+}
+
+/*
+ * Return the number of blocks in the journal inode
+ */
+int ext2fs_check_journal_size(ext2_filsys fs)
+{
+	struct ext2_inode j_inode;
+	int j_blocks;
+
+	if (!(fs->super->s_feature_compat &
+		EXT3_FEATURE_COMPAT_HAS_JOURNAL) ||
+		!fs->super->s_journal_inum)
+		return 0;
+
+	if (ext2fs_read_inode(fs, fs->super->s_journal_inum, &j_inode))
+		return -1;
+
+	/* read journal inode size */
+	j_blocks = j_inode.i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
+	
+	return j_blocks;
+}
+
+#endif
 /*
  * This function adds a journal device to a filesystem
  */
diff --git a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c
index 424d867..787bd5a 100644
--- a/lib/ext2fs/res_gdt.c
+++ b/lib/ext2fs/res_gdt.c
@@ -217,3 +217,220 @@ out_free:
 	return retval;
 }
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+#define ext2fs_group_desc(fs, gdp, grp) (gdp)+(grp)
+
+/*
+ * ext2fs_create_exclude_inode():
+ * the exclude inode owns all the exclude bitmap blocks (one per block group)
+ * the exclude bitmap blocks are double indirectly linked to the exclude inode
+ * the exclude bitmap allocation goal is the first block of the block group
+ * exclude inode creation @flags:
+ * EXCLUDE_ALLOC (1) - allocate missing exclude bitmap blocks
+ * EXCLUDE_RESET (2) - reset exclude bitmap to zero
+ */
+errcode_t ext2fs_create_exclude_inode(ext2_filsys fs, int flags)
+{
+	errcode_t		retval, retval2;
+	struct ext2_super_block	*sb;
+	struct ext2_inode	inode;
+	__u32			*dindir_buf, *indir_buf, *data_buf;
+	unsigned long long	apb, inode_size;
+	blk_t		dindir_blk, indir_blk, data_blk;
+	int			gdt_dirty = 0, dindir_dirty = 0, inode_dirty = 0;
+	int			indir_dirty = 0, data_dirty = 0;
+	int 		dindir_off, indir_off, grp, i, max_groups;
+	int create = flags & EXCLUDE_ALLOC;
+	int reset = flags & EXCLUDE_RESET;
+
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	sb = fs->super;
+
+	retval = ext2fs_get_array(3, fs->blocksize, &dindir_buf);
+	if (retval)
+		goto out_free;
+	indir_buf = (__u32 *)((char *)dindir_buf + 1*fs->blocksize);
+	data_buf = (__u32 *)((char *)dindir_buf + 2*fs->blocksize);
+
+	retval = ext2fs_read_inode(fs, EXT2_EXCLUDE_INO, &inode);
+	if (retval)
+		goto out_free;
+
+#ifdef EXCLUDE_INO_PROGRESS
+	printf("Reserving exclude bitmap blocks:            ");
+#endif
+
+	apb = EXT2_ADDR_PER_BLOCK(sb);
+	if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) {
+#ifdef EXCLUDE_INO_DEBUG
+		printf("reading exclude inode dindir %u\n", dindir_blk);
+#endif
+		retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf);
+		if (retval)
+			goto out_free;
+	} else if (create) {
+		blk_t goal = sb->s_first_data_block + fs->desc_blocks +
+			sb->s_reserved_gdt_blocks + 2 +
+			fs->inode_blocks_per_group;
+
+		retval = ext2fs_alloc_block(fs, goal, (char *)dindir_buf, &dindir_blk);
+		if (retval)
+			goto out_free;
+		inode.i_mode = LINUX_S_IFREG | 0600;
+		inode.i_links_count = 1;
+		inode.i_block[EXT2_DIND_BLOCK] = dindir_blk;
+		ext2fs_iblk_set(fs, &inode, 1);
+#ifdef EXCLUDE_INO_DEBUG
+		printf("allocated exclude inode dindir %u\n", dindir_blk);
+#endif
+		dindir_dirty = inode_dirty = 1;
+		inode.i_ctime = fs->now ? fs->now : time(0);
+	}
+
+	/*
+	 * allocate exclude bitmaps for all existing block groups
+	 * and allocate indirect blocks for all reserved block groups
+	 */
+	max_groups = fs->desc_blocks + sb->s_reserved_gdt_blocks;
+	max_groups *= EXT2_DESC_PER_BLOCK(sb);
+	for (grp = 0; grp < max_groups; grp++) {
+		struct ext2_group_desc *gd =
+			ext2fs_group_desc(fs, fs->group_desc, grp);
+
+		dindir_off = grp/apb;
+		indir_off = grp%apb;
+		if (indir_off == 0) {
+			/* flush current indirect block */
+			if (indir_dirty) {
+				retval = ext2fs_write_ind_block(fs, indir_blk, indir_buf);
+				if (retval)
+					goto out_dindir;
+				indir_dirty = 0;
+			}
+			/* read/alloc next indirect block */
+			if ((indir_blk = dindir_buf[dindir_off])) {
+#ifdef EXCLUDE_INO_DEBUG
+				printf("reading exclude inode indir %u\n", indir_blk);
+#endif
+				retval = ext2fs_read_ind_block(fs, indir_blk, indir_buf);
+				if (retval)
+					goto out_dindir;
+			} else if (create) {
+				retval = ext2fs_alloc_block(fs, dindir_blk, (char *)indir_buf, &indir_blk);
+				if (retval)
+					goto out_dindir;
+				dindir_buf[dindir_off] = indir_blk;
+				ext2fs_iblk_add_blocks(fs, &inode, 1);
+#ifdef EXCLUDE_INO_DEBUG
+				printf("allocated exclude inode indir %u\n", indir_blk);
+#endif
+				dindir_dirty = inode_dirty = 1;
+			}
+		}
+
+		if (grp >= fs->group_desc_count)
+			continue;
+		/* read/alloc exclude bitmap block */
+		data_blk = indir_buf[indir_off];
+		if (!data_blk && create) {
+			/* allocate exclude bitmap block */
+			retval = ext2fs_alloc_block(fs, gd->bg_block_bitmap,
+					(char *)data_buf, &data_blk);
+			if (retval)
+				goto out_dindir;
+			indir_buf[indir_off] = data_blk;
+			ext2fs_iblk_add_blocks(fs, &inode, 1);
+#ifdef EXCLUDE_INO_DEBUG
+			printf("allocated exclude bitmap block %u\n", data_blk);
+#endif
+			indir_dirty = inode_dirty = 1;
+		} else if (data_blk && reset) {
+			/* reset exclude bitmap block */
+#ifdef EXCLUDE_INO_DEBUG
+			printf("reading exclude bitmap block %u\n", data_blk);
+#endif
+			retval = io_channel_read_blk(fs->io, data_blk, 1,
+					data_buf);
+			if (retval)
+				goto out_dindir;
+			/* zero data block */
+			for (i = 0; i < apb; i++) {
+				if (!data_buf[i])
+					continue;
+				data_buf[i] = 0;
+				data_dirty = 1;
+			}
+			if (data_dirty) {
+				retval = io_channel_write_blk(fs->io, data_blk,
+						1, data_buf);
+				if (retval)
+					goto out_dindir;
+				data_dirty = 0;
+			}
+		}
+		if (!gd->bg_exclude_bitmap) {
+			gd->bg_exclude_bitmap = data_blk;
+			ext2fs_group_desc_csum_set(fs, grp);
+			gdt_dirty = 1;
+		}
+#ifdef EXCLUDE_INO_PROGRESS
+		printf("\b\b\b\b\b\b\b\b\b\b\b%5d/%5d", grp,
+				fs->group_desc_count);
+#endif
+	}
+#ifdef EXCLUDE_INO_PROGRESS
+	printf("\b\b\b\b\b\b\b\b\b\b\bdone       \n");
+#endif
+
+	/* exclude bitmap was reset to zero - clear fix_exclude flag */
+	if (sb->s_flags & EXT2_FLAGS_FIX_EXCLUDE) {
+		sb->s_flags &= ~EXT2_FLAGS_FIX_EXCLUDE;
+		ext2fs_mark_super_dirty(fs);
+	}
+
+out_dindir:
+	if (indir_dirty) {
+		retval2 = ext2fs_write_ind_block(fs, indir_blk, indir_buf);
+		if (!retval)
+			retval = retval2;
+	}
+	if (dindir_dirty) {
+		retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf);
+		if (!retval)
+			retval = retval2;
+	}
+out_inode:
+	if (inode_dirty) {
+		inode_size = fs->group_desc_count + apb + EXT2_NDIR_BLOCKS;
+		inode_size *= fs->blocksize;
+		inode.i_size = inode_size & 0xFFFFFFFF;
+		inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0);
+		retval2 = ext2fs_write_new_inode(fs, EXT2_EXCLUDE_INO, &inode);
+		if (!retval)
+			retval = retval2;
+		/* need to write out block bitmaps and group descriptors */
+		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+	}
+	if (gdt_dirty) {
+		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+		ext2fs_mark_super_dirty(fs);
+	}
+        if (!(fs->super->s_feature_compat &
+				EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP)) {
+		/* set exclude_bitmap along with exclude_inode */
+		fs->super->s_feature_compat |=
+				EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP;
+		ext2fs_mark_super_dirty(fs);
+	}
+#ifdef EXCLUDE_INO_DEBUG
+	printf("inode.i_blocks = %u, i_size = %u\n",
+			inode.i_blocks, inode.i_size);
+#endif
+out_free:
+	ext2fs_free_mem(&dindir_buf);
+	return retval;
+}
+
+#endif
diff --git a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c
index 4e77a8f..c681660 100644
--- a/lib/ext2fs/rw_bitmaps.c
+++ b/lib/ext2fs/rw_bitmaps.c
@@ -25,19 +25,47 @@
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
+
+#define blk64_t blk_t
+#define __u64 __u32 
+#define io_channel_read_blk64 io_channel_read_blk
+#define io_channel_write_blk64 io_channel_write_blk
+#define ext2fs_get_block_bitmap_range2 ext2fs_get_block_bitmap_range
+#define ext2fs_set_block_bitmap_range2 ext2fs_set_block_bitmap_range
+#define ext2fs_block_bitmap_loc(fs, group) \
+	fs->group_desc[group].bg_block_bitmap
+#define ext2fs_exclude_bitmap_loc(fs, group) \
+	fs->group_desc[group].bg_exclude_bitmap
+#define ext2fs_inode_bitmap_loc(fs, group) \
+	fs->group_desc[group].bg_inode_bitmap
+#define ext2fs_bg_flags_test(fs, group, bg_flag) \
+	fs->group_desc[group].bg_flags & bg_flag
+#define ext2fs_blocks_count(sb) sb->s_blocks_count
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
 #include "e2image.h"
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block,
+		int do_exclude)
+#else
 static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
+#endif
 {
 	dgrp_t 		i;
 	unsigned int	j;
 	int		block_nbytes, inode_nbytes;
 	unsigned int	nbits;
 	errcode_t	retval;
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	char 		*block_buf, *inode_buf, *exclude_buf;
+#else
 	char 		*block_buf, *inode_buf;
+#endif
 	int		csum_flag = 0;
-	blk_t		blk;
-	blk_t		blk_itr = fs->super->s_first_data_block;
+	blk64_t		blk;
+	blk64_t		blk_itr = fs->super->s_first_data_block;
 	ext2_ino_t	ino_itr = 1;
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -45,6 +73,12 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 	if (!(fs->flags & EXT2_FLAG_RW))
 		return EXT2_ET_RO_FILSYS;
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	if (!EXT2_HAS_COMPAT_FEATURE(fs->super,
+				EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP))
+		do_exclude = 0;
+
+#endif
 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
 		csum_flag = 1;
@@ -58,6 +92,16 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 			return retval;
 		memset(block_buf, 0xff, fs->blocksize);
 	}
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	if (do_exclude) {
+		block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+		retval = ext2fs_get_memalign(fs->blocksize, fs->blocksize,
+					     &exclude_buf);
+		if (retval)
+			return retval;
+		memset(exclude_buf, 0xff, fs->blocksize);
+	}
+#endif
 	if (do_inode) {
 		inode_nbytes = (size_t)
 			((EXT2_INODES_PER_GROUP(fs->super)+7) / 8);
@@ -69,34 +113,67 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 	}
 
 	for (i = 0; i < fs->group_desc_count; i++) {
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+		if (!do_block && !do_exclude)
+#else
 		if (!do_block)
+#endif
 			goto skip_block_bitmap;
 
-		if (csum_flag && fs->group_desc[i].bg_flags &
-		    EXT2_BG_BLOCK_UNINIT)
+		if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT)
+		    )
 			goto skip_this_block_bitmap;
 
-		retval = ext2fs_get_block_bitmap_range(fs->block_map,
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+		if (do_block)
+			retval = ext2fs_get_block_bitmap_range2(fs->block_map,
+					blk_itr, block_nbytes << 3, block_buf);
+		if (retval)
+			return retval;
+
+		if (do_exclude)
+			retval = ext2fs_get_block_bitmap_range2(fs->exclude_map,
+					blk_itr, block_nbytes << 3, exclude_buf);
+#else
+		retval = ext2fs_get_block_bitmap_range2(fs->block_map,
 				blk_itr, block_nbytes << 3, block_buf);
+#endif
 		if (retval)
 			return retval;
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+		if (do_block && i == fs->group_desc_count - 1) {
+#else
 		if (i == fs->group_desc_count - 1) {
+#endif
 			/* Force bitmap padding for the last group */
-			nbits = ((fs->super->s_blocks_count
-				  - fs->super->s_first_data_block)
-				 % EXT2_BLOCKS_PER_GROUP(fs->super));
+			nbits = ((ext2fs_blocks_count(fs->super)
+				  - (__u64) fs->super->s_first_data_block)
+				 % (__u64) EXT2_BLOCKS_PER_GROUP(fs->super));
 			if (nbits)
 				for (j = nbits; j < fs->blocksize * 8; j++)
 					ext2fs_set_bit(j, block_buf);
 		}
-		blk = fs->group_desc[i].bg_block_bitmap;
+		blk = ext2fs_block_bitmap_loc(fs, i);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+		if (do_block && blk) {
+#else
 		if (blk) {
-			retval = io_channel_write_blk(fs->io, blk, 1,
-						      block_buf);
+#endif
+			retval = io_channel_write_blk64(fs->io, blk, 1,
+							block_buf);
 			if (retval)
 				return EXT2_ET_BLOCK_BITMAP_WRITE;
 		}
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+		blk = ext2fs_exclude_bitmap_loc(fs, i);
+		if (do_exclude && blk) {
+			retval = io_channel_write_blk64(fs->io, blk, 1,
+						      exclude_buf);
+			if (retval)
+				return EXT2_ET_BLOCK_BITMAP_WRITE;
+		}
+#endif
 	skip_this_block_bitmap:
 		blk_itr += block_nbytes << 3;
 	skip_block_bitmap:
@@ -104,8 +181,8 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 		if (!do_inode)
 			continue;
 
-		if (csum_flag && fs->group_desc[i].bg_flags &
-		    EXT2_BG_INODE_UNINIT)
+		if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT)
+		    )
 			goto skip_this_inode_bitmap;
 
 		retval = ext2fs_get_inode_bitmap_range(fs->inode_map,
@@ -113,9 +190,9 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 		if (retval)
 			return retval;
 
-		blk = fs->group_desc[i].bg_inode_bitmap;
+		blk = ext2fs_inode_bitmap_loc(fs, i);
 		if (blk) {
-			retval = io_channel_write_blk(fs->io, blk, 1,
+			retval = io_channel_write_blk64(fs->io, blk, 1,
 						      inode_buf);
 			if (retval)
 				return EXT2_ET_INODE_BITMAP_WRITE;
@@ -135,10 +212,19 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 	return 0;
 }
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block,
+		int do_exclude)
+#else
 static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
+#endif
 {
 	dgrp_t i;
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	char *block_bitmap = 0, *inode_bitmap = 0, *exclude_bitmap = 0;
+#else
 	char *block_bitmap = 0, *inode_bitmap = 0;
+#endif
 	char *buf;
 	errcode_t retval;
 	int block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
@@ -146,9 +232,9 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 	int csum_flag = 0;
 	int do_image = fs->flags & EXT2_FLAG_IMAGE_FILE;
 	unsigned int	cnt;
-	blk_t	blk;
-	blk_t	blk_itr = fs->super->s_first_data_block;
-	blk_t   blk_cnt;
+	blk64_t	blk;
+	blk64_t	blk_itr = fs->super->s_first_data_block;
+	blk64_t   blk_cnt;
 	ext2_ino_t ino_itr = 1;
 	ext2_ino_t ino_cnt;
 
@@ -156,6 +242,12 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 
 	fs->write_bitmaps = ext2fs_write_bitmaps;
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	if (!EXT2_HAS_COMPAT_FEATURE(fs->super,
+				EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP))
+		do_exclude = 0;
+
+#endif
 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
 		csum_flag = 1;
@@ -180,7 +272,29 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 			
 		if (retval)
 			goto cleanup;
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	}
+	if (do_exclude) {
+		if (fs->exclude_map)
+			ext2fs_free_block_bitmap(fs->exclude_map);
+		strcpy(buf, "exclude bitmap for ");
+		strcat(buf, fs->device_name);
+		retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->exclude_map);
+		if (retval)
+			goto cleanup;
+		if (do_image)
+			retval = ext2fs_get_mem(fs->blocksize, &exclude_bitmap);
+		else
+			retval = ext2fs_get_memalign((unsigned) block_nbytes,
+						     fs->blocksize,
+						     &exclude_bitmap);	
+		if (retval)
+			goto cleanup;
+	}
+	if (!do_block && !do_exclude)
+#else
 	} else
+#endif
 		block_nbytes = 0;
 	if (do_inode) {
 		if (fs->inode_map)
@@ -202,7 +316,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 		blk = (fs->image_header->offset_inodemap / fs->blocksize);
 		ino_cnt = fs->super->s_inodes_count;
 		while (inode_nbytes > 0) {
-			retval = io_channel_read_blk(fs->image_io, blk++,
+			retval = io_channel_read_blk64(fs->image_io, blk++,
 						     1, inode_bitmap);
 			if (retval)
 				goto cleanup;
@@ -219,17 +333,24 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 		}
 		blk = (fs->image_header->offset_blockmap /
 		       fs->blocksize);
-		blk_cnt = EXT2_BLOCKS_PER_GROUP(fs->super) *
+		blk_cnt = (blk64_t)EXT2_BLOCKS_PER_GROUP(fs->super) *
 			fs->group_desc_count;
 		while (block_nbytes > 0) {
-			retval = io_channel_read_blk(fs->image_io, blk++,
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+			if (do_exclude) {
+				retval = EXT2_ET_BLOCK_BITMAP_READ;
+				goto cleanup;
+			}
+
+#endif
+			retval = io_channel_read_blk64(fs->image_io, blk++,
 						     1, block_bitmap);
 			if (retval)
 				goto cleanup;
 			cnt = fs->blocksize << 3;
 			if (cnt > blk_cnt)
 				cnt = blk_cnt;
-			retval = ext2fs_set_block_bitmap_range(fs->block_map,
+			retval = ext2fs_set_block_bitmap_range2(fs->block_map,
 				       blk_itr, cnt, block_bitmap);
 			if (retval)
 				goto cleanup;
@@ -242,13 +363,13 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 
 	for (i = 0; i < fs->group_desc_count; i++) {
 		if (block_bitmap) {
-			blk = fs->group_desc[i].bg_block_bitmap;
-			if (csum_flag && fs->group_desc[i].bg_flags &
-			    EXT2_BG_BLOCK_UNINIT &&
+			blk = ext2fs_block_bitmap_loc(fs, i);
+			if (csum_flag &&
+			    ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) &&
 			    ext2fs_group_desc_csum_verify(fs, i))
 				blk = 0;
 			if (blk) {
-				retval = io_channel_read_blk(fs->io, blk,
+				retval = io_channel_read_blk64(fs->io, blk,
 					     -block_nbytes, block_bitmap);
 				if (retval) {
 					retval = EXT2_ET_BLOCK_BITMAP_READ;
@@ -257,20 +378,47 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
 			} else
 				memset(block_bitmap, 0, block_nbytes);
 			cnt = block_nbytes << 3;
-			retval = ext2fs_set_block_bitmap_range(fs->block_map,
+			retval = ext2fs_set_block_bitmap_range2(fs->block_map,
 					       blk_itr, cnt, block_bitmap);
 			if (retval)
 				goto cleanup;
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+		}
+		if (exclude_bitmap) {
+			blk = ext2fs_exclude_bitmap_loc(fs, i);
+			if (csum_flag &&
+			    ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) &&
+			    ext2fs_group_desc_csum_verify(fs, i))
+				blk = 0;
+			if (blk) {
+				retval = io_channel_read_blk64(fs->io, blk,
+					     -block_nbytes, exclude_bitmap);
+				if (retval) {
+					retval = EXT2_ET_BLOCK_BITMAP_READ;
+					goto cleanup;
+				}
+			} else
+				memset(exclude_bitmap, 0, block_nbytes);
+			cnt = block_nbytes << 3;
+			retval = ext2fs_set_block_bitmap_range2(fs->exclude_map,
+					       blk_itr, cnt, exclude_bitmap);
+			if (retval)
+				goto cleanup;
+		}
+		if (block_nbytes)
+			blk_itr += block_nbytes << 3;
+#else
 			blk_itr += block_nbytes << 3;
 		}
+#endif
 		if (inode_bitmap) {
-			blk = fs->group_desc[i].bg_inode_bitmap;
-			if (csum_flag && fs->group_desc[i].bg_flags &
-			    EXT2_BG_INODE_UNINIT &&
+			blk = ext2fs_inode_bitmap_loc(fs, i);
+			if (csum_flag &&
+			    ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) &&
 			    ext2fs_group_desc_csum_verify(fs, i))
 				blk = 0;
 			if (blk) {
-				retval = io_channel_read_blk(fs->io, blk,
+				retval = io_channel_read_blk64(fs->io, blk,
 					     -inode_nbytes, inode_bitmap);
 				if (retval) {
 					retval = EXT2_ET_INODE_BITMAP_READ;
@@ -291,6 +439,10 @@ success_cleanup:
 		ext2fs_free_mem(&inode_bitmap);
 	if (block_bitmap)
 		ext2fs_free_mem(&block_bitmap);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	if (exclude_bitmap)
+		ext2fs_free_mem(&exclude_bitmap);
+#endif
 	return 0;
 
 cleanup:
@@ -306,6 +458,10 @@ cleanup:
 		ext2fs_free_mem(&inode_bitmap);
 	if (block_bitmap)
 		ext2fs_free_mem(&block_bitmap);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	if (exclude_bitmap)
+		ext2fs_free_mem(&exclude_bitmap);
+#endif
 	if (buf)
 		ext2fs_free_mem(&buf);
 	return retval;
@@ -313,39 +469,88 @@ cleanup:
 
 errcode_t ext2fs_read_inode_bitmap(ext2_filsys fs)
 {
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	return read_bitmaps(fs, 1, 0, 0);
+#else
 	return read_bitmaps(fs, 1, 0);
+#endif
 }
 
 errcode_t ext2fs_read_block_bitmap(ext2_filsys fs)
 {
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	return read_bitmaps(fs, 0, 1, 0);
+#else
 	return read_bitmaps(fs, 0, 1);
+#endif
+}
+
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+errcode_t ext2fs_read_exclude_bitmap (ext2_filsys fs)
+{
+	return read_bitmaps(fs, 0, 0, 1);
 }
 
+#endif
 errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs)
 {
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	return write_bitmaps(fs, 1, 0, 0);
+#else
 	return write_bitmaps(fs, 1, 0);
+#endif
 }
 
 errcode_t ext2fs_write_block_bitmap (ext2_filsys fs)
 {
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	return write_bitmaps(fs, 0, 1, 0);
+#else
 	return write_bitmaps(fs, 0, 1);
+#endif
 }
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+errcode_t ext2fs_write_exclude_bitmap (ext2_filsys fs)
+{
+	return write_bitmaps(fs, 0, 0, 1);
+}
+
+#endif
 errcode_t ext2fs_read_bitmaps(ext2_filsys fs)
 {
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	if (fs->inode_map && fs->block_map && fs->exclude_map)
+#else
 	if (fs->inode_map && fs->block_map)
+#endif
 		return 0;
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	return read_bitmaps(fs, !fs->inode_map, !fs->block_map, !fs->exclude_map);
+#else
 	return read_bitmaps(fs, !fs->inode_map, !fs->block_map);
+#endif
 }
 
 errcode_t ext2fs_write_bitmaps(ext2_filsys fs)
 {
 	int do_inode = fs->inode_map && ext2fs_test_ib_dirty(fs);
 	int do_block = fs->block_map && ext2fs_test_bb_dirty(fs);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	int do_exclude = fs->exclude_map && ext2fs_test_exclude_dirty(fs);
+#endif
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	if (!do_inode && !do_block && !do_exclude)
+#else
 	if (!do_inode && !do_block)
+#endif
 		return 0;
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+	return write_bitmaps(fs, do_inode, do_block, do_exclude);
+#else
 	return write_bitmaps(fs, do_inode, do_block);
+#endif
 }
diff --git a/misc/chattr.c b/misc/chattr.c
index de33b08..a28ed39 100644
--- a/misc/chattr.c
+++ b/misc/chattr.c
@@ -55,6 +55,10 @@
 #include "nls-enable.h"
 
 static const char * program_name = "chattr";
+#ifdef EXT2FS_SNAPSHOT_CTL
+
+static int chsnap;
+#endif
 
 static int add;
 static int rem;
@@ -81,6 +85,18 @@ static unsigned long sf;
 
 static void usage(void)
 {
+#ifdef EXT2FS_SNAPSHOT_CTL
+	if (chsnap) {
+		fprintf(stderr,
+#ifdef EXT2FS_SNAPSHOT_CTL_OLD
+			_("Usage: %s [-X] [-+=Sn] snapshot files...\n"),
+#else
+			_("Usage: %s [-+=Sn] snapshot files...\n"),
+#endif
+			program_name);
+		exit(1);
+	}
+#endif
 	fprintf(stderr,
 		_("Usage: %s [-RVf] [-+=AacDdeijsSu] [-v version] files...\n"),
 		program_name);
@@ -92,7 +108,11 @@ struct flags_char {
 	char 		optchar;
 };
 
+#ifdef EXT2FS_SNAPSHOT_CTL
+static const struct flags_char ext2_flags_array[] = {
+#else
 static const struct flags_char flags_array[] = {
+#endif
 	{ EXT2_NOATIME_FL, 'A' },
 	{ EXT2_SYNC_FL, 'S' },
 	{ EXT2_DIRSYNC_FL, 'D' },
@@ -106,9 +126,32 @@ static const struct flags_char flags_array[] = {
 	{ EXT2_UNRM_FL, 'u' },
 	{ EXT2_NOTAIL_FL, 't' },
 	{ EXT2_TOPDIR_FL, 'T' },
+#ifdef EXT2FS_SNAPSHOT_CTL
+	{ EXT4_SNAPFILE_FL, 'x' },
+#endif
+	{ 0, 0 }
+};
+
+#ifdef EXT2FS_SNAPSHOT_CTL
+static const struct flags_char *flags_array = ext2_flags_array;
+
+/* Snapshot dynamic state flags */
+static struct flags_char snapshot_flags_array[] = {
+	{ 1UL<<EXT4_SNAPSHOT_LIST, 'S' },
+	{ 1UL<<EXT4_SNAPSHOT_ENABLED, 'n' },
+	{ 0, 0 }
+};
+
+#ifdef EXT2FS_SNAPSHOT_CTL_OLD
+/* Old snapshot flags for backward compatibility with next3 */
+static struct flags_char snapshot_X_flags_array[] = {
+	{ NEXT3_SNAPFILE_LIST_FL, 'S' },
+	{ NEXT3_SNAPFILE_ENABLED_FL, 'n' },
 	{ 0, 0 }
 };
 
+#endif
+#endif
 static unsigned long get_flag(char c)
 {
 	const struct flags_char *fp;
@@ -131,6 +174,12 @@ static int decode_arg (int * i, int argc, char ** argv)
 	{
 	case '-':
 		for (p = &argv[*i][1]; *p; p++) {
+#ifdef EXT2FS_SNAPSHOT_CTL_OLD
+			if (*p == 'X') {
+				flags_array = snapshot_X_flags_array;
+				continue;
+			}
+#endif
 			if (*p == 'R') {
 				recursive = 1;
 				continue;
@@ -193,6 +242,9 @@ static int change_attributes(const char * name)
 	unsigned long flags;
 	STRUCT_STAT	st;
 	int extent_file = 0;
+#ifdef EXT2FS_SNAPSHOT_CTL
+	int ret;
+#endif
 
 	if (LSTAT (name, &st) == -1) {
 		if (!silent)
@@ -201,7 +253,15 @@ static int change_attributes(const char * name)
 		return -1;
 	}
 
+#ifdef EXT2FS_SNAPSHOT_CTL
+	if (chsnap)
+		ret = fgetsnapflags (name, &flags);
+	else
+		ret = fgetflags (name, &flags);
+	if (ret == -1) {
+#else
 	if (fgetflags(name, &flags) == -1) {
+#endif
 		if (!silent)
 			com_err(program_name, errno,
 					_("while reading flags on %s"), name);
@@ -222,7 +282,15 @@ static int change_attributes(const char * name)
 			print_flags (stdout, sf, 0);
 			printf ("\n");
 		}
+#ifdef EXT2FS_SNAPSHOT_CTL
+		if (chsnap)
+			ret = fsetsnapflags (name, sf);
+		else
+			ret = fsetflags (name, sf);
+		if (ret == -1)
+#else
 		if (fsetflags (name, sf) == -1)
+#endif
 			perror (name);
 	} else {
 		if (rem)
@@ -243,7 +311,15 @@ static int change_attributes(const char * name)
 		}
 		if (!S_ISDIR(st.st_mode))
 			flags &= ~EXT2_DIRSYNC_FL;
+#ifdef EXT2FS_SNAPSHOT_CTL
+		if (chsnap)
+			ret = fsetsnapflags (name, flags);
+		else
+			ret = fsetflags (name, flags);
+		if (ret == -1) {
+#else
 		if (fsetflags(name, flags) == -1) {
+#endif
 			if (!silent) {
 				com_err(program_name, errno,
 						_("while setting flags on %s"),
@@ -303,6 +379,13 @@ int main (int argc, char ** argv)
 #endif
 	if (argc && *argv)
 		program_name = *argv;
+#ifdef EXT2FS_SNAPSHOT_CTL
+	i = strlen(program_name);
+	if (i >= 6 && !strcmp(program_name + i - 6, "chsnap")) {
+		flags_array = snapshot_flags_array;
+		chsnap = 1;
+	}
+#endif
 	i = 1;
 	while (i < argc && !end_arg) {
 		/* '--' arg should end option processing */
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 5e0a8a2..65b8ce6 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -216,6 +216,17 @@ static void list_desc (ext2_filsys fs)
 		print_bg_rel_offset(fs, fs->group_desc[i].bg_block_bitmap, 0,
 				    first_block, last_block);
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_BITMAP
+		if (fs->super->s_feature_compat &
+				EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP) {
+			fputs(_(", Exclude bitmap at "), stdout);
+			print_number(fs->group_desc[i].bg_exclude_bitmap);
+			print_bg_rel_offset(fs,
+					fs->group_desc[i].bg_exclude_bitmap, 0,
+					first_block, last_block);
+		}
+
+#endif
 		fputs(_(", Inode bitmap at "), stdout);
 		print_number(fs->group_desc[i].bg_inode_bitmap);
 		print_bg_rel_offset(fs, fs->group_desc[i].bg_inode_bitmap, 0,
diff --git a/misc/e2image.c b/misc/e2image.c
index 83c1cca..1db8069 100644
--- a/misc/e2image.c
+++ b/misc/e2image.c
@@ -536,6 +536,9 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag)
 			}
 		} else {
 			if ((inode.i_flags & EXT4_EXTENTS_FL) ||
+#ifdef EXT2FS_SNAPSHOT_HUGE_SNAPSHOT
+			    (inode.i_flags & EXT4_SNAPFILE_FL) ||
+#endif
 			    inode.i_block[EXT2_IND_BLOCK] ||
 			    inode.i_block[EXT2_DIND_BLOCK] ||
 			    inode.i_block[EXT2_TIND_BLOCK]) {
diff --git a/misc/lsattr.c b/misc/lsattr.c
index 15b17ad..143aa78 100644
--- a/misc/lsattr.c
+++ b/misc/lsattr.c
@@ -70,7 +70,11 @@ static int generation_opt;
 
 static void usage(void)
 {
+#ifdef EXT2FS_SNAPSHOT_CTL_OLD
+	fprintf(stderr, _("Usage: %s [-XRVadlv] [files...]\n"), program_name);
+#else
 	fprintf(stderr, _("Usage: %s [-RVadlv] [files...]\n"), program_name);
+#endif
 	exit(1);
 }
 
@@ -78,8 +82,18 @@ static int list_attributes (const char * name)
 {
 	unsigned long flags;
 	unsigned long generation;
+#ifdef EXT2FS_SNAPSHOT_CTL
+	int ret;
+
+	if (pf_options & PFOPT_SNAPSHOT)
+		ret = fgetsnapflags (name, &flags);
+	else
+		ret = fgetflags (name, &flags);
+	if (ret == -1) {
+#else
 
 	if (fgetflags (name, &flags) == -1) {
+#endif
 		com_err (program_name, errno, _("While reading flags on %s"),
 			 name);
 		return -1;
@@ -169,9 +183,26 @@ int main (int argc, char ** argv)
 #endif
 	if (argc && *argv)
 		program_name = *argv;
+#ifdef EXT2FS_SNAPSHOT_CTL
+	i = strlen(program_name);
+	if (i >= 6 && !strcmp(program_name + i - 6, "lssnap"))
+		pf_options |= PFOPT_SNAPSHOT;
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_CTL_OLD
+	while ((c = getopt (argc, argv, "XRVadlv")) != EOF)
+#else
 	while ((c = getopt (argc, argv, "RVadlv")) != EOF)
+#endif
 		switch (c)
 		{
+#ifdef EXT2FS_SNAPSHOT_CTL_OLD
+			case 'X':
+				/* for backward compatibility with next3 */
+				pf_options &= ~PFOPT_SNAPSHOT;
+				pf_options |= PFOPT_SNAPSHOT_X;
+				break;
+#endif
 			case 'R':
 				recursive = 1;
 				break;
@@ -185,7 +216,11 @@ int main (int argc, char ** argv)
 				dirs_opt = 1;
 				break;
 			case 'l':
+#ifdef EXT2FS_SNAPSHOT_CTL
+				pf_options |= PFOPT_LONG;
+#else
 				pf_options = PFOPT_LONG;
+#endif
 				break;
 			case 'v':
 				generation_opt = 1;
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index b994e1e..1ecc313 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -382,6 +382,12 @@ static void write_inode_tables(ext2_filsys fs, int lazy_flag, int itable_zeroed)
 			/* The kernel doesn't need to zero the itable blocks */
 			fs->group_desc[i].bg_flags |= EXT2_BG_INODE_ZEROED;
 			ext2fs_group_desc_csum_set(fs, i);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+			if (fs->super->s_feature_compat &
+				EXT2_FEATURE_COMPAT_EXCLUDE_INODE)
+				/* zero the designated exclude bitmap block */
+				num++;
+#endif
 		}
 		retval = ext2fs_zero_blocks(fs, blk, num, &blk, &num);
 		if (retval) {
@@ -844,6 +850,9 @@ static void parse_extended_opts(struct ext2_super_block *param,
 static __u32 ok_features[3] = {
 	/* Compat */
 	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+		EXT2_FEATURE_COMPAT_EXCLUDE_INODE |
+#endif
 		EXT2_FEATURE_COMPAT_RESIZE_INODE |
 		EXT2_FEATURE_COMPAT_DIR_INDEX |
 		EXT2_FEATURE_COMPAT_EXT_ATTR,
@@ -855,6 +864,9 @@ static __u32 ok_features[3] = {
 		EXT4_FEATURE_INCOMPAT_FLEX_BG,
 	/* R/O compat */
 	EXT2_FEATURE_RO_COMPAT_LARGE_FILE|
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+		EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|
+#endif
 		EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
 		EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
 		EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
@@ -990,6 +1002,12 @@ static char **parse_fs_type(const char *fs_type,
 		ext_type = "ext2";
 	else if (!strcmp(program_name, "mke3fs"))
 		ext_type = "ext3";
+#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL
+	else if (!strcmp(program_name, "mkfs.next3"))
+		ext_type = "ext3";
+	else if (!strcmp(program_name, "mkfs.next4"))
+		ext_type = "ext4";
+#endif
 	else if (progname) {
 		ext_type = strrchr(progname, '/');
 		if (ext_type)
@@ -1262,6 +1280,27 @@ static void PRS(int argc, char *argv[])
 		if (!strcmp(program_name, "mkfs.ext3") ||
 		    !strcmp(program_name, "mke3fs"))
 			journal_size = -1;
+#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL
+		
+		/* If called as mkfs.next3/next4: */
+		if (!strcmp(program_name, "mkfs.next3") ||
+		    !strcmp(program_name, "mkfs.next4")) {
+			/* 1. create a big journal */
+			journal_size = -NEXT3_MAX_COW_CREDITS;
+			/* 2. use system page size as block size */
+			blocksize = sys_page_size;
+			fs_param.s_log_block_size =
+				int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+			/* 3. create exclude inode */
+			edit_feature("exclude_inode", &fs_param.s_feature_compat);
+#endif
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+			/* 4. enable snapshot support */
+			edit_feature("has_snapshot", &fs_param.s_feature_compat);
+#endif
+		}
+#endif
 	}
 
 	while ((c = getopt (argc, argv,
@@ -2282,6 +2321,17 @@ int main (int argc, char *argv[])
 				exit(1);
 			}
 		}
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+		if (fs->super->s_feature_compat &
+		    EXT2_FEATURE_COMPAT_EXCLUDE_INODE) {
+			retval = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE);
+			if (retval) {
+				com_err("ext2fs_create_exclude_inode", retval,
+				_("while reserving blocks for exclude bitmap"));
+				exit(1);
+			}
+		}
+#endif
 	}
 
 	if (journal_device) {
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index e5722a5..9064402 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -118,6 +118,9 @@ static void usage(void)
 static __u32 ok_features[3] = {
 	/* Compat */
 	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+		EXT2_FEATURE_COMPAT_EXCLUDE_INODE |
+#endif
 		EXT2_FEATURE_COMPAT_DIR_INDEX,
 	/* Incompat */
 	EXT2_FEATURE_INCOMPAT_FILETYPE |
@@ -125,6 +128,9 @@ static __u32 ok_features[3] = {
 		EXT4_FEATURE_INCOMPAT_FLEX_BG,
 	/* R/O compat */
 	EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+		EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|
+#endif
 		EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
 		EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
 		EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
@@ -135,6 +141,9 @@ static __u32 ok_features[3] = {
 static __u32 clear_ok_features[3] = {
 	/* Compat */
 	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+		EXT2_FEATURE_COMPAT_EXCLUDE_INODE |
+#endif
 		EXT2_FEATURE_COMPAT_RESIZE_INODE |
 		EXT2_FEATURE_COMPAT_DIR_INDEX,
 	/* Incompat */
@@ -142,6 +151,9 @@ static __u32 clear_ok_features[3] = {
 		EXT4_FEATURE_INCOMPAT_FLEX_BG,
 	/* R/O compat */
 	EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+		EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|
+#endif
 		EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
 		EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
 		EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
@@ -249,23 +261,170 @@ no_valid_journal:
 	free(journal_path);
 }
 
+#define blk64_t blk_t
+#define ext2fs_block_iterate3 \
+	ext2fs_block_iterate2
+#define ext2fs_unmark_block_bitmap2 \
+	ext2fs_unmark_block_bitmap
+#define ext2fs_group_of_blk2 \
+	ext2fs_group_of_blk
+#define ext2fs_bg_free_blocks_count_set(fs, group, count) \
+	fs->group_desc[group].bg_free_blocks_count = (count)
+#define ext2fs_bg_free_blocks_count(fs, group) \
+	fs->group_desc[group].bg_free_blocks_count
+#define ext2fs_free_blocks_count_add(sb, count) \
+	sb->s_free_blocks_count += (count)
+#define ext2fs_group_desc(fs, gdp, grp) \
+	(gdp)+(grp)
+
 /* Helper function for remove_journal_inode */
-static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
-			       int blockcnt EXT2FS_ATTR((unused)),
+static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr,
+			       e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
+			       blk64_t ref_block EXT2FS_ATTR((unused)),
+			       int ref_offset EXT2FS_ATTR((unused)),
 			       void *private EXT2FS_ATTR((unused)))
 {
-	blk_t	block;
+	blk64_t	block;
 	int	group;
 
 	block = *blocknr;
-	ext2fs_unmark_block_bitmap(fs->block_map, block);
-	group = ext2fs_group_of_blk(fs, block);
-	fs->group_desc[group].bg_free_blocks_count++;
+	ext2fs_unmark_block_bitmap2(fs->block_map, block);
+	group = ext2fs_group_of_blk2(fs, block);
+	ext2fs_bg_free_blocks_count_set(fs, group, ext2fs_bg_free_blocks_count(fs, group) + 1);
 	ext2fs_group_desc_csum_set(fs, group);
-	fs->super->s_free_blocks_count++;
+	ext2fs_free_blocks_count_add(fs->super, 1);
 	return 0;
 }
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+/*
+ * Remove a special inode from the filesystem:
+ * - resize inode, @nlink = 0
+ * - exclude inode, @nlink = 0
+ * - snapshot inodes, @nlink = 1 (snapshots directory)
+ */
+static void remove_special_inode(ext2_filsys fs, ext2_ino_t ino,
+		struct ext2_inode *inode, int nlink)
+{
+	int retval = ext2fs_read_bitmaps(fs);
+	if (retval) {
+		com_err(program_name, retval,
+				_("while reading bitmaps"));
+		exit(1);
+	}
+	retval = ext2fs_block_iterate3(fs, ino,
+			BLOCK_FLAG_READ_ONLY, NULL,
+			release_blocks_proc, NULL);
+	if (retval) {
+		com_err(program_name, retval,
+				_("while clearing inode"));
+		exit(1);
+	}
+	if (nlink) {
+		/* reset truncated inode */
+		inode->i_size = 0;
+		inode->i_size_high = 0;
+		inode->i_blocks = 0;
+		memset(inode->i_block, 0, sizeof(inode->i_block));
+	} else {
+		/* clear unlinked inode */
+		memset(inode, 0, sizeof(*inode));
+	}
+	ext2fs_mark_bb_dirty(fs);
+	fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+}
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_CLEANUP
+/*
+ * Discard snapshots list (free all snapshot blocks)
+ */
+static void discard_snapshot_list(ext2_filsys fs)
+{
+	struct ext2_super_block *sb = fs->super;
+	struct ext2_inode	inode;
+	ext2_ino_t		ino = sb->s_snapshot_list;
+	errcode_t		retval;
+	int i = 0;
+	
+	if (!ino)
+		/* no snapshot list, but maybe active snapshot exists? */
+		ino = sb->s_snapshot_inum;
+	if (ino)
+		fputs(_("Discarding snapshots: "), stderr);
+
+	while (ino) {
+		retval = ext2fs_read_inode(fs, ino,  &inode);
+		if (retval) {
+			com_err(program_name, retval,
+					_("while reading snapshot inode %u"),
+					ino);
+			exit(1);
+		}
+
+		remove_special_inode(fs, ino, &inode, 1);
+
+		retval = ext2fs_write_inode(fs, ino, &inode);
+		if (retval) {
+			com_err(program_name, retval,
+					_("while writing snapshot inode %u"),
+					ino);
+			exit(1);
+		}
+
+		fprintf(stderr, _("%u,"), inode.i_generation);
+		ino = inode.i_next_snapshot;
+		i++;
+	}
+	
+	if (i > 0) {
+		sb->s_snapshot_inum = 0;
+		sb->s_snapshot_id = 0;
+		sb->s_snapshot_r_blocks_count = 0;
+		sb->s_snapshot_list = 0;
+		fputs(_("done\n"), stderr);
+	}
+	
+	/* no snapshots, so no snapshot problems to fix */
+	sb->s_flags &= ~EXT2_FLAGS_FIX_SNAPSHOT;
+	fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+	ext2fs_mark_super_dirty(fs);
+}
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+/*
+ * Remove the exclude inode from the filesystem
+ */
+static void remove_exclude_inode(ext2_filsys fs)
+{
+	struct ext2_inode	inode;
+	ino_t			ino = EXT2_EXCLUDE_INO;
+	errcode_t		retval;
+
+	/* clear fix_exclude flag */
+	fs->super->s_flags &= ~EXT2_FLAGS_FIX_EXCLUDE;
+	fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+	ext2fs_mark_super_dirty(fs);
+
+	retval = ext2fs_read_inode(fs, ino,  &inode);
+	if (retval) {
+		com_err(program_name, retval,
+			_("while reading exclude inode"));
+		exit(1);
+	}
+
+	remove_special_inode(fs, ino, &inode, 0);
+
+	retval = ext2fs_write_inode(fs, ino, &inode);
+	if (retval) {
+		com_err(program_name, retval,
+			_("while writing exclude inode"));
+		exit(1);
+	}
+}
+
+#endif
 /*
  * Remove the journal inode from the filesystem
  */
@@ -281,6 +440,11 @@ static void remove_journal_inode(ext2_filsys fs)
 			_("while reading journal inode"));
 		exit(1);
 	}
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+	if (ino == EXT2_JOURNAL_INO)
+		remove_special_inode(fs, ino, &inode, 0);
+	else
+#else
 	if (ino == EXT2_JOURNAL_INO) {
 		retval = ext2fs_read_bitmaps(fs);
 		if (retval) {
@@ -288,9 +452,9 @@ static void remove_journal_inode(ext2_filsys fs)
 				_("while reading bitmaps"));
 			exit(1);
 		}
-		retval = ext2fs_block_iterate(fs, ino,
-					      BLOCK_FLAG_READ_ONLY, NULL,
-					      release_blocks_proc, NULL);
+		retval = ext2fs_block_iterate3(fs, ino,
+					       BLOCK_FLAG_READ_ONLY, NULL,
+					       release_blocks_proc, NULL);
 		if (retval) {
 			com_err(program_name, retval,
 				_("while clearing journal inode"));
@@ -300,6 +464,7 @@ static void remove_journal_inode(ext2_filsys fs)
 		ext2fs_mark_bb_dirty(fs);
 		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
 	} else
+#endif
 		inode.i_flags &= ~EXT2_IMMUTABLE_FL;
 	retval = ext2fs_write_inode(fs, ino, &inode);
 	if (retval) {
@@ -338,6 +503,34 @@ static void request_fsck_afterwards(ext2_filsys fs)
 		printf(_("(and reboot afterwards!)\n"));
 }
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+static int verify_clean_fs(ext2_filsys fs, int compat, unsigned int mask,
+		int on)
+{
+	struct ext2_super_block *sb= fs->super;
+
+	if ((mount_flags & EXT2_MF_MOUNTED) &&
+		!(mount_flags & EXT2_MF_READONLY)) {
+		fprintf(stderr, _("The '%s' feature may only be "
+					"%s when the filesystem is\n"
+					"unmounted or mounted read-only.\n"),
+				e2p_feature2string(compat, mask),
+				on ? "set" : "cleared");
+		exit(1);
+	}
+	if (sb->s_feature_incompat &
+		EXT3_FEATURE_INCOMPAT_RECOVER) {
+		fprintf(stderr, _("The needs_recovery flag is set.  "
+					"Please run e2fsck before %s\n"
+					"the '%s' flag.\n"),
+				on ? "setting" : "clearing",
+				e2p_feature2string(compat, mask));
+		exit(1);
+	}
+	return 1;
+}
+
+#endif
 /*
  * Update the feature set as provided by the user.
  */
@@ -356,11 +549,32 @@ static void update_feature_set(ext2_filsys fs, char *features)
 				 !((&sb->s_feature_compat)[(type)] & (mask)))
 #define FEATURE_CHANGED(type, mask) ((mask) & \
 		     (old_features[(type)] ^ (&sb->s_feature_compat)[(type)]))
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+#define FEATURE_ON_SAFE(compat, mask) \
+	(FEATURE_ON(compat, mask) && verify_clean_fs(fs, compat, mask, 1))
+#define FEATURE_OFF_SAFE(compat, mask) \
+	(FEATURE_OFF(compat, mask) && verify_clean_fs(fs, compat, mask, 0))
+#endif
 
 	old_features[E2P_FEATURE_COMPAT] = sb->s_feature_compat;
 	old_features[E2P_FEATURE_INCOMPAT] = sb->s_feature_incompat;
 	old_features[E2P_FEATURE_RO_INCOMPAT] = sb->s_feature_ro_compat;
 
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+	/* disallow changing features when filesystem has snapshots */
+	if (sb->s_feature_ro_compat & 
+		EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) {
+		ok_features[E2P_FEATURE_COMPAT] = 0;
+		ok_features[E2P_FEATURE_INCOMPAT] = 0;
+		ok_features[E2P_FEATURE_RO_INCOMPAT] =
+			EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT;
+		clear_ok_features[E2P_FEATURE_COMPAT] = 0;
+		clear_ok_features[E2P_FEATURE_INCOMPAT] = 0;
+		clear_ok_features[E2P_FEATURE_RO_INCOMPAT] =
+			EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT;
+	}
+
+#endif
 	if (e2p_edit_feature2(features, &sb->s_feature_compat,
 			      ok_features, clear_ok_features,
 			      &type_err, &mask_err)) {
@@ -368,6 +582,14 @@ static void update_feature_set(ext2_filsys fs, char *features)
 			fprintf(stderr,
 				_("Invalid filesystem option set: %s\n"),
 				features);
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+		else if (old_features[E2P_FEATURE_RO_INCOMPAT] & 
+				EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT)
+			fputs(_("The filesystem has snapshots.  "
+						"Please clear the has_snapshot flag\n"
+						"before clearing/setting other filesystem flags.\n"), 
+					stderr);
+#endif
 		else if (type_err & E2P_FEATURE_NEGATE_FLAG)
 			fprintf(stderr, _("Clearing filesystem feature '%s' "
 					  "not supported.\n"),
@@ -416,6 +638,78 @@ static void update_feature_set(ext2_filsys fs, char *features)
 		sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
 	}
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+	if (FEATURE_OFF_SAFE(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) {
+		remove_exclude_inode(fs);
+		/* clear exclude_bitmap along with exclude_inode */
+		sb->s_feature_compat &=
+			~EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP;
+	}
+
+	if (FEATURE_ON_SAFE(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) {
+		retval = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE);
+		if (retval) {
+			com_err(program_name, retval,
+					_("while creating exclude inode"));
+			exit(1);
+		}
+	}
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_CLEANUP
+	if (FEATURE_OFF_SAFE(E2P_FEATURE_RO_INCOMPAT,
+				EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT)) {
+		discard_snapshot_list(fs);
+		if (sb->s_feature_compat & 
+				EXT2_FEATURE_COMPAT_EXCLUDE_INODE) {
+			/* reset exclude bitmap blocks */
+			retval = ext2fs_create_exclude_inode(fs, EXCLUDE_RESET);
+			if (retval)
+				/* cleanup bad exclude inode/bitmap */
+				sb->s_feature_compat &=
+					~(EXT2_FEATURE_COMPAT_EXCLUDE_INODE|
+					  EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP);
+		}
+	}
+
+#endif
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+	if (FEATURE_ON_SAFE(E2P_FEATURE_RO_INCOMPAT,
+				EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT)) {
+		int big_journal = 0;
+
+		if ((sb->s_feature_compat &
+		    EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
+			/* update 'big_journal' flag */
+			big_journal = (ext2fs_check_journal_size(fs) >=
+					NEXT3_MIN_JOURNAL_BLOCKS);
+		} else if (!journal_size || journal_size == -1) {
+			/* Create a big journal for Next3 */
+			journal_size = -NEXT3_MAX_COW_CREDITS;
+			big_journal = 1;
+		}
+	
+		if (!big_journal)
+			fprintf(stderr,
+				_("Warning: journal size is not big enough.\n"
+				"For best operation of Next3, try re-creating "
+				"the journal with '-J big' before setting the "
+				"'has_snapshot' flag.\n"));
+
+		/* allocate/reset exclude bitmap blocks */
+		retval = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE);
+		if (!retval)
+			sb->s_feature_compat |=
+				EXT2_FEATURE_COMPAT_EXCLUDE_INODE;
+		else
+			fprintf(stderr,
+				_("Warning: failed to create exclude inode.\n"
+				"For best operation of Next3, try re-creating "
+				"the exclude inode before setting the "
+				"'has_snapshot' flag.\n"));
+	}
+
+#endif
 	if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX)) {
 		if (!sb->s_def_hash_version)
 			sb->s_def_hash_version = EXT2_HASH_HALF_MD4;
diff --git a/misc/util.c b/misc/util.c
index 2897937..9502082 100644
--- a/misc/util.c
+++ b/misc/util.c
@@ -222,6 +222,22 @@ void parse_journal_opts(const char *opts)
 			journal_size = strtoul(arg, &p, 0);
 			if (*p)
 				journal_usage++;
+#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL
+		} else if (strcmp(token, "big") == 0) {
+			/* Create a big journal for Next3 */
+			journal_size = -NEXT3_MAX_COW_CREDITS;
+			continue;
+		} else if (strcmp(token, "bigger") == 0) {
+			/* Create a journal bigger than default */
+			if (!arg) {
+				journal_usage++;
+				continue;
+			}
+			journal_size = -strtoul(arg, &p, 0);
+			if (*p)
+				journal_usage++;
+			continue;
+#endif
 		} else if (strcmp(token, "v1_superblock") == 0) {
 			journal_flags |= EXT2_MKJOURNAL_V1_SUPER;
 			continue;
@@ -235,6 +251,10 @@ void parse_journal_opts(const char *opts)
 			"\tis set off by an equals ('=') sign.\n\n"
 			"Valid journal options are:\n"
 			"\tsize=<journal size in megabytes>\n"
+#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL
+			"\tbig (Next3 big journal size)\n"
+			"\tbigger=<X times bigger than default size>\n"
+#endif
 			"\tdevice=<journal device>\n\n"
 			"The journal size must be between "
 			"1024 and 10240000 filesystem blocks.\n\n"), stderr);
@@ -263,6 +283,22 @@ unsigned int figure_journal_size(int size, ext2_filsys fs)
 		return 0;
 	}
 
+#ifdef EXT2FS_SNAPSHOT_BIG_JOURNAL
+	if (size < -1) {
+		/* bigger journal requested */
+		j_blocks = ext2fs_big_journal_size(-size, fs->super->s_blocks_count);
+		if (j_blocks < EXT3_DEF_JOURNAL_BLOCKS*(-size)) {
+			fputs(_("\nFilesystem too small for requested "
+					"journal size.  "), stderr);
+			if (j_blocks < 0) {
+				fputs(_("Aborting.\n"), stderr);
+				exit(1);
+			}
+			fputs(_("Creating a smaller journal.\n"), stderr);
+		}
+	}
+
+#endif
 	if (size > 0) {
 		j_blocks = size * 1024 / (fs->blocksize	/ 1024);
 		if (j_blocks < 1024 || j_blocks > 10240000) {
diff --git a/next3 b/next3
new file mode 100755
index 0000000..fdddbd3
--- /dev/null
+++ b/next3
@@ -0,0 +1,764 @@
+#!/bin/sh
+#
+# NEXT3(R) snapshot management script
+#
+# Written by Amir Goldstein <amir73il@users.sf.net>, 2008
+# Copyright (C) 2008-2010 CTERA Networks
+#
+
+SCRIPT_NAME=$(basename $0)
+SCRIPT_VER=1.0.13-5
+SCRIPT_DESC="$FSTYPE snapshot management script"
+# fs/module name = script name
+FSTYPE=$SCRIPT_NAME
+
+# generic e2fs progs
+FSCK=e2fsck
+MKFS=mke2fs
+TUNEFS=tune2fs
+LSSNAP="lssnap"
+CHSNAP="chsnap"
+CHATTR=chattr
+
+# override generic e2fs progs with installed e2fs.$FSTYPE progs in /sbin fodler
+# and override those with private built e2fs.$FSTYPE progs in ./bin folder
+for dir in /sbin ./bin ; do
+if [ -f $dir/fsck.$FSTYPE ] ; then
+	FSCK=$dir/fsck.$FSTYPE
+fi
+if [ -f $dir/mkfs.$FSTYPE ] ; then
+	MKFS=$dir/mkfs.$FSTYPE
+fi
+if [ -f $dir/tunefs.$FSTYPE ] ; then
+	TUNEFS=$dir/tunefs.$FSTYPE
+fi
+if [ -f $dir/lssnap ] ; then
+	LSSNAP=$dir/lssnap
+elif [ -f $dir/lsattr.$FSTYPE ] ; then
+	LSSNAP="$dir/lsattr.$FSTYPE -X"
+fi
+if [ -f $dir/chsnap ] ; then
+	CHSNAP=$dir/chsnap
+elif [ -f $dir/chattr.$FSTYPE ] ; then
+	CHSNAP="$dir/chattr.$FSTYPE -X"
+fi
+if [ -f $dir/chattr.$FSTYPE ] ; then
+	CHATTR=$dir/chattr.$FSTYPE
+fi
+done 
+
+if [ $# -ge 2 ] && [ $1 != help ] && [ $1_$2 != $1_help ] && [ $1 != config ] && \
+	[ $1 != version ] && [ $1 != debug ] && [ $1 != delay ] && [ _$1 != _test ] && [ $1 != tests ] ; then
+	# parse [[next-mount@]snapshot-name] or $FSTYPE-mount or $FSTYPE-device argument
+	s=$( echo $2 | ( IFS=$IFS@ ; read a b ; echo $b ) )
+	if [ -z $s ] ; then
+		# check if argument is a $FSTYPE mount point
+		dev=$( cat /proc/mounts | while read a b c d ; do ( [ _$b = _$2 ] && [ _$c = _$FSTYPE ] && echo $a ) ; done )
+		if [ ! -z $dev ] ; then
+			# found $FSTYPE mount point
+			ROOTMNT=$2
+			ROOTDEV=$dev
+		elif [ -b $2 ] || [ $1 = mkfs ] || [ $1 = on ] || [ $1 = off ] ; then
+			# argument is a $FSTYPE block device
+			ROOTDEV=$2
+			ROOTMNT=$3
+		else
+			# snapshot name was given without $FSTYPE mount point
+			s=$2
+		fi
+	else
+		# snapshot name is prepended with $FSTYPE mount point
+		ROOTMNT=$( echo $2 | ( IFS=$IFS@ ; read a b ; echo $a ) )
+		# find $FSTYPE block device from $FSTYPE mount point
+		ROOTDEV=$( cat /proc/mounts | while read a b c d ; do ( [ _$b = _$ROOTMNT ] && [ _$c = _$FSTYPE ] && echo $a ) ; done )
+		if [ -z $ROOTDEV ] ; then
+			echo "$0 $1 $2: $ROOTMNT is not a mounted $FSTYPE filesystem!"
+			exit 1
+		fi
+		# strip / from snapshot name
+		s=$( echo $s | ( IFS=$IFS/ ; read a b ; echo $a ) )
+	fi
+fi
+
+SYSDEBUGDIR=/sys/kernel/debug
+SNAPDEBUGDIR=$SYSDEBUGDIR/$FSTYPE
+
+# default debug-level is old-debug-level
+if [ ! -d $SNAPDEBUGDIR ] ; then
+	mount -t debugfs debugfs $SYSDEBUGDIR 2> /dev/null
+fi
+if [ -e $SNAPDEBUGDIR/snapshot-debug ] ; then
+	L=$(cat $SNAPDEBUGDIR/snapshot-debug 2> /dev/null)
+	if [ -z $2 ] ; then
+		l=$L
+	else
+		l=$2
+	fi
+else
+	L=0
+	l=0
+fi
+# check if test read is enabled
+if [ -e $SNAPDEBUGDIR/test-read-delay-msec ] ; then
+	test_read=$(cat $SNAPDEBUGDIR/test-read-delay-msec)
+else
+	test_read=0
+fi
+
+# read $FSTYPE block device and mount point from conf file
+CONFFILE=.$FSTYPE.conf
+
+# look for snapshot config file in current then home directory
+if [ -z $ROOTDEV ] && [ -z $ROOTMNT ] ; then
+	if [ -f ./$CONFFILE ] ; then
+		. ./$CONFFILE
+	elif [ -f ~/$CONFFILE ] ; then
+		. ~/$CONFFILE
+	elif [ $( mount -t $FSTYPE | grep -v noload | wc -l ) = 1 ] ; then
+		# exactly one mounted $FSTYPE filesystem found (mounted snapshots excluded)
+		ROOTDEV=$( mount -t $FSTYPE | grep -v noload | ( read a b c d ; echo $a ) )
+		ROOTMNT=$( mount -t $FSTYPE | grep -v noload | ( read a b c d ; echo $c ) )
+	fi
+fi
+
+if [ ! -z $1 ] && [ $1 != help ] && [ $1_$2 != $1_help ] && [ $1 != config ] && \
+	[ $1 != version ] && [ $1 != debug ] && [ $1 != delay ] && [ _$1 != _test ] && [ $1 != tests ] ; then
+	if [ $1 != mkfs ] && [ $1 != fsck ] && [ $1 != on ] && [ $1 != off ] ; then
+	if [ -z $ROOTMNT ] ; then
+		echo "$0: ambigous or missing $FSTYPE mount point"
+		echo "run '$0 config' to set default value"
+		exit 1
+	fi
+	if [ ! -d $ROOTMNT ] ; then
+		echo "$0: bad $FSTYPE mount point $ROOTMNT"
+		exit 1
+	fi
+	else
+	if [ -z $ROOTDEV ] ; then
+		echo "$0: ambigous or missing $FSTYPE block device"
+		echo "run '$0 config' to set default value"
+		exit 1
+	fi
+	if [ ! -b $ROOTDEV ] && [ ! -f $ROOTDEV ] ; then
+		echo "$0: bad $FSTYPE block device $ROOTDEV"
+		exit 1
+	fi
+	fi
+	export ROOTMNT
+	export ROOTDEV
+	S=$ROOTMNT@$s
+fi
+
+if [ -z $SNAPDIR ] ; then
+	# directory inside $FSTYPE filesystem to store snapshot files
+	SNAPDIR=$ROOTMNT/.snapshots
+fi
+if [ -z $SNAPMNT ] ; then
+	# directory prefix for snapshot mount points
+	# snaphot mount points will be created as $SNAPMNT<snapshot-name>
+	# default to ZFS snapshot naming convention <filesystem>@<snapshot>
+	SNAPMNT=$ROOTMNT@
+fi
+TESTDIR=test
+
+# See how we were called.
+case "$1" in
+# Snapshot global commands
+  help)
+	if [ -z $2 ] ; then
+		$0
+	else
+		$0 $2 help
+	fi
+	;;
+  version)
+	if [ $1_$2 = $1_help ] ; then
+		echo "$1: display $FSTYPE snapshot version."
+		exit 0
+	fi
+	ver=$(cat $SNAPDEBUGDIR/snapshot-version)
+	echo snapshot-version = $ver
+	;;
+  debug)
+	if [ $1_$2 = $1_help ] ; then
+		echo "$1: set snapshot debug level."
+		echo "debug levels: 0=none, 1=error, 2=warning, 3=info, 4=debug."
+		echo "usage: $0 $1 [debug-level]"
+		exit 0
+	fi
+	if [ -e $SNAPDEBUGDIR/snapshot-debug ] ; then
+		echo $l > $SNAPDEBUGDIR/snapshot-debug
+		l=$(cat $SNAPDEBUGDIR/snapshot-debug)
+	fi
+	echo snapshot-debug = $l
+	;;
+  delay)
+	if [ $1_$2 = $1_help ] ; then
+		echo "$1: set snapshot test delay."
+		echo "adds delay to specific snapshot operation."
+		echo "usage: $0 $1 <take|delete|cow|read|bitmap> [delay-sec] [delay-msec]"
+		exit 0
+	fi
+	if [ -z $2 ] ; then
+		echo missing test name
+		$0 usage
+	fi
+	if [ ! -e $SNAPDEBUGDIR/test-$2-delay-msec ] ; then
+		echo invalid test name $2
+		$0 usage
+	fi
+	if [ ! -z $3 ] ; then
+		if [ $3 -gt 60 ] || [ $3 -lt 0 ] ; then
+			echo valid range for test delay is 0-60 sec
+		else
+			if [ ! -z $4 ] ; then
+				ms=$3$4
+			else
+				ms=${3}000
+			fi
+			echo $ms > $SNAPDEBUGDIR/test-$2-delay-msec
+		fi
+	fi
+	ms=$(cat $SNAPDEBUGDIR/test-$2-delay-msec)
+	echo test-$2-delay = $ms msec
+	;;
+
+# $FSTYPE filesystem commands
+  config)
+	if [ $1_$2 = $1_help ] ; then
+		echo "$1: configure or print default next filesystem parameters."
+		echo "creates configuration file $CONFFILE in current directory."
+		echo "usage: $0 config [$FSTYPE-device $FSTYPE-mount]"
+		exit 0
+	fi
+	if [ ! -z $2 ] && [ ! -b $2 ] && [ ! -f $2 ] ; then
+		echo "$0 config: bad block device: $2"
+		exit 1
+	fi
+	if [ ! -z $3 ] && [ ! -d $3 ] ; then
+		echo "$0 config: bad mount point: $3"
+		exit 1
+	fi
+	if [ ! -z $2 ] && [ ! -z $3 ] ; then
+		echo ROOTDEV=$2 > $CONFFILE
+		echo ROOTMNT=$3 >> $CONFFILE
+	fi
+	test -f $CONFFILE && cat $CONFFILE
+	;;
+  on)
+	if [ $1_$2 = $1_help ] ; then
+		echo "on: add the snapshot feature to an existing ext3 filesystem."
+		echo "after adding the snapshot feature to an ext3 filesystem, the filesystem"
+		echo "can no longer be mounted as ext3 read-write (only as $FSTYPE or ext3 read-only)."
+		echo "usage: $0 on [$FSTYPE-device]"
+		exit 0
+	fi
+	$TUNEFS -e remount-ro -O has_snapshot $ROOTDEV || exit 1
+	echo snapshot feature added to $ROOTDEV
+	echo .
+	;;
+  off)
+	if [ $1_$2 = $1_help ] ; then
+		echo "off: remove the snapshot feature from a $FSTYPE filesystem."
+		echo "after removing the snapshot feature, all existing snapshots will be discarded"
+		echo "and the filesystem could be mounted as ext3 read-write again."
+		echo "usage: $0 off [$FSTYPE-device]"
+		exit 0
+	fi
+	$TUNEFS -O ^has_snapshot $ROOTDEV || exit 1
+	echo snapshot feature removed from $ROOTDEV
+	echo .
+	;;
+  mkfs)
+	if [ $1_$2 = $1_help ] ; then
+		echo "$1: create a $FSTYPE filesystem."
+		echo "usage: $0 mkfs [$FSTYPE-device]"
+		exit 0
+	fi
+	$MKFS -b 4096 -j -J big -O has_snapshot,exclude_inode $ROOTDEV || exit 1
+	$TUNEFS -e remount-ro $ROOTDEV
+	echo $FSTYPE filesystem created on $ROOTDEV
+	echo .
+	;;
+
+  stat)
+	if [ $1_$2 = $1_help ] ; then
+		echo "stat: display status of $FSTYPE filesystem and all its snapshots."
+		echo "snapshot attributes are displayed in short format. to display"
+		echo "attributes in long format use the '$0 lsattr' command."
+		echo "usage: $0 stat [next-mount]"
+		exit 0
+	fi
+	echo "Mounted $FSTYPE filesystem and snapshots:"
+	df -h $ROOTMNT $SNAPMNT* 2> /dev/null
+	echo .
+	echo "Snapshots list:"
+	echo "id inode attributes disk-usage mtime filename"
+	echo "---------------------------------------------"
+	for s in $( ls -vr $SNAPDIR/* ) ; do
+		( echo "$( $LSSNAP -v $s ) " ; \
+			echo "$( du -h $s ) " ; \
+			echo "$( ls -lih --color=never $s ) " ) | \
+		( read id attr a ; read du b ; read ino c d e f g mm dd tt fname h ; \
+			echo $id $ino $attr $du $mm $dd $tt $fname )
+	done 2> /dev/null
+	echo .
+	;;
+  lsattr)
+	if [ $1_$2 = $1_help ] ; then
+		echo "$1: display attributes of $FSTYPE snapshots in long format."
+		echo "usage: $0 $1"
+		exit 0
+	fi
+	$LSSNAP -l $SNAPDIR
+	;;
+
+# Snapshot control commands (single snapshot)
+  take)
+	if [ $1_$2 = $1_help ] ; then
+		echo "take: take a snapshot."
+		echo "usage: $0 take [[next-mount@]snapshot-name] (default=$FSTYPE-mount@GMT-date)"
+		exit 0
+	fi
+	mkdir -p $SNAPDIR || exit 1
+	$CHATTR +dx $SNAPDIR || exit 1
+	if [ -z $s ] ; then
+		# default snapshot-name is the <date-time> in shadow copy format
+		s=$( date -u +GMT-%Y.%m.%d-%H.%M.%S )
+		S=$ROOTMNT@$s
+	fi
+	if [ -f $SNAPDIR/$s ] ; then
+		echo "$0 take: snapshot $S already exists!"
+		exit 1
+	fi
+	touch $SNAPDIR/$s 2> /dev/null
+	sync
+	$CHSNAP +S $SNAPDIR/$s || exit 1
+	echo snapshot $S was created
+	echo .
+	;;
+  delete)
+	if [ $1_$2 = $1_help ] ; then
+		echo "delete: umount a snapshot and mark it for deletion."
+		echo "any non-mounted snapshot can be marked for deletion"
+		echo "but some snapshot deletion is deferred to later time."
+		echo "usage: $0 delete [next-mount@]<snapshot-name>"
+		exit 0
+	fi
+	if [ -z $s ] ; then
+		$0
+		exit 1
+	fi
+	if [ -f $SNAPDIR/$s ] ; then
+		$0 umount $s
+		$CHSNAP -S $SNAPDIR/$s || exit 1
+	fi
+	echo snapshot $S is deleted
+	echo .
+	;;
+  remove)
+	if [ $1_$2 = $1_help ] ; then
+		echo "remove: delete a snapshot permanently."
+		echo "this command will fail for active snapshot and snapshots"
+		echo "in use by older snapshots. try using the delete command."
+		echo "usage: $0 remove [next-mount@]<snapshot-name>"
+		exit 0
+	fi
+	if [ -z $2 ] ; then
+		$0
+		exit 1
+	fi
+	if [ -f $SNAPDIR/$s ] ; then
+		$0 delete $s
+		rm -f $SNAPDIR/$s 2> /dev/null || exit 1
+	fi
+	echo snapshot $S was removed
+	echo .
+	;;
+  enable)
+	if [ $1_$2 = $1_help ] ; then
+		echo "enable: enable access to snapshot file."
+		echo "usage: $0 enable [next-mount@]<snapshot-name>"
+		exit 0
+	fi
+	if [ -z $2 ] ; then
+		echo "$0 enable: snapshot name is expected!"
+		exit 1
+	fi
+  	if [ ! -f $SNAPDIR/$s ] ; then
+		echo "$0 enable: snapshot $S not found!"
+		exit 1
+  	fi
+	$CHSNAP +n $SNAPDIR/$s || exit 1
+	;;
+  disable)
+	if [ $1_$2 = $1_help ] ; then
+		echo "disable: disable access to snapshot file."
+		echo "usage: $0 disable [next-mount@]<snapshot-name>"
+		exit 0
+	fi
+	if [ -z $2 ] ; then
+		echo "$0 disable: snapshot name is expected!"
+		exit 1
+	fi
+  	if [ ! -f $SNAPDIR/$s ] ; then
+		echo "$0 disable: snapshot $S not found!"
+		exit 1
+  	fi
+	if grep $SNAPMNT$s /proc/mounts ; then
+		echo "$0 disable: snapshot $S is mounted!"
+		exit 1
+	fi
+	$CHSNAP -n $SNAPDIR/$s || exit 1
+	;;
+
+# Snapshot control commands (single or all snapshots)
+  mount)
+	if [ $1_$2 = $1_help ] ; then
+		echo "mount: mount a snapshot."
+		echo "usage: $0 mount [$FSTYPE-device $FSTYPE-mount]"
+		echo "usage: $0 mount [next-mount@]<snapshot-name>"
+		exit 0
+	fi
+	if [ -z $s ] ; then
+		# mount $FSTYPE filesystem 
+		MNTOPT="-o errors=remount-ro"
+		# uncomment to enable delayed allocation 
+		#MNTOPT="$MNTOPT,nodelalloc"
+		if [ ! -z $ROOTDEV ] && [ -f $ROOTDEV ] ; then
+			MNTOPT="$MNTOPT,loop"
+		fi
+		# we may be called from mount.$FSTYPE so add -i
+		mount -t $FSTYPE -i $MNTOPT $ROOTDEV $ROOTMNT || exit 1
+		echo $FSTYPE filesystem is mounted on $ROOTMNT
+	else
+  		$0 enable $s || exit 1
+		if grep $SNAPMNT$s /proc/mounts ; then
+			echo "$0 mount: snapshot $S is already mounted!"
+			exit 1
+		fi
+		mkdir -p $SNAPMNT$s
+  		mount -t $FSTYPE -r -o loop,noload $SNAPDIR/$s $SNAPMNT$s || exit 1
+		echo snapshot $S is mounted
+  	fi
+	echo .
+	;;
+  umount)
+	if [ $1_$2 = $1_help ] ; then
+		echo "umount: umount a snapshot."
+		echo "usage: $0 umount [$FSTYPE-mount]"
+		echo "usage: $0 umount [next-mount@]<snapshot-name>"
+		exit 0
+	fi
+	if [ -z $s ] ; then
+		# umount all snapshots
+		for s in $( ls $SNAPDIR/ 2> /dev/null ) ; do
+			$0 umount $s 2> /dev/null
+		done
+		# umount $FSTYPE filesystem 
+		# we may be called from umount.$FSTYPE so add -i
+		umount -i -d $MNTOPT $ROOTMNT || exit 1
+		echo $FSTYPE filesystem was unmounted
+	else
+		if [ -d $SNAPMNT$s ] ; then
+			umount -d $SNAPMNT$s || exit 1
+			rmdir $SNAPMNT$s
+		fi
+  		$0 disable $s
+		echo snapshot $S was unmounted
+	fi
+	echo .
+	;;
+
+# Snapshot sanity tests
+  dump)
+	if [ $1_$2 = $1_help ] ; then
+		echo "dump: print a map of a snapshot file to kernel ring buffer."
+		echo "usage: $0 dump [next-mount@]<snapshot-name>"
+		exit 0
+	fi
+	if [ -z $2 ] ; then
+		echo "$0 dump: snapshot name is expected!"
+		exit 1
+	fi
+	if [ ! -f $SNAPDIR/$s ] ; then
+		echo "$0 dump: snapshot $S not found!"
+		exit 1
+	fi
+	$CHATTR -d $SNAPDIR/$s
+	echo "map of snapshot $S was printed to kernel ring buffer."
+	echo "use dmesg to display kernel ring buffer."
+	echo .
+	;;
+  fsck)
+	if [ $1_$2 = $1_help ] ; then
+		echo "fsck: run fsck on a snapshot or $FSTYPE filesystem."
+		echo "usage: $0 fsck [next-device]"
+		echo "usage: $0 fsck [next-mount@]<snapshot-name>"
+		exit 0
+	fi
+	if [ -z $s ] ; then
+		echo "Fscking $FSTYPE filesystem on $ROOTDEV..."
+		$FSCK -pf -C 0 $ROOTDEV >&2 && \
+			echo $FSTYPE filesystem on $ROOTDEV 'is healthy' || \
+			echo $FSTYPE filesystem on $ROOTDEV 'has errors'
+	else
+  		$0 enable $s || exit 1
+		echo "Fscking snapshot $S..."
+		$FSCK -nf -C 0 $SNAPDIR/$s >&2 && \
+			echo snapshot $S 'is healthy' || \
+			echo snaphsot $S 'has errors'
+		if ! grep $SNAPMNT$s /proc/mounts ; then
+  			$0 disable $s || exit 1
+		fi
+	fi
+	echo .
+	;;
+
+  mktest)
+	if [ -d $ROOTMNT/A ] ; then
+		exit 0
+	fi
+	mkdir $ROOTMNT/A
+	mkdir $ROOTMNT/B
+	mkdir $ROOTMNT/C
+	mkdir $ROOTMNT/D
+	mkdir $ROOTMNT/E
+	echo aligator > $ROOTMNT/A/a.txt
+	echo bizon > $ROOTMNT/B/b.txt
+	echo camel > $ROOTMNT/C/c.txt
+	# create non-snapshot dir in snapshots dir block group
+	mkdir -p $ROOTMNT/$TESTDIR
+	$CHATTR -x $ROOTMNT/$TESTDIR || exit 1
+	;;
+  rmtest)
+	rm -rf $ROOTMNT/? 2> /dev/null
+	rm -rf $ROOTMNT/$TESTDIR 2> /dev/null
+	;;
+  lstest)
+	if [ $1_$2 = $1_help ] ; then
+		echo "lstest: list the content of test files in a snapshot or $FSTYPE filesystem."
+		echo "usage: $0 lstest [[next-mount@]snapshot-name] (default=$FSTYPE-mount)"
+		exit 0
+	fi
+	if [ -z $s ] ; then
+		echo Files in file system:
+		d=$ROOTMNT
+	else
+		$0 mount $s || exit 1
+		echo Files in snapshot $s:
+		d=$SNAPMNT$s
+	fi
+	if [ -d $d ] ; then
+		cd $d > /dev/null
+		grep -v xxx ?/*
+		test -e $TESTDIR/md5list && (cd $TESTDIR ; md5sum -c md5list || exit 1)
+		#find $d/ -maxdepth 1 -type f -print
+		cd - > /dev/null
+		echo .
+	fi
+	if [ ! -z $s ] ; then
+		$0 umount $s || exit 1
+	fi
+	;;
+  tests)
+	if [ $1_$2 = $1_help ] ; then
+		echo "tests: run snapshot sanity tests 1..N."
+		echo "usage: $0 tests [test-number] [delay-sec] [file-size-mb]"
+		echo "delay-sec: sleep between tests"
+		echo "file-size-mb: test file size in mega bytes (default = 1)"
+		exit 0
+	fi
+	if [ -z $2 ] ; then
+		N=4
+	else
+		N=$2
+	fi
+	# disable read-ahead if test read is enabled
+	test $test_read = 0 || blockdev --setra 0 $ROOTDEV
+	for s in $( ls $SNAPDIR/ 2> /dev/null ) ; do
+		$0 delete $s 2> /dev/null
+	done
+	for s in $( ls $SNAPDIR/ 2> /dev/null ) ; do
+		$0 remove $s 2> /dev/null
+	done
+	for n in $( seq 0 $N ) ; do
+		$0 test $n $3 $4 || exit 1
+	done
+	$0 lstest
+	for n in $( seq 1 $N ) ; do
+		$0 lstest $n
+	done
+	# skip fsck if non zero read delay or zero delay between tests         
+        ( [ ${test_read}_ms = 0_ms ] && [ ${3}_ms != 0_ms ] ) || exit 0
+	sleep 1
+	if [ $N = 0 ] ; then
+		$0 umount $ROOTMNT || exit 1
+		$0 fsck $ROOTDEV
+		$0 mount $ROOTDEV $ROOTMNT || exit 1
+	else
+		for n in $( seq 1 $N ) ; do
+			$0 fsck $n 2> /dev/null
+		done
+	fi
+	;;
+  test)
+	if [ $1_$2 = $1_help ] ; then
+		echo "test: run snapshot sanity test N."
+		echo "usage: $0 test [test-number] [delay-sec] [file-size-mb]"
+		echo "delay-sec: sleep before test"
+		echo "file-size-mb: test file size in mega bytes (default = 1)"
+		exit 0
+	fi
+	if [ -z $2 ] ; then
+		n=1
+	else
+		n=$2
+	fi
+	if [ $n = 0 ] ; then
+		$0 rmtest
+		exit 0
+	fi
+	$0 mktest
+	echo
+	echo Running snapshot test $n:
+	echo ------------------------
+	if [ ! -z $3 ] ; then
+		sleep $3 # delay between tests
+	fi
+	if [ ! -z $4 ] ; then
+		M=$4
+	else
+		M=1
+	fi
+	F=${M}M
+	cd $ROOTMNT/$TESTDIR > /dev/null
+	NOTRUNC="conv=notrunc"
+	# uncomment the following line to run in-place write tests
+	INPLACE=$NOTRUNC
+	# uncomment the following line to run direct I/O write tests
+	DIRECT="oflag=direct"
+	TRUNCSIZE=4
+	echo Appending $F zeros to $F.1 $DIRECT
+	# append writes to new allocated blocks
+	dd if=/dev/zero bs=1M count=$M of=$F.1 $NOTRUNC oflag=append $DIRECT status=noxfer || exit 1
+	echo Writing $F random data to $n files
+	for i in $( seq 1 $n ) ; do
+		# 1st rewrite moves existing blocks to snapshot and allocates new blocks
+		fallocate -mrs -l 1M $F.$i 2>/dev/null || \
+			dd if=/dev/urandom bs=1M count=$M of=$F.$i $INPLACE status=noxfer || exit 1
+		# subsequent rewrites doesn't move blocks to snapshot
+		fallocate -mrs -l 1M $F.1 2>/dev/null || \
+			dd if=/dev/urandom bs=1M count=$M of=$F.1 $INPLACE $DIRECT status=noxfer || exit 1
+	done
+	for i in $( seq 1 $n ) ; do
+		md5sum $F.$i || exit 1
+	done > md5list
+	cd - > /dev/null
+	$0 lstest || exit 1
+	s=$n
+	$0 take $s || exit 1
+	case "$n" in
+  	  1)
+		echo Create test:
+		echo ------------
+		echo 'Creating d.txt'
+		echo dodo > $ROOTMNT/D/d.txt
+		echo 'Creating e.txt'
+		echo emu > $ROOTMNT/E/e.txt
+	  ;;
+	  2)
+		echo Write test:
+		echo -----------
+		echo 'Writing b.txt (append)'
+		echo 'barracuda' >> $ROOTMNT/B/b.txt
+		echo 'Writing c.txt (truncate)'
+		echo 'crocodile' > $ROOTMNT/C/c.txt
+	  ;;
+	  3)
+		echo Remove test:
+		echo ------------
+		echo "Truncating c.txt (to size $TRUNCSIZE)"
+		truncate -s $TRUNCSIZE $ROOTMNT/C/c.txt
+		echo 'Removing d.txt'
+		rm $ROOTMNT/D/d.txt
+	  ;;
+	  4)
+		echo Restore test:
+		echo -------------
+		f=$( ls -v $SNAPDIR/ | head -n 1 )
+		echo 'Restoring from snapshot' $f
+		if ! grep $SNAPMNT$f /proc/mounts ; then
+			$0 mount $f || exit 1
+		fi
+		rm -rf $ROOTMNT/?
+		cp -R $SNAPMNT$f/? $ROOTMNT/
+		$0 umount $f || exit 1
+	  ;;
+	  5)
+		echo Delete excluded test:
+		echo ---------------------
+	  	#echo Removing excluded files
+	  	#rm $ROOTMNT/*M
+	  ;;
+	  6)
+		echo Delete reallocated test:
+		echo ------------------------
+		#echo Removing /$F
+		#rm $ROOTMNT/$F
+	  ;;
+	  7)
+		echo Shrink snapshots test:
+		echo ---------------------
+		$0 mount 1
+		for f in 5 6 4 3 2 ; do
+			echo 'Deleting snapshot' $f
+			$0 delete $f 2> /dev/null
+			$0 stat
+		done
+	  ;;
+	  8)
+		echo Merge snapshots test:
+		echo ---------------------
+		$0 umount 1
+		for f in 7 ; do
+			echo 'Deleting snapshot' $f
+			$0 delete $f 2> /dev/null
+			$0 stat
+		done
+	  ;;
+	esac || exit 1
+	echo .
+	$0 lstest || exit 1
+	$0 lstest $s || exit 1
+	$0 stat
+	;;
+  *)
+	echo "$SCRIPT_NAME v$SCRIPT_VER ($SCRIPT_DESC)"
+	echo "usage: $0 help [cmd]"
+	echo "usage: $0 version"
+	echo
+	echo "$FSTYPE filesystem maintenance commands:"
+	echo "usage: $0 {config|mount} [$FSTYPE-device $FSTYPE-mount]"
+	echo "usage: $0 {mkfs|fsck|on|off} [$FSTYPE-device]"
+	echo "usage: $0 {stat|umount} [$FSTYPE-mount]"
+	echo
+	echo "$FSTYPE snapshot commands:"
+	echo "usage: $0 take [[next-mount@]snapshot-name] (default=next-mount@GMT-date)"
+	echo "usage: $0 {mount|umount|delete|remove|fsck|dump} [next-mount@]<snapshot-name>"
+	echo
+	echo "<snapshot-name> parameter may be just the name of the snapshot or may be"
+	echo "given in ZFS style of <$FSTYPE-mount>@<snapshot-name>."
+	echo "in the former case, the $FSTYPE mount point is either read from /proc/mounts"
+	echo "(if only one $FSTYPE filesystem is mounted) or from $CONFFILE config file."
+	echo "in the latter case, the $FSTYPE mount point is parsed from the snapshot name prefix."
+	echo
+	echo "$FSTYPE debug and test commands:"
+	echo "usage: $0 debug [debug-level]"
+	echo "usage: $0 delay <take|delete|cow|read|bitmap> [delay-sec] [delay-msec]"
+	echo "usage: $0 {test|tests} [test-number] [delay-sec] [file-size-mb]"
+	echo
+	exit 1
+esac
+
+exit 0
diff --git a/resize/main.c b/resize/main.c
index bb04d0b..f386ae3 100644
--- a/resize/main.c
+++ b/resize/main.c
@@ -452,6 +452,19 @@ int main (int argc, char ** argv)
 	if (mount_flags & EXT2_MF_MOUNTED) {
 		retval = online_resize_fs(fs, mtpt, &new_size, flags);
 	} else {
+#ifdef EXT2FS_SNAPSHOT_HAS_SNAPSHOT
+		/* do not offline resize a volume with active snapshot */
+		if (!force && (fs->super->s_feature_ro_compat &
+					EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) &&
+				fs->super->s_snapshot_inum) {
+			fprintf(stderr,
+				_("offline resize will damage next3 snapshots "
+					"on %s - Please mount the filesystem "
+					"for online resize.\n\n"),
+				device_name);
+			exit(1);
+		}
+#endif
 		if (!force && ((fs->super->s_lastcheck < fs->super->s_mtime) ||
 			       (fs->super->s_state & EXT2_ERROR_FS) ||
 			       ((fs->super->s_state & EXT2_VALID_FS) == 0))) {
diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index 024f1cc..333d656 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -48,6 +48,9 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs);
 static errcode_t inode_ref_fix(ext2_resize_t rfs);
 static errcode_t move_itables(ext2_resize_t rfs);
 static errcode_t fix_resize_inode(ext2_filsys fs);
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+static errcode_t fix_exclude_inode(ext2_filsys fs);
+#endif
 static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs);
 static errcode_t fix_sb_journal_backup(ext2_filsys fs);
 
@@ -153,6 +156,12 @@ errcode_t resize_fs(ext2_filsys fs, blk_t *new_size, int flags,
 	if (retval)
 		goto errout;
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+	retval = fix_exclude_inode(rfs->new_fs);
+	if (retval)
+		goto errout;
+
+#endif
 	retval = fix_sb_journal_backup(rfs->new_fs);
 	if (retval)
 		goto errout;
@@ -1754,6 +1763,27 @@ errout:
 	return retval;
 }
 
+#ifdef EXT2FS_SNAPSHOT_EXCLUDE_INODE
+/*
+ * Fix the exclude inode
+ */
+static errcode_t fix_exclude_inode(ext2_filsys fs)
+{
+	if (!(fs->super->s_feature_compat &
+	      EXT2_FEATURE_COMPAT_EXCLUDE_INODE))
+		return 0;
+	/*
+	 * create_exclude_inode():
+	 * - updates exclude_blks for existing block groups
+	 * - allocates exclude bitmap blocks for new block groups
+	 * - doesn't free exclude bitmap blocks of deleted block group,
+	 *   so when resizing from large to small filesystem,
+	 *   it would be wise to remove the exclude inode beforehand.
+	 */
+	return ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE);
+}
+
+#endif
 /*
  * Finally, recalculate the summary information
  */
diff --git a/version.h b/version.h
index 7005be1..a24a9e9 100644
--- a/version.h
+++ b/version.h
@@ -7,5 +7,5 @@
  * file may be redistributed under the GNU Public License v2.
  */
 
-#define E2FSPROGS_VERSION "1.41.14"
-#define E2FSPROGS_DATE "22-Dec-2010"
+#define E2FSPROGS_VERSION "1.41.14-next3-1.0.13-7"
+#define E2FSPROGS_DATE "24-May-2011"