Adds Next3 snapshot awareness to e2fsprogs.

There are no snapshot COW operations during offline processing,
only restrictions on changing a file system with snapshots offline.
Adds the compatible features 'exclude_inode' and 'big_journal'.
Adds the ro-compatible features 'has_snapshot','is_snapshot','fix_exclude','fix_snapshot'.
chattr is used to control Next3 snapshots and lsattr is used to display snapshots status.

Signed-off-by: Amir Goldstein <amir73il@users.sf.net>

--------------------------------------------------------------------------------
diff -Nuarp a/debugfs/set_fields.c b/debugfs/set_fields.c
--- a/debugfs/set_fields.c	2009-08-23 05:44:35.000000000 +0300
+++ b/debugfs/set_fields.c	2010-07-26 09:47:16.800445322 +0300
@@ -134,6 +134,11 @@ static struct field_set_info super_field
 	{ "raid_stripe_width", &set_sb.s_raid_stripe_width, 4, parse_uint },
 	{ "log_groups_per_flex", &set_sb.s_log_groups_per_flex, 1, parse_uint },
 	{ "kbytes_written", &set_sb.s_kbytes_written, 8, parse_uint },
+	{ "snapshot_inum", &set_sb.s_snapshot_inum, 4, parse_uint },
+	{ "snapshot_id", &set_sb.s_snapshot_id, 4, parse_uint },
+	{ "snapshot_r_blocks_count", &set_sb.s_snapshot_r_blocks_count,
+		8, parse_uint },
+	{ "snapshot_list", &set_sb.s_snapshot_list, 4, parse_uint },
 	{ 0, 0, 0, 0 }
 };
 
diff -Nuarp a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
--- a/e2fsck/e2fsck.h	2009-08-23 05:39:05.000000000 +0300
+++ b/e2fsck/e2fsck.h	2010-07-26 09:47:16.740445926 +0300
@@ -155,6 +155,7 @@ struct resource_track {
 #define E2F_OPT_WRITECHECK	0x0200
 #define E2F_OPT_COMPRESS_DIRS	0x0400
 #define E2F_OPT_FRAGCHECK	0x0800
+#define E2F_OPT_FIX_SNAPSHOT	0x1000
 
 /*
  * E2fsck flags
@@ -176,6 +177,11 @@ struct resource_track {
 #define E2F_FLAG_RESIZE_INODE	0x0400 /* Request to recreate resize inode */
 #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 */
+#define E2F_FLAG_EXCLUDE_INODE	0x4000 /* Request to recreate exclude inode */
+#define E2F_FLAG_CLEAR_SNAPSHOTS	0x8000 /* Clear all snapshot inodes */
+
+#define E2F_RESET_FLAGS (E2F_FLAG_TIME_INSANE)
 
 /*
  * Defines for indicating the e2fsck pass number
@@ -230,6 +236,7 @@ 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 */
+	ext2fs_block_bitmap block_excluded_map; /* Blocks which are excluded */
 	ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */
 	ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */
 
@@ -472,6 +479,8 @@ void e2fsck_rehash_directories(e2fsck_t 
 void check_super_block(e2fsck_t ctx);
 int check_backup_super_block(e2fsck_t ctx);
 void check_resize_inode(e2fsck_t ctx);
+void check_exclude_inode(e2fsck_t ctx);
+void check_snapshots(e2fsck_t ctx);
 
 /* util.c */
 extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
diff -Nuarp a/e2fsck/journal.c b/e2fsck/journal.c
--- a/e2fsck/journal.c	2009-08-23 05:44:35.000000000 +0300
+++ b/e2fsck/journal.c	2010-07-26 09:47:16.710487408 +0300
@@ -836,6 +836,20 @@ static errcode_t recover_ext3_journal(e2
 
 
 	if (journal->j_superblock->s_errno) {
+		/* journal message buffer at journal super block + 1K */
+		char *buf = ((char *) journal->j_superblock) +
+			SUPERBLOCK_OFFSET;
+		int len = ctx->fs->blocksize - 2*SUPERBLOCK_OFFSET;
+
+		if (len >= 2*SUPERBLOCK_OFFSET && *buf) {
+			/* write journal message buffer to super block + 2K */
+			io_channel_set_blksize(ctx->fs->io, SUPERBLOCK_OFFSET);
+			retval = io_channel_write_blk(ctx->fs->io, 2, 2, buf);
+			io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize);
+			/* clear journal message buffer */
+			memset(buf, 0, len);
+		}
+
 		ctx->fs->super->s_state |= EXT2_ERROR_FS;
 		ext2fs_mark_super_dirty(ctx->fs);
 		journal->j_superblock->s_errno = 0;
diff -Nuarp a/e2fsck/pass1.c b/e2fsck/pass1.c
--- a/e2fsck/pass1.c	2009-08-23 05:44:35.000000000 +0300
+++ b/e2fsck/pass1.c	2010-07-26 09:47:16.720444777 +0300
@@ -55,6 +55,13 @@
 #define _INLINE_ inline
 #endif
 
+#define ext2fs_inode_i_blocks(fs, inode) \
+	(inode)->i_blocks
+#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,12 +86,12 @@ static void adjust_extattr_refcount(e2fs
 struct process_block_struct {
 	ext2_ino_t	ino;
 	unsigned	is_dir:1, is_reg:1, clear:1, suppress:1,
-				fragmented:1, compressed:1, bbcheck:1;
-	blk_t		num_blocks;
-	blk_t		max_blocks;
+			fragmented:1, compressed:1, bbcheck:1, snapfile:1;
+	blk64_t		num_blocks;
+	blk64_t		max_blocks;
 	e2_blkcnt_t	last_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;
@@ -581,6 +588,16 @@ void e2fsck_pass1(e2fsck_t ctx)
 		ctx->flags |= E2F_FLAG_ABORT;
 		return;
 	}
+	if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_EXCLUDE_INODE)
+		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;
+	}
 	e2fsck_setup_tdb_icount(ctx, 0, &ctx->inode_link_info);
 	if (!ctx->inode_link_info)
 		pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
@@ -845,7 +862,7 @@ 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;
-			} else if (ino == EXT2_RESIZE_INO) {
+			} else if (ino == EXT2_RESIZE_INO || ino == EXT2_EXCLUDE_INO) {
 				if (inode->i_mode &&
 				    !LINUX_S_ISREG(inode->i_mode))
 					problem = PR_1_RESERVED_BAD_MODE;
@@ -1026,7 +1043,8 @@ 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)) {
+		     (inode->i_flags & EXT4_SNAPFILE_FL) ||
+		     ext2fs_file_acl_block(inode))) {
 			inodes_to_process[process_inode_count].ino = ino;
 			inodes_to_process[process_inode_count].inode = *inode;
 			process_inode_count++;
@@ -1094,6 +1112,25 @@ void e2fsck_pass1(e2fsck_t ctx)
 		ctx->flags &= ~E2F_FLAG_RESIZE_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;
+	}
+
 	if (ctx->flags & E2F_FLAG_RESTART) {
 		/*
 		 * Only the master copy of the superblock and block
@@ -1598,6 +1635,17 @@ void e2fsck_clear_inode(e2fsck_t ctx, ex
 			struct ext2_inode *inode, int restart_flag,
 			const char *source)
 {
+	/* 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);
+	}
+
 	inode->i_flags = 0;
 	inode->i_links_count = 0;
 	ext2fs_icount_store(ctx->inode_link_info, ino, 0);
@@ -1818,6 +1866,7 @@ static void check_blocks(e2fsck_t ctx, s
 	pb.previous_block = 0;
 	pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
 	pb.is_reg = LINUX_S_ISREG(inode->i_mode);
+	pb.snapfile = (pb.is_reg && (inode->i_flags & EXT4_SNAPFILE_FL));
 	pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
 	pb.inode = inode;
 	pb.pctx = pctx;
@@ -1825,6 +1874,13 @@ static void check_blocks(e2fsck_t ctx, s
 	pctx->ino = ino;
 	pctx->errcode = 0;
 
+	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;
+	}
+
 	extent_fs = (ctx->fs->super->s_feature_incompat &
                      EXT3_FEATURE_INCOMPAT_EXTENTS);
 
@@ -1917,8 +1973,10 @@ static void check_blocks(e2fsck_t ctx, s
 		}
 	}
 
-	if (!(fs->super->s_feature_ro_compat &
-	      EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
+	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)) ||
 	    !(inode->i_flags & EXT4_HUGE_FILE_FL))
 		pb.num_blocks *= (fs->blocksize / 512);
 #if 0
@@ -1948,6 +2006,7 @@ static void check_blocks(e2fsck_t ctx, s
 		     size < (__u64)(pb.last_block & ~(blkpg-1)) *fs->blocksize))
 			bad_size = 3;
 		else if (!(extent_fs && (inode->i_flags & EXT4_EXTENTS_FL)) &&
+			 !(pb.is_reg && (inode->i_flags & EXT4_SNAPFILE_FL)) &&
 			 size > ext2_max_sizes[fs->super->s_log_block_size])
 			/* too big for a direct/indirect-mapped file */
 			bad_size = 4;
@@ -1972,9 +2031,11 @@ static void check_blocks(e2fsck_t ctx, s
 	if (LINUX_S_ISREG(inode->i_mode) &&
 	    (inode->i_size_high || inode->i_size & 0x80000000UL))
 		ctx->large_files++;
-	if ((pb.num_blocks != inode->i_blocks) ||
-	    ((fs->super->s_feature_ro_compat &
-	      EXT4_FEATURE_RO_COMPAT_HUGE_FILE) &&
+	if ((pb.num_blocks != ext2fs_inode_i_blocks(fs, inode)) ||
+	    (((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)) &&
 	     (inode->i_flags & EXT4_HUGE_FILE_FL) &&
 	     (inode->osd2.linux2.l_i_blocks_hi != 0))) {
 		pctx->num = pb.num_blocks;
@@ -2189,6 +2250,9 @@ static int process_block(ext2_filsys fs,
 			mark_block_used(ctx, blk);
 	} else
 		mark_block_used(ctx, blk);
+	/* mark snapshot file blocks excluded */
+	if (p->snapfile && ctx->block_excluded_map)
+		ext2fs_fast_mark_block_bitmap2(ctx->block_excluded_map, blk);
 	p->num_blocks++;
 	if (blockcnt >= 0)
 		p->last_block = blockcnt;
diff -Nuarp a/e2fsck/pass5.c b/e2fsck/pass5.c
--- a/e2fsck/pass5.c	2009-08-23 05:44:35.000000000 +0300
+++ b/e2fsck/pass5.c	2010-07-26 09:47:16.720444777 +0300
@@ -14,6 +14,7 @@
 #include "problem.h"
 
 static void check_block_bitmaps(e2fsck_t ctx);
+static void check_exclude_bitmaps(e2fsck_t ctx);
 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 +45,9 @@ void e2fsck_pass5(e2fsck_t ctx)
 	check_block_bitmaps(ctx);
 	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
 		return;
+	check_exclude_bitmaps(ctx);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		return;
 	check_inode_bitmaps(ctx);
 	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
 		return;
@@ -60,6 +64,9 @@ void e2fsck_pass5(e2fsck_t ctx)
 	ctx->inode_dir_map = 0;
 	ext2fs_free_block_bitmap(ctx->block_found_map);
 	ctx->block_found_map = 0;
+	if (ctx->block_excluded_map)
+		ext2fs_free_block_bitmap(ctx->block_excluded_map);
+	ctx->block_excluded_map = 0;
 
 	print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io);
 }
@@ -82,6 +89,18 @@ static void print_bitmap_problem(e2fsck_
 		else
 			problem = PR_5_BLOCK_RANGE_USED;
 		break;
+	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;
 	case PR_5_INODE_UNUSED:
 		if (pctx->ino == pctx->ino2)
 			pctx->ino2 = 0;
@@ -293,6 +312,10 @@ redo_counts:
 	} else if (fixit == 0)
 		ext2fs_unmark_valid(fs);
 
+	if (fs->super->s_flags & EXT2_FLAGS_IS_SNAPSHOT)
+		/* ignore free block counts in next3 snapshot image */
+		goto errout;
+
 	for (i = 0; i < fs->group_desc_count; i++) {
 		if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
 			pctx.group = i;
@@ -323,6 +346,140 @@ 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
+
+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_INODE))
+		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);
+}
+
 static void check_inode_bitmaps(e2fsck_t ctx)
 {
 	ext2_filsys fs = ctx->fs;
diff -Nuarp a/e2fsck/problem.c b/e2fsck/problem.c
--- a/e2fsck/problem.c	2009-08-22 20:19:04.000000000 +0300
+++ b/e2fsck/problem.c	2010-07-26 09:47:16.690443946 +0300
@@ -40,7 +40,9 @@
 #define PROMPT_UNLINK	17
 #define PROMPT_CLEAR_HTREE 18
 #define PROMPT_RECREATE 19
-#define PROMPT_NULL	20
+#define PROMPT_TERMINATE_LIST 20
+#define PROMPT_DISCARD_SNAPSHOTS 21
+#define PROMPT_NULL	22
 
 /*
  * These are the prompts which are used to ask the user if they want
@@ -67,7 +69,9 @@ static const char *prompt[] = {
 	N_("Unlink"),		/* 17 */
 	N_("Clear HTree index"),/* 18 */
 	N_("Recreate"),		/* 19 */
-	"",			/* 20 */
+	N_("Terminate list"),	/* 20 */
+	N_("Discard snapshots"),/* 21 */
+	"",			/* 22 */
 };
 
 /*
@@ -332,6 +336,27 @@ static struct e2fsck_problem problem_tab
 	  N_("Resize @i not valid.  "),
 	  PROMPT_RECREATE, 0 },
 
+	/* 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 },
+
+	/* Bad snapshot on list */
+	{ PR_0_BAD_SNAPSHOT,
+	  N_("Bad @i found on snapshot list.  "),
+	  PROMPT_TERMINATE_LIST, PR_PREEN_OK },
+
+	/* 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 },
+
 	/* 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 +825,11 @@ static struct e2fsck_problem problem_tab
 	  N_("Resize @i (re)creation failed: %m."),
 	  PROMPT_ABORT, 0 },
 
+	/* Exclude inode failed */
+	{ PR_1_EXCLUDE_INODE_CREATE,
+	  N_("Exclude @i (re)creation failed: %m."),
+	  PROMPT_CLEAR, 0 },
+
 	/* invalid inode->i_extra_isize */
 	{ PR_1_EXTRA_ISIZE,
 	  N_("@i %i has a extra size (%IS) which is @n\n"),
@@ -1528,6 +1558,26 @@ static struct e2fsck_problem problem_tab
 	  "\n",
 	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
 
+	/* 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 },
+
 	/* Inode bitmap differences header */
 	{ PR_5_INODE_BITMAP_HEADER,
 	  N_("@i @B differences: "),
@@ -1604,6 +1654,16 @@ static struct e2fsck_problem problem_tab
 	  " +(%b--%c)",
 	  PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
 
+	/* 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 },
+
 	/* Inode range not used, but marked in bitmap */
 	{ PR_5_INODE_RANGE_UNUSED,
 	  " -(%i--%j)",
@@ -1645,6 +1705,7 @@ 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 },
+	{ PR_LATCH_XBITMAP, PR_5_EXCLUDE_BITMAP_HEADER, PR_5_EXCLUDE_BITMAP_END },
 	{ 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 -Nuarp a/e2fsck/problem.h b/e2fsck/problem.h
--- a/e2fsck/problem.h	2009-08-22 20:19:04.000000000 +0300
+++ b/e2fsck/problem.h	2010-07-26 09:47:16.730448437 +0300
@@ -39,6 +39,7 @@ 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 */
+#define PR_LATCH_XBITMAP 0x00B0 /* Latch for pass 5 exclude bitmap proc. */
 
 #define PR_LATCH(x)	((((x) & PR_LATCH_MASK) >> 4) - 1)
 
@@ -227,6 +228,18 @@ struct problem_context {
 /* Block group checksum (latch question) */
 #define PR_0_GDT_CSUM_LATCH			0x00003E
 
+/* 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
+
+/* Bas snapshot on list */
+#define PR_0_BAD_SNAPSHOT			0x000102
+
+/* Corrupted snapshot */
+#define PR_0_FIX_SNAPSHOT			0x000103
+
 
 /*
  * Pass 1 errors
@@ -517,6 +530,9 @@ struct problem_context {
 /* Extent node header invalid */
 #define PR_1_EXTENT_HEADER_INVALID	0x01005F
 
+/* Exclude inode failed */
+#define PR_1_EXCLUDE_INODE_CREATE	0x010100
+
 /*
  * Pass 1b errors
  */
@@ -980,6 +996,24 @@ struct problem_context {
 /* Inode in use but group is marked INODE_UNINIT */
 #define PR_5_INODE_UNINIT		0x050019
 
+/* 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
+
 /*
  * Post-Pass 5 errors
  */
diff -Nuarp a/e2fsck/rehash.c b/e2fsck/rehash.c
--- a/e2fsck/rehash.c	2009-08-23 05:44:35.000000000 +0300
+++ b/e2fsck/rehash.c	2010-07-26 09:47:16.710487408 +0300
@@ -812,6 +812,13 @@ void e2fsck_rehash_directories(e2fsck_t 
 	int			cur, max, all_dirs, dir_index, first = 1;
 
 	init_resource_track(&rtrack, ctx->fs->io);
+
+ 	/* 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;
+
 	all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
 
 	if (!ctx->dirs_to_hash && !all_dirs)
diff -Nuarp a/e2fsck/super.c b/e2fsck/super.c
--- a/e2fsck/super.c	2009-08-23 05:44:35.000000000 +0300
+++ b/e2fsck/super.c	2010-07-26 09:47:16.700442435 +0300
@@ -229,6 +229,12 @@ static int release_orphan_inodes(e2fsck_
 	struct problem_context pctx;
 	char *block_buf;
 
+	/* 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;
+
 	if ((ino = fs->super->s_last_orphan) == 0)
 		return 0;
 
@@ -424,6 +430,240 @@ cleanup:
  }
 
 /*
+ * 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);
+
+	/* Migrate from old to new Next3 on-disk format */
+	if (fs->super->s_feature_compat &
+	      NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD) {
+		/* Move exclude inode from old to new position */
+		retval = ext2fs_read_inode(fs, EXT2_EXCLUDE_INO_OLD, &inode);
+		if (!retval) {
+			e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, &inode,
+					   "copy_old_exclude_ino");
+			memset(&inode, 0, sizeof(inode));
+			e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO_OLD, &inode,
+					   "clear_old_exclude_ino");
+			/* Clear old exclude inode flag */
+			fs->super->s_feature_compat &=
+				~NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD;
+			ext2fs_mark_super_dirty(fs);
+		}
+	}
+
+	/* 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;
+	}
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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;
+
+	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_flags & EXT2_FLAGS_FIX_SNAPSHOT) ||
+				(sb->s_snapshot_list || sb->s_snapshot_inum)) {
+				/* reset snapshot list head */
+				sb->s_snapshot_list = sb->s_snapshot_inum = 0;
+				sb->s_flags &= ~EXT2_FLAGS_FIX_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;
+		}
+	}
+
+	if (!check_snapshot_list(ctx))
+		/* no valid snapshots on list */
+		return;
+
+	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);
+	}
+}
+
+/*
  * This function checks the dirhash signed/unsigned hint if necessary.
  */
 static void e2fsck_fix_dirhash_hint(e2fsck_t ctx)
@@ -450,6 +690,43 @@ static void e2fsck_fix_dirhash_hint(e2fs
 	}
 }
 
+/*
+ * This function prints the message buffer at the end of super block.
+ */
+static void e2fsck_print_message_buffer(e2fsck_t ctx)
+{
+	char *buf;
+	int len = ctx->fs->blocksize - 2*SUPERBLOCK_OFFSET;
+	unsigned offset = 0;
+	int retval;
+#define MSGLEN 256
+
+	if (len < 2*SUPERBLOCK_OFFSET)
+		return;
+
+	buf = (char *) e2fsck_allocate_memory(ctx, len, "message buffer");
+
+	io_channel_set_blksize(ctx->fs->io, SUPERBLOCK_OFFSET);
+	/* read message buffer from super block + 2K */
+	retval = io_channel_read_blk(ctx->fs->io, 2, 2, buf);
+	if (retval || !*buf)
+		goto out;
+
+	/* print messages in buffer */
+	puts("Error messages recorded in message buffer:");
+	while (offset < len && buf[offset]) {
+		fputs(buf+offset, stdout);
+		offset += MSGLEN;
+	}
+	/* clear message buffer */
+	memset(buf, 0, len);
+	retval = io_channel_write_blk(ctx->fs->io, 2, 2, buf);
+	puts("End of message buffer.");
+out:
+	io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize);
+	ext2fs_free_mem(&buf);
+}
+
 
 void check_super_block(e2fsck_t ctx)
 {
@@ -855,6 +1132,11 @@ void check_super_block(e2fsck_t ctx)
 	 */
 	e2fsck_fix_dirhash_hint(ctx);
 
+	/*
+	 * Print message buffer if necessary
+	 */
+	e2fsck_print_message_buffer(ctx);
+
 	return;
 }
 
diff -Nuarp a/e2fsck/unix.c b/e2fsck/unix.c
--- a/e2fsck/unix.c	2009-08-23 05:44:35.000000000 +0300
+++ b/e2fsck/unix.c	2010-07-26 09:47:16.700442435 +0300
@@ -73,7 +73,7 @@ int journal_enable_debug = -1;
 static void usage(e2fsck_t ctx)
 {
 	fprintf(stderr,
-		_("Usage: %s [-panyrcdfvtDFV] [-b superblock] [-B blocksize]\n"
+		_("Usage: %s [-panyrcdfvtxDFV] [-b superblock] [-B blocksize]\n"
 		"\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 +92,7 @@ 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"
+		" -x                   Fix or discard snapshots\n"
 		));
 
 	exit(FSCK_USAGE);
@@ -659,7 +660,7 @@ static errcode_t PRS(int argc, char *arg
 		ctx->program_name = *argv;
 	else
 		ctx->program_name = "e2fsck";
-	while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
+	while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkx")) != EOF)
 		switch (c) {
 		case 'C':
 			ctx->progress = e2fsck_update_progress;
@@ -782,6 +783,9 @@ static errcode_t PRS(int argc, char *arg
 		case 'k':
 			keep_bad_blocks++;
 			break;
+		case 'x':
+			ctx->options |= E2F_OPT_FIX_SNAPSHOT;
+			break;
 		default:
 			usage(ctx);
 		}
@@ -1284,6 +1288,8 @@ print_unsupp_features:
 		fatal_error(ctx, 0);
 	check_if_skip(ctx);
 	check_resize_inode(ctx);
+	check_snapshots(ctx);
+	check_exclude_inode(ctx);
 	if (bad_blocks_file)
 		read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
 	else if (cflag)
diff -Nuarp a/lib/e2p/e2p.h b/lib/e2p/e2p.h
--- a/lib/e2p/e2p.h	2009-08-23 05:44:35.000000000 +0300
+++ b/lib/e2p/e2p.h	2010-07-26 09:47:16.630443266 +0300
@@ -17,6 +17,24 @@
 /* `options' for print_flags() */
 
 #define PFOPT_LONG  1 /* Must be 1 for compatibility with `int long_format'. */
+#define PFOPT_SNAPSHOT  2 /* list/control snapshot flags */
+#define PFOPT_SNAPSHOT_X 4 /* -X option for 'Snapshot' flags */
+
+/*
+ * snapshot status/control flags for lsattr/chattr -X.
+ * reusing compression flags on the GET/SETFLAGS ioctl for snapshot control API.
+ * all the flags below are either read-only on-disk inode flags (deleted and
+ * shrunk) or in-memory inode status flags (the rest).
+ * TODO: implement new ioctls for snapshot status/control.
+ */
+#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 */
 
 
 int fgetflags (const char * name, unsigned long * flags);
diff -Nuarp a/lib/e2p/feature.c b/lib/e2p/feature.c
--- a/lib/e2p/feature.c	2009-08-13 04:39:57.000000000 +0300
+++ b/lib/e2p/feature.c	2010-07-26 09:47:16.620442748 +0300
@@ -39,6 +39,8 @@ static struct feature feature_list[] = {
 			"resize_inode" },
 	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_LAZY_BG,
 			"lazy_bg" },
+	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_INODE,
+			"exclude_inode" },
 
 	{	E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER,
 			"sparse_super" },
@@ -54,6 +56,8 @@ static struct feature feature_list[] = {
 			"dir_nlink" },
 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE,
 			"extra_isize" },
+	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT,
+			"has_snapshot" },
 
 	{	E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION,
 			"compression" },
diff -Nuarp a/lib/e2p/ls.c b/lib/e2p/ls.c
--- a/lib/e2p/ls.c	2009-08-23 05:44:35.000000000 +0300
+++ b/lib/e2p/ls.c	2010-07-26 09:47:16.620442748 +0300
@@ -158,6 +158,18 @@ static void print_super_flags(struct ext
 		fputs("test_filesystem ", f);
 		flags_found++;
 	}
+	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++;
+	}
 	if (flags_found)
 		fputs("\n", f);
 	else
@@ -311,6 +323,17 @@ void list_super2(struct ext2_super_block
 	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 -Nuarp a/lib/e2p/pf.c b/lib/e2p/pf.c
--- a/lib/e2p/pf.c	2009-08-13 04:39:57.000000000 +0300
+++ b/lib/e2p/pf.c	2010-07-26 09:47:16.610443636 +0300
@@ -46,16 +46,47 @@ 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" },
+	{ EXT4_SNAPFILE_FL, "x", "Snapshot_File" },
+	{ 0, NULL, NULL }
+};
+
+/* Traditional snapshot flags */
+static struct flags_name snapshot_flags_array[] = {
+	{ NEXT3_SNAPFILE_LIST_FL, "l", "on_List" },
+	{ NEXT3_SNAPFILE_ENABLED_FL, "e", "Enabled" },
+	{ NEXT3_SNAPFILE_ACTIVE_FL, "a", "Active" },
+	{ NEXT3_SNAPFILE_INUSE_FL, "i", "Inuse_by_previous" },
+	{ NEXT3_SNAPFILE_DELETED_FL, "d", "Deleted" },
+	{ NEXT3_SNAPFILE_SHRUNK_FL, "s", "Shrunk" },
+	{ NEXT3_SNAPFILE_OPEN_FL, "m", "Mounted" },
+	{ NEXT3_SNAPFILE_TAGGED_FL, "t", "Tagged" },
+	{ 0, NULL, NULL }
+};
+
+/* Cool 'Snapshot' flags */
+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 }
 };
 
 void print_flags (FILE * f, unsigned long flags, unsigned options)
 {
+	struct flags_name *array = ((options & PFOPT_SNAPSHOT_X) ?
+			snapshot_X_flags_array :
+			((options & PFOPT_SNAPSHOT) ?
+			 snapshot_flags_array : flags_array));
 	int long_opt = (options & PFOPT_LONG);
 	struct flags_name *fp;
 	int	first = 1;
 
-	for (fp = flags_array; fp->flag != 0; fp++) {
+	for (fp = array; fp->flag != 0; fp++) {
 		if (flags & fp->flag) {
 			if (long_opt) {
 				if (first)
diff -Nuarp a/lib/ext2fs/block.c b/lib/ext2fs/block.c
--- a/lib/ext2fs/block.c	2009-08-23 05:44:35.000000000 +0300
+++ b/lib/ext2fs/block.c	2010-07-26 09:47:16.650443902 +0300
@@ -309,6 +309,7 @@ errcode_t ext2fs_block_iterate2(ext2_fil
 	errcode_t	retval;
 	struct block_context ctx;
 	int	limit;
+	blk_t	blk64;
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -456,9 +457,17 @@ errcode_t ext2fs_block_iterate2(ext2_fil
 	 * Iterate over normal data blocks
 	 */
 	for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
+		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;
+
 		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;
 		}
@@ -484,6 +493,15 @@ errcode_t ext2fs_block_iterate2(ext2_fil
 		if (ret & BLOCK_ABORT)
 			goto abort_exit;
 	}
+	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++) {
+			ret |= block_iterate_tind(&inode.i_block[i],
+						  0, EXT2_N_BLOCKS+i, &ctx);
+			if (ret & BLOCK_ABORT)
+				goto abort_exit;
+		}
+	}
 
 abort_exit:
 	if (ret & BLOCK_CHANGED) {
diff -Nuarp a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c
--- a/lib/ext2fs/bmap.c	2009-08-13 04:39:57.000000000 +0300
+++ b/lib/ext2fs/bmap.c	2010-07-26 09:47:16.660444109 +0300
@@ -136,6 +136,8 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, e
 	struct ext2_inode inode_buf;
 	ext2_extent_handle_t handle = 0;
 	blk_t addr_per_block;
+	blk64_t addr_per_tind_block;
+	int	tind;
 	blk_t	b, blk32;
 	char	*buf = 0;
 	errcode_t	retval = 0;
@@ -289,7 +291,20 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, e
 
 	/* Triply indirect block */
 	block -= addr_per_block * addr_per_block;
-	b = inode_bmap(inode, EXT2_TIND_BLOCK);
+	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);
 	if (!b) {
 		if (!(bmap_flags & BMAP_ALLOC)) {
 			if (bmap_flags & BMAP_SET)
@@ -301,7 +316,7 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, e
 		retval = ext2fs_alloc_block(fs, b, block_buf, &b);
 		if (retval)
 			goto done;
-		inode_bmap(inode, EXT2_TIND_BLOCK) = b;
+		inode_bmap(inode, tind) = b;
 		blocks_alloc++;
 	}
 	retval = block_tind_bmap(fs, bmap_flags, b, block_buf,
diff -Nuarp a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
--- a/lib/ext2fs/ext2_fs.h	2009-08-23 05:44:35.000000000 +0300
+++ b/lib/ext2fs/ext2_fs.h	2010-07-26 09:47:16.680445479 +0300
@@ -50,6 +50,8 @@
 #define EXT2_UNDEL_DIR_INO	 6	/* Undelete directory inode */
 #define EXT2_RESIZE_INO		 7	/* Reserved group descriptors inode */
 #define EXT2_JOURNAL_INO	 8	/* Journal inode */
+#define EXT2_EXCLUDE_INO	 9	/* The "exclude" inode, for snapshots */
+#define EXT2_EXCLUDE_INO_OLD	10	/* Old exclude inode */
 
 /* First non-reserved inode for old ext2 filesystems */
 #define EXT2_GOOD_OLD_FIRST_INO	11
@@ -149,6 +151,12 @@ struct ext2_group_desc
 	__u16	bg_checksum;		/* crc16(s_uuid+grouo_num+group_desc)*/
 };
 
+#define bg_exclude_bitmap_old bg_reserved[0]	/* Old exclude bitmap cache */
+#define bg_cow_bitmap_old bg_reserved[1]	/* Old COW bitmap cache */
+
+/*
+ * Structure of a blocks group descriptor
+ */
 struct ext4_group_desc
 {
 	__u32	bg_block_bitmap;	/* Blocks bitmap block */
@@ -246,6 +254,17 @@ 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)
+/*
+ * 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
 
 /*
  * Inode flags
@@ -273,10 +292,14 @@ struct ext2_dx_countlimit {
 #define EXT2_TOPDIR_FL			0x00020000 /* Top of directory hierarchies*/
 #define EXT4_HUGE_FILE_FL               0x00040000 /* Set to each huge file */
 #define EXT4_EXTENTS_FL 		0x00080000 /* Inode uses extents */
+#define EXT4_EOFBLOCKS_FL		0x00400000 /* Blocks allocated beyond EOF */
+#define EXT4_SNAPFILE_FL		0x01000000  /* Inode is a snapshot */
+#define EXT4_SNAPFILE_DELETED_FL	0x04000000  /* Snapshot is being deleted */
+#define EXT4_SNAPFILE_SHRUNK_FL		0x08000000  /* Snapshot shrink has completed */
 #define EXT2_RESERVED_FL		0x80000000 /* reserved for ext2 lib */
 
-#define EXT2_FL_USER_VISIBLE		0x000BDFFF /* User visible flags */
-#define EXT2_FL_USER_MODIFIABLE		0x000080FF /* User modifiable flags */
+#define EXT2_FL_USER_VISIBLE		0x004BDFFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE		0x004B80FF /* User modifiable flags */
 
 /*
  * ioctl commands
@@ -422,7 +445,7 @@ struct ext2_inode_large {
 #define i_size_high	i_dir_acl
 
 #if defined(__KERNEL__) || defined(__linux__)
-#define i_reserved1	osd1.linux1.l_i_reserved1
+#define i_next_snapshot	osd1.linux1.l_i_version
 #define i_frag		osd2.linux2.l_i_frag
 #define i_fsize		osd2.linux2.l_i_fsize
 #define i_uid_low	i_uid
@@ -461,6 +484,9 @@ struct ext2_inode_large {
 #define EXT2_FLAGS_SIGNED_HASH		0x0001  /* Signed dirhash in use */
 #define EXT2_FLAGS_UNSIGNED_HASH	0x0002  /* Unsigned dirhash in use */
 #define EXT2_FLAGS_TEST_FILESYS		0x0004	/* OK for use on development code */
+#define EXT2_FLAGS_IS_SNAPSHOT		0x0010	/* This is a snapshot image */
+#define EXT2_FLAGS_FIX_SNAPSHOT		0x0020	/* Snapshot inodes corrupted */
+#define EXT2_FLAGS_FIX_EXCLUDE		0x0040	/* Exclude bitmaps corrupted */
 
 /*
  * Mount flags
@@ -580,9 +606,21 @@ struct ext2_super_block {
 	__u8    s_reserved_char_pad;
 	__u16	s_reserved_pad;		/* Padding to next 32bits */
 	__u64	s_kbytes_written;	/* nr of lifetime kilobytes written */
-	__u32   s_reserved[160];        /* Padding to the end of the block */
+	__u32	s_snapshot_inum;	/* Inode number of active snapshot */
+	__u32	s_snapshot_id;		/* sequential ID of active snapshot */
+	__u64	s_snapshot_r_blocks_count; /* reserved blocks for active
+					      snapshot's future use */
+	__u32	s_snapshot_list;	/* inode number of the head of the
+					   on-disk snapshot list */
+	__u32   s_reserved[155];        /* Padding to the end of the block */
 };
 
+/* old snapshot field positions */
+#define s_snapshot_list_old	s_reserved[151]	/* Old snapshot list head */
+#define s_snapshot_r_blocks_old	s_reserved[152] /* Old reserved for snapshot */
+#define s_snapshot_id_old	s_reserved[153]	/* Old active snapshot ID */
+#define s_snapshot_inum_old	s_reserved[154]	/* Old active snapshot inode */
+
 /*
  * Codes for operating systems
  */
@@ -609,6 +647,17 @@ struct ext2_super_block {
 #define EXT3_JNL_BACKUP_BLOCKS	1
 
 /*
+ * 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)
+
+/*
  * Feature set definitions
  */
 
@@ -626,6 +675,9 @@ struct ext2_super_block {
 #define EXT2_FEATURE_COMPAT_RESIZE_INODE	0x0010
 #define EXT2_FEATURE_COMPAT_DIR_INDEX		0x0020
 #define EXT2_FEATURE_COMPAT_LAZY_BG		0x0040
+#define EXT2_FEATURE_COMPAT_EXCLUDE_INODE	0x0080
+#define NEXT3_FEATURE_COMPAT_BIG_JOURNAL_OLD	0x1000 /* Old big journal */
+#define NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD	0x2000 /* Old exclude inode */
 
 #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER	0x0001
 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE	0x0002
@@ -634,6 +686,11 @@ struct ext2_super_block {
 #define EXT4_FEATURE_RO_COMPAT_GDT_CSUM		0x0010
 #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK	0x0020
 #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE	0x0040
+#define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT	0x0080
+#define NEXT3_FEATURE_RO_COMPAT_HAS_SNAPSHOT_OLD 0x1000 /* Old has snapshots */
+#define NEXT3_FEATURE_RO_COMPAT_IS_SNAPSHOT_OLD	0x2000 /* Old is snapshot */
+#define NEXT3_FEATURE_RO_COMPAT_FIX_SNAPSHOT_OLD 0x4000 /* Old fix snapshot */
+#define NEXT3_FEATURE_RO_COMPAT_FIX_EXCLUDE_OLD	0x8000 /* Old fix exclude */
 
 #define EXT2_FEATURE_INCOMPAT_COMPRESSION	0x0001
 #define EXT2_FEATURE_INCOMPAT_FILETYPE		0x0002
@@ -650,6 +707,7 @@ struct ext2_super_block {
 #define EXT2_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE)
 #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)
 
diff -Nuarp a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
--- a/lib/ext2fs/ext2fs.h	2009-08-23 05:44:35.000000000 +0300
+++ b/lib/ext2fs/ext2fs.h	2010-07-26 09:47:16.680445479 +0300
@@ -172,6 +172,9 @@ 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_EXCLUDE_DIRTY		0x100000
 
 /*
  * Special flag in the ext2 inode i_flag field that means that this is
@@ -186,6 +189,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;
@@ -196,10 +201,13 @@ 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;
+	__u32 *				exclude_blks;
 	int				inode_blocks_per_group;
 	ext2fs_inode_bitmap		inode_map;
 	ext2fs_block_bitmap		block_map;
+	ext2fs_block_bitmap		exclude_map;
+	/* 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);
@@ -504,6 +512,8 @@ typedef struct ext2_icount *ext2_icount_
 #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|\
+					 NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD|\
 					 EXT2_FEATURE_COMPAT_RESIZE_INODE|\
 					 EXT2_FEATURE_COMPAT_DIR_INDEX|\
 					 EXT2_FEATURE_COMPAT_EXT_ATTR)
@@ -534,6 +544,7 @@ typedef struct ext2_icount *ext2_icount_
 #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)
@@ -644,8 +655,10 @@ extern errcode_t ext2fs_copy_bitmap(ext2
 				    ext2fs_generic_bitmap *dest);
 extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs);
 extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs);
+extern errcode_t ext2fs_write_exclude_bitmap (ext2_filsys fs);
 extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs);
 extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs);
+extern errcode_t ext2fs_read_exclude_bitmap (ext2_filsys fs);
 extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
 					      const char *descr,
 					      ext2fs_block_bitmap *ret);
@@ -1070,6 +1083,8 @@ extern errcode_t ext2fs_add_journal_devi
 extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size,
 					  int flags);
 extern int ext2fs_default_journal_size(__u64 blocks);
+extern int ext2fs_big_journal_size(int factor, __u64 blocks);
+extern int ext2fs_check_journal_size(ext2_filsys fs);
 
 /* openfs.c */
 extern errcode_t ext2fs_open(const char *name, int flags, int superblock,
@@ -1114,6 +1129,13 @@ extern errcode_t ext2fs_read_bb_FILE(ext
 
 /* res_gdt.c */
 extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
+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 */
 
 /* swapfs.c */
 extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
@@ -1306,6 +1328,14 @@ _INLINE_ void ext2fs_mark_bb_dirty(ext2_
 }
 
 /*
+ * Mark the exclude bitmap as dirty
+ */
+_INLINE_ void ext2fs_mark_exclude_dirty(ext2_filsys fs)
+{
+	fs->flags |= EXT2_FLAG_EXCLUDE_DIRTY | EXT2_FLAG_CHANGED;
+}
+
+/*
  * Check to see if a filesystem's inode bitmap is dirty
  */
 _INLINE_ int ext2fs_test_ib_dirty(ext2_filsys fs)
@@ -1322,6 +1352,14 @@ _INLINE_ int ext2fs_test_bb_dirty(ext2_f
 }
 
 /*
+ * 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);
+}
+
+/*
  * Return the group # of a block
  */
 _INLINE_ int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk)
diff -Nuarp a/lib/ext2fs/i_block.c b/lib/ext2fs/i_block.c
--- a/lib/ext2fs/i_block.c	2009-08-13 04:39:57.000000000 +0300
+++ b/lib/ext2fs/i_block.c	2010-07-26 09:47:16.650443902 +0300
@@ -21,6 +21,7 @@
 #if HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
+#include <errno.h>
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
@@ -28,18 +29,24 @@
 errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode,
 				 blk64_t num_blocks)
 {
-	unsigned long long b;
+	unsigned long long b = inode->i_blocks;
 
-	if ((fs->super->s_feature_ro_compat &
-	     EXT4_FEATURE_RO_COMPAT_HUGE_FILE) &&
-	    (inode->i_flags & EXT4_HUGE_FILE_FL)) {
-		b = inode->i_blocks +
-			(((long long) inode->osd2.linux2.l_i_blocks_hi) << 32);
-		b += num_blocks;
-		inode->i_blocks = b & 0xFFFFFFFF;
+	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)) ||
+	    !(inode->i_flags & EXT4_HUGE_FILE_FL))
+	    num_blocks *= fs->blocksize / 512;
+
+	b += num_blocks;
+
+	if (fs->super->s_feature_ro_compat &
+	    EXT4_FEATURE_RO_COMPAT_HUGE_FILE) {
+		b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32;
 		inode->osd2.linux2.l_i_blocks_hi = b >> 32;
-	} else
-		inode->i_blocks += (fs->blocksize / 512) * num_blocks;
+	} else if (b > 0xFFFFFFFF)
+		return EOVERFLOW;
+	inode->i_blocks = b & 0xFFFFFFFF;
 	return 0;
 }
 
@@ -47,29 +54,42 @@ errcode_t ext2fs_iblk_add_blocks(ext2_fi
 errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode,
 				 blk64_t num_blocks)
 {
-	unsigned long long b;
+	unsigned long long b = inode->i_blocks;
 
-	if ((fs->super->s_feature_ro_compat &
-	     EXT4_FEATURE_RO_COMPAT_HUGE_FILE) &&
-	    (inode->i_flags & EXT4_HUGE_FILE_FL)) {
-		b = inode->i_blocks +
-			(((long long) inode->osd2.linux2.l_i_blocks_hi) << 32);
-		b -= num_blocks;
-		inode->i_blocks = b & 0xFFFFFFFF;
+	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)) ||
+	    !(inode->i_flags & EXT4_HUGE_FILE_FL))
+	    num_blocks *= fs->blocksize / 512;
+
+	if (num_blocks > b)
+		return EOVERFLOW;
+
+	b -= num_blocks;
+
+	if (fs->super->s_feature_ro_compat &
+	    EXT4_FEATURE_RO_COMPAT_HUGE_FILE) {
+		b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32;
 		inode->osd2.linux2.l_i_blocks_hi = b >> 32;
-	} else
-		inode->i_blocks -= (fs->blocksize / 512) * num_blocks;
+	}
+	inode->i_blocks = b & 0xFFFFFFFF;
 	return 0;
 }
 
 errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b)
 {
-	if ((fs->super->s_feature_ro_compat &
-	     EXT4_FEATURE_RO_COMPAT_HUGE_FILE) &&
-	    (inode->i_flags & EXT4_HUGE_FILE_FL)) {
-		inode->i_blocks = b & 0xFFFFFFFF;
+	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)) ||
+	    !(inode->i_flags & EXT4_HUGE_FILE_FL))
+		b *= fs->blocksize / 512;
+
+	inode->i_blocks = b & 0xFFFFFFFF;
+	if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE)
 		inode->osd2.linux2.l_i_blocks_hi = b >> 32;
-	} else
-		inode->i_blocks = (fs->blocksize / 512) * b;
+	else if (b >> 32)
+		return EOVERFLOW;
 	return 0;
 }
diff -Nuarp a/lib/ext2fs/mkjournal.c b/lib/ext2fs/mkjournal.c
--- a/lib/ext2fs/mkjournal.c	2009-08-23 05:44:35.000000000 +0300
+++ b/lib/ext2fs/mkjournal.c	2010-07-26 09:47:16.640447884 +0300
@@ -388,6 +388,47 @@ int ext2fs_default_journal_size(__u64 bl
 	return 32768;
 }
 
+/* 
+ * 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;
+}
+
 /*
  * This function adds a journal device to a filesystem
  */
diff -Nuarp a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
--- a/lib/ext2fs/openfs.c	2009-08-23 05:44:35.000000000 +0300
+++ b/lib/ext2fs/openfs.c	2010-07-26 09:47:16.670444963 +0300
@@ -199,6 +199,51 @@ errcode_t ext2fs_open2(const char *name,
 		goto cleanup;
 	}
 
+	/* Migrate super from old to new Next3 on-disk format */
+	if ((fs->super->s_feature_ro_compat &
+			NEXT3_FEATURE_RO_COMPAT_HAS_SNAPSHOT_OLD) &&
+		!(fs->super->s_feature_ro_compat &
+			EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT)) {
+		struct ext2_super_block *sb = fs->super;
+
+		/* Copy snapshot fields to new positions */
+		sb->s_snapshot_inum = sb->s_snapshot_inum_old;
+		sb->s_snapshot_id = sb->s_snapshot_id_old;
+		sb->s_snapshot_r_blocks_count = sb->s_snapshot_r_blocks_old;
+		sb->s_snapshot_list = sb->s_snapshot_list_old;
+		/* Clear old snapshot fields */
+		sb->s_snapshot_inum_old = 0;
+		sb->s_snapshot_id_old = 0;
+		sb->s_snapshot_r_blocks_old = 0;
+		sb->s_snapshot_list_old = 0;
+		/* Copy snapshot flags to new positions */
+		fs->super->s_feature_ro_compat |=
+				EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT;
+		if (fs->super->s_feature_compat &
+				NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD)
+			fs->super->s_feature_compat |=
+				EXT2_FEATURE_COMPAT_EXCLUDE_INODE;
+		if (fs->super->s_feature_ro_compat &
+				NEXT3_FEATURE_RO_COMPAT_FIX_SNAPSHOT_OLD)
+			fs->super->s_flags |= EXT2_FLAGS_FIX_SNAPSHOT;
+		if (fs->super->s_feature_ro_compat &
+				NEXT3_FEATURE_RO_COMPAT_FIX_EXCLUDE_OLD)
+			fs->super->s_flags |= EXT2_FLAGS_FIX_EXCLUDE;
+		/* Clear old snapshot flags */
+		fs->super->s_feature_ro_compat &=
+				~(NEXT3_FEATURE_RO_COMPAT_HAS_SNAPSHOT_OLD|
+				NEXT3_FEATURE_RO_COMPAT_IS_SNAPSHOT_OLD|
+				NEXT3_FEATURE_RO_COMPAT_FIX_SNAPSHOT_OLD|
+				NEXT3_FEATURE_RO_COMPAT_FIX_EXCLUDE_OLD);
+		/* Clear deprecated big journal flag */
+		fs->super->s_feature_compat &=
+				~NEXT3_FEATURE_COMPAT_BIG_JOURNAL_OLD;
+		/* Keep old exclude inode flag b/c inode was not moved yet */
+		if (flags & EXT2_FLAG_RW)
+			/* in read-only mode just convert the in-memory copy */
+			ext2fs_mark_super_dirty(fs);
+	}
+
 	/*
 	 * Check for feature set incompatibility
 	 */
diff -Nuarp a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c
--- a/lib/ext2fs/res_gdt.c	2009-08-23 05:39:05.000000000 +0300
+++ b/lib/ext2fs/res_gdt.c	2010-07-26 09:47:16.630443266 +0300
@@ -217,3 +217,243 @@ out_free:
 	return retval;
 }
 
+#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);
+
+	/* Migrate from old to new Next3 on-disk format */
+	if (fs->super->s_feature_compat &
+	      NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD) {
+		/* Move exclude inode from old to new position */
+		retval = ext2fs_read_inode(fs, EXT2_EXCLUDE_INO_OLD, &inode);
+		if (!retval) {
+			retval = ext2fs_write_inode(fs, EXT2_EXCLUDE_INO,
+					&inode);
+			if (retval)
+				goto out_free;
+			memset(&inode, 0, sizeof(inode));
+			retval = ext2fs_write_inode(fs, EXT2_EXCLUDE_INO_OLD,
+					&inode);
+			if (retval)
+				goto out_free;
+			/* Clear old exclude inode flag */
+			fs->super->s_feature_compat &=
+				~NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD;
+			ext2fs_mark_super_dirty(fs);
+		}
+	}
+
+	retval = ext2fs_read_inode(fs, EXT2_EXCLUDE_INO, &inode);
+	if (retval)
+		goto out_free;
+
+	if (fs->exclude_blks)
+		ext2fs_free_mem(&fs->exclude_blks);
+	retval = ext2fs_get_array(fs->group_desc_count, fs->blocksize,
+			&fs->exclude_blks);
+	if (retval)
+		goto out_free;
+	memset(fs->exclude_blks, 0, fs->group_desc_count*fs->blocksize);
+
+#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);
+	}
+
+	/*
+	 * init exclude_blks array 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;
+			}
+		}
+		fs->exclude_blks[grp] = data_blk;
+		/* reset old exclude/cow bitmap cache to zero */
+		if (gd->bg_exclude_bitmap_old || gd->bg_cow_bitmap_old) {
+			gd->bg_exclude_bitmap_old = 0;
+			gd->bg_cow_bitmap_old = 0;
+			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);
+	}
+#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;
+}
+
diff -Nuarp a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c
--- a/lib/ext2fs/rw_bitmaps.c	2009-08-23 05:44:35.000000000 +0300
+++ b/lib/ext2fs/rw_bitmaps.c	2010-07-26 09:47:16.660444109 +0300
@@ -25,19 +25,37 @@
 
 #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_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"
 
-static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
+static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block,
+		int do_exclude)
 {
 	dgrp_t 		i;
 	unsigned int	j;
 	int		block_nbytes, inode_nbytes;
 	unsigned int	nbits;
 	errcode_t	retval;
-	char 		*block_buf, *inode_buf;
+	char 		*block_buf, *inode_buf, *exclude_buf;
 	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 +63,17 @@ static errcode_t write_bitmaps(ext2_fils
 	if (!(fs->flags & EXT2_FLAG_RW))
 		return EXT2_ET_RO_FILSYS;
 
+	if (!EXT2_HAS_COMPAT_FEATURE(fs->super,
+				EXT2_FEATURE_COMPAT_EXCLUDE_INODE))
+		do_exclude = 0;
+
+	if (do_exclude && !fs->exclude_blks) {
+		/* read exclude_blks from exclude inode */
+		retval = ext2fs_create_exclude_inode(fs, 0);
+		if (retval)
+			return retval;
+	}
+
 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
 		csum_flag = 1;
@@ -57,6 +86,13 @@ static errcode_t write_bitmaps(ext2_fils
 			return retval;
 		memset(block_buf, 0xff, fs->blocksize);
 	}
+	if (do_exclude) {
+		block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+		retval = ext2fs_get_mem(fs->blocksize, &exclude_buf);
+		if (retval)
+			return retval;
+		memset(exclude_buf, 0xff, fs->blocksize);
+	}
 	if (do_inode) {
 		inode_nbytes = (size_t)
 			((EXT2_INODES_PER_GROUP(fs->super)+7) / 8);
@@ -67,31 +103,44 @@ static errcode_t write_bitmaps(ext2_fils
 	}
 
 	for (i = 0; i < fs->group_desc_count; i++) {
-		if (!do_block)
+		if (!do_block && !do_exclude)
 			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,
-				blk_itr, block_nbytes << 3, block_buf);
+		if (do_block)
+			retval = ext2fs_get_block_bitmap_range2(fs->block_map,
+					blk_itr, block_nbytes << 3, block_buf);
 		if (retval)
 			return retval;
 
-		if (i == fs->group_desc_count - 1) {
+		if (do_exclude)
+			retval = ext2fs_get_block_bitmap_range2(fs->exclude_map,
+					blk_itr, block_nbytes << 3, exclude_buf);
+		if (retval)
+			return retval;
+
+		if (do_block && i == fs->group_desc_count - 1) {
 			/* 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;
-		if (blk) {
-			retval = io_channel_write_blk(fs->io, blk, 1,
-						      block_buf);
+		blk = ext2fs_block_bitmap_loc(fs, i);
+		if (do_block && blk) {
+			retval = io_channel_write_blk64(fs->io, blk, 1,
+							block_buf);
+			if (retval)
+				return EXT2_ET_BLOCK_BITMAP_WRITE;
+		}
+		if (do_exclude && (blk = fs->exclude_blks[i])) {
+			retval = io_channel_write_blk64(fs->io, blk, 1,
+						      exclude_buf);
 			if (retval)
 				return EXT2_ET_BLOCK_BITMAP_WRITE;
 		}
@@ -102,8 +151,8 @@ static errcode_t write_bitmaps(ext2_fils
 		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,
@@ -111,9 +160,9 @@ static errcode_t write_bitmaps(ext2_fils
 		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;
@@ -133,10 +182,11 @@ static errcode_t write_bitmaps(ext2_fils
 	return 0;
 }
 
-static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
+static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block,
+		int do_exclude)
 {
 	dgrp_t i;
-	char *block_bitmap = 0, *inode_bitmap = 0;
+	char *block_bitmap = 0, *inode_bitmap = 0, *exclude_bitmap = 0;
 	char *buf;
 	errcode_t retval;
 	int block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
@@ -144,9 +194,9 @@ static errcode_t read_bitmaps(ext2_filsy
 	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;
 
@@ -154,6 +204,17 @@ static errcode_t read_bitmaps(ext2_filsy
 
 	fs->write_bitmaps = ext2fs_write_bitmaps;
 
+	if (!EXT2_HAS_COMPAT_FEATURE(fs->super,
+				EXT2_FEATURE_COMPAT_EXCLUDE_INODE))
+		do_exclude = 0;
+
+	if (do_exclude && !fs->exclude_blks) {
+		/* read exclude_blks from exclude inode */
+		retval = ext2fs_create_exclude_inode(fs, 0);
+		if (retval)
+			return retval;
+	}
+
 	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
 				       EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
 		csum_flag = 1;
@@ -173,7 +234,21 @@ static errcode_t read_bitmaps(ext2_filsy
 					(unsigned) block_nbytes, &block_bitmap);
 		if (retval)
 			goto cleanup;
-	} else
+	}
+	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;
+		retval = ext2fs_get_mem(do_image ? fs->blocksize :
+					(unsigned) block_nbytes, &exclude_bitmap);
+		if (retval)
+			goto cleanup;
+	}
+	if (!do_block && !do_exclude)
 		block_nbytes = 0;
 	if (do_inode) {
 		if (fs->inode_map)
@@ -195,7 +270,7 @@ static errcode_t read_bitmaps(ext2_filsy
 		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;
@@ -212,17 +287,22 @@ static errcode_t read_bitmaps(ext2_filsy
 		}
 		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++,
+			if (do_exclude) {
+				retval = EXT2_ET_BLOCK_BITMAP_READ;
+				goto cleanup;
+			}
+
+			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;
@@ -235,13 +315,13 @@ static errcode_t read_bitmaps(ext2_filsy
 
 	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;
@@ -250,20 +330,42 @@ static errcode_t read_bitmaps(ext2_filsy
 			} 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;
-			blk_itr += block_nbytes << 3;
 		}
+		if (exclude_bitmap) {
+			blk = fs->exclude_blks[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;
 		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;
@@ -284,6 +386,8 @@ success_cleanup:
 		ext2fs_free_mem(&inode_bitmap);
 	if (block_bitmap)
 		ext2fs_free_mem(&block_bitmap);
+	if (exclude_bitmap)
+		ext2fs_free_mem(&exclude_bitmap);
 	return 0;
 
 cleanup:
@@ -299,6 +403,8 @@ cleanup:
 		ext2fs_free_mem(&inode_bitmap);
 	if (block_bitmap)
 		ext2fs_free_mem(&block_bitmap);
+	if (exclude_bitmap)
+		ext2fs_free_mem(&exclude_bitmap);
 	if (buf)
 		ext2fs_free_mem(&buf);
 	return retval;
@@ -306,39 +412,50 @@ cleanup:
 
 errcode_t ext2fs_read_inode_bitmap(ext2_filsys fs)
 {
-	return read_bitmaps(fs, 1, 0);
+	return read_bitmaps(fs, 1, 0, 0);
 }
 
 errcode_t ext2fs_read_block_bitmap(ext2_filsys fs)
 {
-	return read_bitmaps(fs, 0, 1);
+	return read_bitmaps(fs, 0, 1, 0);
+}
+
+errcode_t ext2fs_read_exclude_bitmap (ext2_filsys fs)
+{
+	return read_bitmaps(fs, 0, 0, 1);
 }
 
 errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs)
 {
-	return write_bitmaps(fs, 1, 0);
+	return write_bitmaps(fs, 1, 0, 0);
 }
 
 errcode_t ext2fs_write_block_bitmap (ext2_filsys fs)
 {
-	return write_bitmaps(fs, 0, 1);
+	return write_bitmaps(fs, 0, 1, 0);
+}
+
+errcode_t ext2fs_write_exclude_bitmap (ext2_filsys fs)
+{
+	return write_bitmaps(fs, 0, 0, 1);
 }
 
 errcode_t ext2fs_read_bitmaps(ext2_filsys fs)
 {
-	if (fs->inode_map && fs->block_map)
+	if (fs->inode_map && fs->block_map && fs->exclude_map)
 		return 0;
 
-	return read_bitmaps(fs, !fs->inode_map, !fs->block_map);
+	return read_bitmaps(fs, !fs->inode_map, !fs->block_map, !fs->exclude_map);
 }
 
 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);
+	int do_exclude = fs->exclude_map && ext2fs_test_exclude_dirty(fs);
 
-	if (!do_inode && !do_block)
+	if (!do_inode && !do_block && !do_exclude)
 		return 0;
 
-	return write_bitmaps(fs, do_inode, do_block);
+	return write_bitmaps(fs, do_inode, do_block, do_exclude);
 }
diff -Nuarp a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
--- a/lib/ext2fs/swapfs.c	2009-08-23 05:39:05.000000000 +0300
+++ b/lib/ext2fs/swapfs.c	2010-07-26 09:47:16.640447884 +0300
@@ -71,6 +71,17 @@ void ext2fs_swap_super(struct ext2_super
 	sb->s_want_extra_isize = ext2fs_swab16(sb->s_want_extra_isize);
 	sb->s_flags = ext2fs_swab32(sb->s_flags);
 	sb->s_kbytes_written = ext2fs_swab64(sb->s_kbytes_written);
+	sb->s_snapshot_inum = ext2fs_swab32(sb->s_snapshot_inum);
+	sb->s_snapshot_id = ext2fs_swab32(s_snapshot_id);
+	sb->s_snapshot_r_blocks_count =
+		ext2fs_swab64(sb->s_snapshot_r_blocks_count);
+	sb->s_snapshot_list = ext2fs_swab32(sb->s_snapshot_list);
+	sb->s_snapshot_inum_old = ext2fs_swab32(sb->s_snapshot_inum_old);
+	sb->s_snapshot_id_old = ext2fs_swab32(sb->s_snapshot_id_old);
+	sb->s_snapshot_r_blocks_old =
+		ext2fs_swab32(sb->s_snapshot_r_blocks_old);
+	sb->s_snapshot_list_old = ext2fs_swab32(sb->s_snapshot_list_old);
+
 	for (i=0; i < 4; i++)
 		sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]);
 	for (i=0; i < 17; i++)
diff -Nuarp a/misc/chattr.c b/misc/chattr.c
--- a/misc/chattr.c	2009-08-13 04:39:57.000000000 +0300
+++ b/misc/chattr.c	2010-07-26 09:47:16.770445154 +0300
@@ -56,6 +56,8 @@
 
 static const char * program_name = "chattr";
 
+static int chsnap;
+
 static int add;
 static int rem;
 static int set;
@@ -81,8 +83,12 @@ static unsigned long sf;
 
 static void usage(void)
 {
+	fprintf(stderr, chsnap ?
+		_("Usage: %s [-+=let] snapshot files...\n") :
+		_("Usage: %s [-RVf] [-+=AacDdeijsSux] [-v version] files...\n"),
+		program_name);
 	fprintf(stderr,
-		_("Usage: %s [-RVf] [-+=AacDdeijsSu] [-v version] files...\n"),
+		_("Usage: %s -X [-+=Snt] snapshot files...\n"),
 		program_name);
 	exit(1);
 }
@@ -92,7 +98,7 @@ struct flags_char {
 	char 		optchar;
 };
 
-static const struct flags_char flags_array[] = {
+static const struct flags_char ext2_flags_array[] = {
 	{ EXT2_NOATIME_FL, 'A' },
 	{ EXT2_SYNC_FL, 'S' },
 	{ EXT2_DIRSYNC_FL, 'D' },
@@ -106,6 +112,25 @@ static const struct flags_char flags_arr
 	{ EXT2_UNRM_FL, 'u' },
 	{ EXT2_NOTAIL_FL, 't' },
 	{ EXT2_TOPDIR_FL, 'T' },
+	{ EXT4_SNAPFILE_FL, 'x' },
+	{ 0, 0 }
+};
+
+static const struct flags_char *flags_array = ext2_flags_array;
+
+/* Traditional snapshot flags */
+static struct flags_char snapshot_flags_array[] = {
+	{ NEXT3_SNAPFILE_LIST_FL, 'l' },
+	{ NEXT3_SNAPFILE_ENABLED_FL, 'e' },
+	{ NEXT3_SNAPFILE_TAGGED_FL, 't' },
+	{ 0, 0 }
+};
+
+/* Cool 'Snapshot' flags */
+static struct flags_char snapshot_X_flags_array[] = {
+	{ NEXT3_SNAPFILE_LIST_FL, 'S' },
+	{ NEXT3_SNAPFILE_ENABLED_FL, 'n' },
+	{ NEXT3_SNAPFILE_TAGGED_FL, 't' },
 	{ 0, 0 }
 };
 
@@ -131,6 +156,10 @@ static int decode_arg (int * i, int argc
 	{
 	case '-':
 		for (p = &argv[*i][1]; *p; p++) {
+			if (*p == 'X') {
+				flags_array = snapshot_X_flags_array;
+				continue;
+			}
 			if (*p == 'R') {
 				recursive = 1;
 				continue;
@@ -303,6 +332,11 @@ int main (int argc, char ** argv)
 #endif
 	if (argc && *argv)
 		program_name = *argv;
+	i = strlen(program_name);
+	if (i >= 6 && !strcmp(program_name + i - 6, "chsnap")) {
+		flags_array = snapshot_flags_array;
+		chsnap = 1;
+	}
 	i = 1;
 	while (i < argc && !end_arg) {
 		/* '--' arg should end option processing */
diff -Nuarp a/misc/dumpe2fs.c b/misc/dumpe2fs.c
--- a/misc/dumpe2fs.c	2009-08-23 05:44:35.000000000 +0300
+++ b/misc/dumpe2fs.c	2010-07-26 09:47:16.740445926 +0300
@@ -207,6 +207,13 @@ static void list_desc (ext2_filsys fs)
 		diff = fs->group_desc[i].bg_inode_bitmap - first_block;
 		if (diff >= 0)
 			printf(" (+%ld)", diff);
+		if (fs->exclude_blks && fs->exclude_blks[i]) {
+			fputs(_(", Exclude bitmap at "), stdout);
+			print_number(fs->exclude_blks[i]);
+			diff = fs->exclude_blks[i] - first_block;
+			if (diff >= 0 && diff <= fs->super->s_blocks_per_group)
+				printf(" (+%ld)", diff);
+		}
 		fputs(_("\n  Inode table at "), stdout);
 		print_range(fs->group_desc[i].bg_inode_table,
 			    fs->group_desc[i].bg_inode_table +
diff -Nuarp a/misc/e2image.c b/misc/e2image.c
--- a/misc/e2image.c	2009-08-23 05:44:35.000000000 +0300
+++ b/misc/e2image.c	2010-07-26 09:47:16.780449547 +0300
@@ -536,6 +536,7 @@ static void write_raw_image_file(ext2_fi
 			}
 		} else {
 			if ((inode.i_flags & EXT4_EXTENTS_FL) ||
+			    (inode.i_flags & EXT4_SNAPFILE_FL) ||
 			    inode.i_block[EXT2_IND_BLOCK] ||
 			    inode.i_block[EXT2_DIND_BLOCK] ||
 			    inode.i_block[EXT2_TIND_BLOCK]) {
diff -Nuarp a/misc/lsattr.c b/misc/lsattr.c
--- a/misc/lsattr.c	2009-08-13 04:39:57.000000000 +0300
+++ b/misc/lsattr.c	2010-07-26 09:47:16.770445154 +0300
@@ -70,7 +70,7 @@ static int generation_opt;
 
 static void usage(void)
 {
-	fprintf(stderr, _("Usage: %s [-RVadlv] [files...]\n"), program_name);
+	fprintf(stderr, _("Usage: %s [-XRVadlv] [files...]\n"), program_name);
 	exit(1);
 }
 
@@ -169,9 +169,16 @@ int main (int argc, char ** argv)
 #endif
 	if (argc && *argv)
 		program_name = *argv;
-	while ((c = getopt (argc, argv, "RVadlv")) != EOF)
+	i = strlen(program_name);
+	if (i >= 6 && !strcmp(program_name + i - 6, "lssnap"))
+		pf_options |= PFOPT_SNAPSHOT;
+
+	while ((c = getopt (argc, argv, "XRVadlv")) != EOF)
 		switch (c)
 		{
+			case 'X':
+				pf_options |= PFOPT_SNAPSHOT_X;
+				break;
 			case 'R':
 				recursive = 1;
 				break;
@@ -185,7 +192,7 @@ int main (int argc, char ** argv)
 				dirs_opt = 1;
 				break;
 			case 'l':
-				pf_options = PFOPT_LONG;
+				pf_options |= PFOPT_LONG;
 				break;
 			case 'v':
 				generation_opt = 1;
diff -Nuarp a/misc/mke2fs.c b/misc/mke2fs.c
--- a/misc/mke2fs.c	2009-08-23 05:44:35.000000000 +0300
+++ b/misc/mke2fs.c	2010-07-26 09:47:16.750515354 +0300
@@ -378,6 +378,10 @@ static void write_inode_tables(ext2_fils
 			/* 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);
+			if (fs->super->s_feature_compat &
+				EXT2_FEATURE_COMPAT_EXCLUDE_INODE)
+				/* zero the designated exclude bitmap block */
+				num++;
 		}
 		retval = ext2fs_zero_blocks(fs, blk, num, &blk, &num);
 		if (retval) {
@@ -832,6 +836,7 @@ static void parse_extended_opts(struct e
 static __u32 ok_features[3] = {
 	/* Compat */
 	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
+		EXT2_FEATURE_COMPAT_EXCLUDE_INODE |
 		EXT2_FEATURE_COMPAT_RESIZE_INODE |
 		EXT2_FEATURE_COMPAT_DIR_INDEX |
 		EXT2_FEATURE_COMPAT_EXT_ATTR,
@@ -843,6 +848,7 @@ static __u32 ok_features[3] = {
 		EXT4_FEATURE_INCOMPAT_FLEX_BG,
 	/* R/O compat */
 	EXT2_FEATURE_RO_COMPAT_LARGE_FILE|
+		EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\
 		EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
 		EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
 		EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
@@ -949,6 +955,9 @@ static char **parse_fs_type(const char *
 		ext_type = "ext2";
 	else if (!strcmp(program_name, "mke3fs"))
 		ext_type = "ext3";
+	else if (!strcmp(program_name, "mkfs.next3") ||
+		!strcmp(program_name, "mkn3fs"))
+		ext_type = "ext3";
 	else if (progname) {
 		ext_type = strrchr(progname, '/');
 		if (ext_type)
@@ -1163,6 +1172,17 @@ static void PRS(int argc, char *argv[])
 		if (!strcmp(program_name, "mkfs.ext3") ||
 		    !strcmp(program_name, "mke3fs"))
 			journal_size = -1;
+		
+		/* If called as mkfs.next3: */
+		if (!strcmp(program_name, "mkfs.next3") ||
+		    !strcmp(program_name, "mkn3fs")) {
+			/* 1. create a big journal */
+			journal_size = -NEXT3_MAX_COW_CREDITS;
+			/* 2. use system page size as block size */
+			blocksize = sys_page_size;
+			/* 3. create exclude inode */
+			edit_feature("exclude_inode", &fs_param.s_feature_compat);
+		}
 	}
 
 	while ((c = getopt (argc, argv,
@@ -2052,6 +2072,15 @@ int main (int argc, char *argv[])
 				exit(1);
 			}
 		}
+		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);
+			}
+		}
 	}
 
 	if (journal_device) {
diff -Nuarp a/misc/tune2fs.c b/misc/tune2fs.c
--- a/misc/tune2fs.c	2009-08-23 05:44:35.000000000 +0300
+++ b/misc/tune2fs.c	2010-07-26 09:47:16.760447608 +0300
@@ -118,6 +118,7 @@ static void usage(void)
 static __u32 ok_features[3] = {
 	/* Compat */
 	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
+		EXT2_FEATURE_COMPAT_EXCLUDE_INODE |
 		EXT2_FEATURE_COMPAT_DIR_INDEX,
 	/* Incompat */
 	EXT2_FEATURE_INCOMPAT_FILETYPE |
@@ -125,6 +126,7 @@ static __u32 ok_features[3] = {
 		EXT4_FEATURE_INCOMPAT_FLEX_BG,
 	/* R/O compat */
 	EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
+		EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\
 		EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
 		EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
 		EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
@@ -135,6 +137,7 @@ static __u32 ok_features[3] = {
 static __u32 clear_ok_features[3] = {
 	/* Compat */
 	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
+		EXT2_FEATURE_COMPAT_EXCLUDE_INODE |
 		EXT2_FEATURE_COMPAT_RESIZE_INODE |
 		EXT2_FEATURE_COMPAT_DIR_INDEX,
 	/* Incompat */
@@ -142,6 +145,7 @@ static __u32 clear_ok_features[3] = {
 		EXT4_FEATURE_INCOMPAT_FLEX_BG,
 	/* R/O compat */
 	EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
+		EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\
 		EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
 		EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
 		EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
@@ -249,57 +253,199 @@ 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;
 }
 
 /*
- * Remove the journal inode from the filesystem
+ * Remove a special inode from the filesystem:
+ * - resize inode, @nlink = 0
+ * - exclude inode, @nlink = 0
+ * - snapshot inodes, @nlink = 1 (snapshots directory)
  */
-static void remove_journal_inode(ext2_filsys fs)
+static void remove_special_inode(ext2_filsys fs, ext2_ino_t ino,
+		struct ext2_inode *inode, int nlink)
 {
-	struct ext2_inode	inode;
-	errcode_t		retval;
-	ino_t			ino = fs->super->s_journal_inum;
-
-	retval = ext2fs_read_inode(fs, ino,  &inode);
+	int retval = ext2fs_read_bitmaps(fs);
 	if (retval) {
 		com_err(program_name, retval,
-			_("while reading journal inode"));
+				_("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 (ino == EXT2_JOURNAL_INO) {
-		retval = ext2fs_read_bitmaps(fs);
+	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;
+}
+
+/*
+ * 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 bitmaps"));
+					_("while reading snapshot inode %u"),
+					ino);
 			exit(1);
 		}
-		retval = ext2fs_block_iterate(fs, ino,
-					      BLOCK_FLAG_READ_ONLY, NULL,
-					      release_blocks_proc, NULL);
+
+		remove_special_inode(fs, ino, &inode, 1);
+
+		retval = ext2fs_write_inode(fs, ino, &inode);
 		if (retval) {
 			com_err(program_name, retval,
-				_("while clearing journal inode"));
+					_("while writing snapshot inode %u"),
+					ino);
 			exit(1);
 		}
-		memset(&inode, 0, sizeof(inode));
-		ext2fs_mark_bb_dirty(fs);
-		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
-	} else
+
+		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);
+}
+
+/*
+ * 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;
+	struct ext2_group_desc *gd;
+	int i;
+
+	if (fs->super->s_feature_compat &
+	      NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD) {
+		/* Remove old exclude inode */
+		ino = EXT2_EXCLUDE_INO_OLD;
+		/* Clear old exclude inode flag */
+		fs->super->s_feature_compat &=
+			~NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD;
+		/* Reset old exclude/cow bitmap cache to zero */
+		for (i = 0; i < fs->group_desc_count; i++) {
+			gd = ext2fs_group_desc(fs, fs->group_desc, i);
+			gd->bg_exclude_bitmap_old = 0;
+			gd->bg_cow_bitmap_old = 0;
+		}
+	}
+
+	/* 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);
+	}
+}
+
+/*
+ * Remove the journal inode from the filesystem
+ */
+static void remove_journal_inode(ext2_filsys fs)
+{
+	struct ext2_inode	inode;
+	errcode_t		retval;
+	ino_t			ino = fs->super->s_journal_inum;
+
+	retval = ext2fs_read_inode(fs, ino,  &inode);
+	if (retval) {
+		com_err(program_name, retval,
+			_("while reading journal inode"));
+		exit(1);
+	}
+	if (ino == EXT2_JOURNAL_INO)
+		remove_special_inode(fs, ino, &inode, 0);
+	else
 		inode.i_flags &= ~EXT2_IMMUTABLE_FL;
 	retval = ext2fs_write_inode(fs, ino, &inode);
 	if (retval) {
@@ -326,6 +472,32 @@ static void update_mntopts(ext2_filsys f
 	ext2fs_mark_super_dirty(fs);
 }
 
+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;
+}
+
 /*
  * Update the feature set as provided by the user.
  */
@@ -333,6 +505,7 @@ static void update_feature_set(ext2_fils
 {
 	struct ext2_super_block *sb = fs->super;
 	__u32		old_features[3];
+	errcode_t	retval;
 	int		type_err;
 	unsigned int	mask_err;
 
@@ -342,11 +515,32 @@ static void update_feature_set(ext2_fils
 				 !((&sb->s_feature_compat)[(type)] & (mask)))
 #define FEATURE_CHANGED(type, mask) ((mask) & \
 		     (old_features[(type)] ^ (&sb->s_feature_compat)[(type)]))
+#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))
 
 	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;
 
+	/* disallow changing features when filesystem has snapshots */
+	if (sb->s_feature_ro_compat & 
+		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);
+		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;
+	}
+
 	if (e2p_edit_feature2(features, &sb->s_feature_compat,
 			      ok_features, clear_ok_features,
 			      &type_err, &mask_err)) {
@@ -402,6 +596,67 @@ static void update_feature_set(ext2_fils
 		sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
 	}
 
+	if (FEATURE_OFF_SAFE(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) {
+		remove_exclude_inode(fs);
+	}
+
+	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);
+		}
+	}
+
+	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)
+				sb->s_feature_compat &=
+					~EXT2_FEATURE_COMPAT_EXCLUDE_INODE;
+		}
+	}
+
+	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"));
+	}
+
 	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 -Nuarp a/misc/util.c b/misc/util.c
--- a/misc/util.c	2009-08-23 05:39:05.000000000 +0300
+++ b/misc/util.c	2010-07-26 09:47:16.760447608 +0300
@@ -218,6 +218,20 @@ void parse_journal_opts(const char *opts
 			journal_size = strtoul(arg, &p, 0);
 			if (*p)
 				journal_usage++;
+		} 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;
 		} else if (strcmp(token, "v1_superblock") == 0) {
 			journal_flags |= EXT2_MKJOURNAL_V1_SUPER;
 			continue;
@@ -231,6 +245,8 @@ 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"
+			"\tbig (Next3 big journal size)\n"
+			"\tbigger=<X times bigger than default size>\n"
 			"\tdevice=<journal device>\n\n"
 			"The journal size must be between "
 			"1024 and 10240000 filesystem blocks.\n\n"), stderr);
@@ -259,6 +275,20 @@ unsigned int figure_journal_size(int siz
 		return 0;
 	}
 
+	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);
+		}
+	}
+
 	if (size > 0) {
 		j_blocks = size * 1024 / (fs->blocksize	/ 1024);
 		if (j_blocks < 1024 || j_blocks > 10240000) {
diff -Nuarp a/resize/main.c b/resize/main.c
--- a/resize/main.c	2009-08-23 05:39:05.000000000 +0300
+++ b/resize/main.c	2010-07-26 09:47:16.790517166 +0300
@@ -437,6 +437,17 @@ int main (int argc, char ** argv)
 	if (mount_flags & EXT2_MF_MOUNTED) {
 		retval = online_resize_fs(fs, mtpt, &new_size, flags);
 	} else {
+		/* 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);
+		}
 		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 -Nuarp a/resize/resize2fs.c b/resize/resize2fs.c
--- a/resize/resize2fs.c	2009-08-23 05:44:35.000000000 +0300
+++ b/resize/resize2fs.c	2010-07-26 09:47:16.790517166 +0300
@@ -48,6 +48,7 @@ static errcode_t inode_scan_and_fix(ext2
 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);
+static errcode_t fix_exclude_inode(ext2_filsys fs);
 static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs);
 static errcode_t fix_sb_journal_backup(ext2_filsys fs);
 
@@ -153,6 +154,10 @@ errcode_t resize_fs(ext2_filsys fs, blk_
 	if (retval)
 		goto errout;
 
+	retval = fix_exclude_inode(rfs->new_fs);
+	if (retval)
+		goto errout;
+
 	retval = fix_sb_journal_backup(rfs->new_fs);
 	if (retval)
 		goto errout;
@@ -1770,6 +1775,25 @@ errout:
 }
 
 /*
+ * 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);
+}
+
+/*
  * Finally, recalculate the summary information
  */
 static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs)
diff -Nuarp a/version.h b/version.h
--- a/version.h	2009-08-23 06:04:33.000000000 +0300
+++ b/version.h	2010-07-26 09:47:16.800445322 +0300
@@ -7,5 +7,5 @@
  * redistributed under the GNU Public License.
  */
 
-#define E2FSPROGS_VERSION "1.41.9"
-#define E2FSPROGS_DATE "22-Aug-2009"
+#define E2FSPROGS_VERSION "1.41.9-next3-1.0.12"
+#define E2FSPROGS_DATE "25-Jul-2010"