Skip to contentSkip to conversation

e2fsprogs-mmp.patch

PATCH In review Kalpak Shah 1 participant 1 message Oct 6, 2008

lore
Share & tools
lore
#0 lore raw

PATCH 12/15 e2fsprogs-mmp.patch

Add multi-mount protection support to libext2fs (INCOMPAT_MMP feature).

This allows mke2fs, e2fsck, and others to detect if the filesystem is mounted on a remote node (on SAN disks) and avoid corrupting the filesystem. For e2fsprogs this only means that it check the MMP block to see if the filesystem is in use, and mark the filesystem busy while e2fsck is running on the system.

There is no requirement that e2fsck updates the MMP block in any regular interval, but e2fsck does this occasionally to provide additional information to the sysadmin in case of conflict.

Add multi-mount protection support to libext2fs (INCOMPAT_MMP feature).

This allows mke2fs, e2fsck, and others to detect if the filesystem is mounted on a remote node (on SAN disks) and avoid corrupting the filesystem. For e2fsprogs this only means that it check the MMP block to see if the filesystem is in use, and mark the filesystem busy while e2fsck is running on the system.

There is no requirement that e2fsck updates the MMP block in any regular interval, but e2fsck does this occasionally to provide additional information to the sysadmin in case of conflict.

lib/e2p/feature.c +3 -1
Index: e2fsprogs-1.41.1/lib/e2p/feature.c
===================================================================
@@ -72,7 +72,9 @@ static struct feature feature_list[] = {
 	{	E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_64BIT,
 			"64bit" },
 	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG,
-                        "flex_bg"},
+			"flex_bg"},
+	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP,
+			"mmp" },
 	{	0, 0, 0 },
 };
 
lib/ext2fs/ext2_fs.h +25 -16

65 lines not on this page · raw message

lib/ext2fs/ext2fs.h +35 -2
Index: e2fsprogs-1.41.1/lib/ext2fs/ext2fs.h
===================================================================
@@ -172,6 +172,8 @@ 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_SKIP_MMP		0x20000
+
 
 /*
  * Special flag in the ext2 inode i_flag field that means that this is
@@ -186,6 +188,15 @@ typedef struct ext2_file *ext2_file_t;
  */
 #define EXT2_MKJOURNAL_V1_SUPER	0x0000001
 
+/*
+ * The timestamp in the MMP structure will be updated by e2fsck at some
+ * arbitary intervals (start of passes, after every EXT2_MMP_INODE_INTERVAL
+ * inodes in pass1 and pass1b).  There is no guarantee that e2fsck is updating
+ * the MMP block in a timely manner, and the updates it does are purely for
+ * the convenience of the sysadmin and not for automatic validation.
+ */
+#define EXT2_MMP_INODE_INTERVAL 20000
+
 struct struct_ext2_filsys {
 	errcode_t			magic;
 	io_channel			io;
@@ -231,6 +242,16 @@ struct struct_ext2_filsys {
 	io_channel			image_io;
 
 	/*
+	 * Buffers for Multiple mount protection(MMP) block.
+	 */
+	char *mmp_buf;
+	char *mmp_cmp;
+	/*
+	 * Time at which e2fsck last updated the MMP block.
+	 */
+	long mmp_last_written;
+
+	/*
 	 * More callback functions
 	 */
 	errcode_t (*get_alloc_block)(ext2_filsys fs, blk64_t goal,
@@ -528,14 +549,16 @@ typedef struct ext2_icount *ext2_icount_
 					 EXT2_FEATURE_INCOMPAT_META_BG|\
 					 EXT3_FEATURE_INCOMPAT_RECOVER|\
 					 EXT3_FEATURE_INCOMPAT_EXTENTS|\
-					 EXT4_FEATURE_INCOMPAT_FLEX_BG)
+					 EXT4_FEATURE_INCOMPAT_FLEX_BG|\
+					 EXT4_FEATURE_INCOMPAT_MMP)
 #else
 #define EXT2_LIB_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE|\
 					 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
 					 EXT2_FEATURE_INCOMPAT_META_BG|\
 					 EXT3_FEATURE_INCOMPAT_RECOVER|\
 					 EXT3_FEATURE_INCOMPAT_EXTENTS|\
-					 EXT4_FEATURE_INCOMPAT_FLEX_BG)
+					 EXT4_FEATURE_INCOMPAT_FLEX_BG|\
+					 EXT4_FEATURE_INCOMPAT_MMP)
 #endif
 #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
 					 EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
@@ -1100,6 +1123,15 @@ errcode_t ext2fs_link(ext2_filsys fs, ex
 errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name,
 			ext2_ino_t ino, int flags);
 
+/* mmp.c */
+errcode_t ext2fs_mmp_read(ext2_filsys fs, blk_t mmp_blk, char *buf);
+errcode_t ext2fs_mmp_write(ext2_filsys fs, blk_t mmp_blk, char *buf);
+errcode_t ext2fs_mmp_init(ext2_filsys fs);
+errcode_t ext2fs_mmp_start(ext2_filsys fs);
+errcode_t ext2fs_mmp_update(ext2_filsys fs);
+errcode_t ext2fs_mmp_stop(ext2_filsys fs);
+long int ext2fs_mmp_new_seq();
+
 /* read_bb.c */
 extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs,
 				      ext2_badblocks_list *bb_list);
@@ -1134,6 +1166,7 @@ extern void ext2fs_swap_inode_full(ext2_
 				   int bufsize);
 extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t,
 			      struct ext2_inode *f, int hostorder);
+extern void ext2fs_swap_mmp(struct mmp_struct *mmp);
 
 /* valid_blk.c */
 extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
misc/tune2fs.c +192 -46

505 lines not on this page · raw message

e2fsck/pass1.c +13 -0
Index: e2fsprogs-1.41.1/e2fsck/pass1.c
===================================================================
@@ -814,7 +814,20 @@ void e2fsck_pass1(e2fsck_t ctx)
 	    (fs->super->s_mtime < fs->super->s_inodes_count))
 		busted_fs_time = 1;
 
+	if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) &&
+	    !(fs->super->s_mmp_block < fs->super->s_first_data_block ||
+	      fs->super->s_mmp_block >= fs->super->s_blocks_count))
+		ext2fs_mark_block_bitmap(ctx->block_found_map,
+					 fs->super->s_mmp_block);
+
 	while (1) {
+		if (ino % EXT2_MMP_INODE_INTERVAL == 0) {
+			errcode_t error;
+
+			error = e2fsck_mmp_update(fs);
+			if (error)
+				fatal_error(ctx, 0);
+		}
 		old_op = ehandler_operation(_("getting next inode from scan"));
 		pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
 							  inode, inode_size);
e2fsck/unix.c +18 -0
Index: e2fsprogs-1.41.1/e2fsck/unix.c
===================================================================
@@ -1124,6 +1124,24 @@ restart:
 			       "to do a read-only\n"
 			       "check of the device.\n"));
 #endif
+		else if (retval == EXT2_ET_MMP_BAD_BLOCK) {
+			if (fix_problem(ctx, PR_0_MMP_INVALID_BLK, &pctx)) {
+				fs->super->s_mmp_block = 0;
+				ext2fs_mark_super_dirty(fs);
+			}
+		}
+		else if (retval == EXT2_ET_MMP_FAILED) {
+			dump_mmp_msg((struct mmp_struct *)fs->mmp_buf,
+				     _("Device active on another node"));
+		}
+		else if (retval == EXT2_ET_MMP_FSCK_ON) {
+			dump_mmp_msg((struct mmp_struct *)fs->mmp_buf,
+				     _("It seems that e2fsck is running on the "
+				     "filesystem.\nIf you are sure that e2fsck "
+				     "is not running then use "
+				     "'tune2fs -O ^mmp {device}'\n"
+				     "followed by 'tune2fs -O mmp {device}'"));
+		}
 		else
 			fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
 		fatal_error(ctx, 0);
e2fsck/problem.c +5 -0
Index: e2fsprogs-1.41.1/e2fsck/problem.c
===================================================================
@@ -394,6 +394,11 @@ static struct e2fsck_problem problem_tab
 	  PROMPT_NONE, 0 },
 
 
+	/* Superblock has invalid MMP block. */
+	{ PR_0_MMP_INVALID_BLK,
+	  N_("@S has invalid MMP block.  "),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
 	/* Pass 1 errors */
 
 	/* Pass 1: Checking inodes, blocks, and sizes */
e2fsck/problem.h +3 -0
Index: e2fsprogs-1.41.1/e2fsck/problem.h
===================================================================
@@ -225,6 +225,9 @@ struct problem_context {
 #define PR_0_CLEAR_EXTRA_ISIZE			0x00003C
 
 
+/* Superblock has invalid MMP block. */
+#define PR_0_MMP_INVALID_BLK			0x00003A
+
 /*
  * Pass 1 errors
  */
lib/ext2fs/swapfs.c +10 -0
Index: e2fsprogs-1.41.1/lib/ext2fs/swapfs.c
===================================================================
@@ -70,6 +70,8 @@ void ext2fs_swap_super(struct ext2_super
 	sb->s_min_extra_isize = ext2fs_swab16(sb->s_min_extra_isize);
 	sb->s_want_extra_isize = ext2fs_swab16(sb->s_want_extra_isize);
 	sb->s_flags = ext2fs_swab32(sb->s_flags);
+	sb->s_mmp_update_interval = ext2fs_swab16(sb->s_mmp_update_interval);
+	sb->s_mmp_block = ext2fs_swab64(sb->s_mmp_block);
 	for (i=0; i < 4; i++)
 		sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]);
 	for (i=0; i < 17; i++)
@@ -271,4 +273,12 @@ void ext2fs_swap_inode(ext2_filsys fs, s
 				sizeof(struct ext2_inode));
 }
 
+void ext2fs_swap_mmp(struct mmp_struct *mmp)
+{
+	mmp->mmp_magic = ext2fs_swab32(mmp->mmp_magic);
+	mmp->mmp_seq = ext2fs_swab32(mmp->mmp_seq);
+	mmp->mmp_time = ext2fs_swab64(mmp->mmp_time);
+	mmp->mmp_check_interval = ext2fs_swab16(mmp->mmp_check_interval);
+}
+
 #endif
lib/ext2fs/openfs.c +12 -0
Index: e2fsprogs-1.41.1/lib/ext2fs/openfs.c
===================================================================
@@ -22,6 +22,9 @@
 #if HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
 
 #include "ext2_fs.h"
 
@@ -76,6 +79,7 @@ errcode_t ext2fs_open(const char *name, 
  * 	EXT2_FLAG_FORCE - Open the filesystem even if some of the
  *				features aren't supported.
  *	EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device
+ *	EXT2_FLAG_SKIP_MMP - Open without multi-mount protection check.
  */
 errcode_t ext2fs_open2(const char *name, const char *io_options,
 		       int flags, int superblock,
@@ -325,6 +329,14 @@ errcode_t ext2fs_open2(const char *name,
 
 	fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR;
 	*ret_fs = fs;
+
+	if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) &&
+	    (flags & EXT2_FLAG_RW) && !(flags & EXT2_FLAG_SKIP_MMP)) {
+		retval = ext2fs_mmp_start(fs);
+		if (retval)
+			goto cleanup;
+	}
+
 	return 0;
 cleanup:
 	if (flags & EXT2_FLAG_NOFREE_ON_ERROR)
lib/ext2fs/ext2_err.et.in +18 -0
Index: e2fsprogs-1.41.1/lib/ext2fs/ext2_err.et.in
===================================================================
@@ -440,4 +440,22 @@ ec	EXT2_ET_EA_NAME_NOT_FOUND,
 ec	EXT2_ET_EA_NAME_EXISTS,
 	"Extended attribute name already exists"
 
+ec	EXT2_ET_MMP_MAGIC_INVALID,
+	"MMP: invalid magic number in header"
+
+ec	EXT2_ET_MMP_FAILED,
+	"MMP: device already active on another node"
+
+ec	EXT2_ET_MMP_FSCK_ON,
+	"MMP: appears fsck currently being run on the filesystem"
+
+ec	EXT2_ET_MMP_BAD_BLOCK,
+	"MMP: block number beyond filesystem range"
+
+ec	EXT2_ET_MMP_UNKNOWN_SEQ,
+	"MMP: seems to be undergoing an unknown operation"
+
+ec	EXT2_ET_MMP_CHANGE_ABORT,
+	"MMP: sequence changed - filesystem may be in use elsewhere"
+
 	end
lib/ext2fs/closefs.c +9 -0
Index: e2fsprogs-1.41.1/lib/ext2fs/closefs.c
===================================================================
@@ -458,11 +458,20 @@ errcode_t ext2fs_close(ext2_filsys fs)
 		if (retval)
 			return retval;
 	}
+
 	if (fs->write_bitmaps) {
 		retval = fs->write_bitmaps(fs);
 		if (retval)
 			return retval;
 	}
+
+	if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) &&
+	    (fs->flags & EXT2_FLAG_RW) && !(fs->flags & EXT2_FLAG_SKIP_MMP)) {
+		retval = ext2fs_mmp_stop(fs);
+		if (retval)
+			return retval;
+	}
+
 	ext2fs_free(fs);
 	return 0;
 }
e2fsck/e2fsck.c +4 -0
Index: e2fsprogs-1.41.1/e2fsck/e2fsck.c
===================================================================
@@ -201,6 +201,7 @@ int e2fsck_run(e2fsck_t ctx)
 {
 	int	i;
 	pass_t	e2fsck_pass;
+	int error;
 
 #ifdef HAVE_SETJMP_H
 	if (setjmp(ctx->abort_loc)) {
@@ -213,6 +214,9 @@ int e2fsck_run(e2fsck_t ctx)
 	for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
 		if (ctx->flags & E2F_FLAG_RUN_RETURN)
 			break;
+		error = e2fsck_mmp_update(ctx->fs);
+		if (error)
+			fatal_error(ctx, 0);
 		e2fsck_pass(ctx);
 		if (ctx->progress)
 			(void) (ctx->progress)(ctx, 0, 0, 0);
e2fsck/e2fsck.h +2 -0
Index: e2fsprogs-1.41.1/e2fsck/e2fsck.h
===================================================================
@@ -528,6 +528,8 @@ extern void mtrace_print(char *mesg);
 extern blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
 			   const char *name, io_manager manager);
 extern int ext2_file_type(unsigned int mode);
+void dump_mmp_msg(struct mmp_struct *mmp, const char *msg);
+errcode_t e2fsck_mmp_update(ext2_filsys fs);
 
 /* unix.c */
 extern void e2fsck_clear_progbar(e2fsck_t ctx);
lib/ext2fs/Makefile.in +4 -0
Index: e2fsprogs-1.41.1/lib/ext2fs/Makefile.in
===================================================================
@@ -61,6 +61,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_O
 	lookup.o \
 	mkdir.o \
 	mkjournal.o \
+	mmp.o \
 	native.o \
 	newdir.o \
 	openfs.o \
@@ -124,6 +125,7 @@ SRCS= ext2_err.c \
 	$(srcdir)/lookup.c \
 	$(srcdir)/mkdir.c \
 	$(srcdir)/mkjournal.c \
+	$(srcdir)/mmp.c	\
 	$(srcdir)/namei.c \
 	$(srcdir)/native.c \
 	$(srcdir)/newdir.c \
@@ -622,6 +624,8 @@ mkjournal.o: $(srcdir)/mkjournal.c $(src
  $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
  $(srcdir)/bitops.h $(srcdir)/jfs_user.h $(srcdir)/kernel-jbd.h \
  $(srcdir)/jfs_compat.h $(srcdir)/kernel-list.h
+mmp.o: $(srcdir)/ext2_fs.h $(srcdir)/ext2fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h
 namei.o: $(srcdir)/namei.c $(srcdir)/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
  $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
lib/ext2fs/mmp.c new file +335 -0

335 lines not on this page · raw message

e2fsck/pass1b.c +7 -0
Index: e2fsprogs-1.41.1/e2fsck/pass1b.c
===================================================================
@@ -272,6 +272,13 @@ static void pass1b(e2fsck_t ctx, char *b
 	pb.pctx = &pctx;
 	pctx.str = "pass1b";
 	while (1) {
+		if (ino % EXT2_MMP_INODE_INTERVAL == 0) {
+			errcode_t error;
+
+			error = e2fsck_mmp_update(fs);
+			if (error)
+				fatal_error(ctx, 0);
+		}
 		pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
 		if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
 			continue;
misc/tune2fs.8.in +8 -0
Index: e2fsprogs-1.41.1/misc/tune2fs.8.in
===================================================================
@@ -439,6 +439,11 @@ Setting the filesystem feature is equiva
 .B \-j
 option.
 .TP
+.B mmp
+Enable or disable multiple mount protection(MMP) feature. MMP helps to protect
+the filesystem from being multiply mounted and is useful in shared storage
+environment.
+.TP
 .B sparse_super
 Limit the number of backup superblocks to save space on large filesystems.
 .TP
@@ -481,6 +486,9 @@ and
 .BR flex_bg
 features are only supported by the ext4 filesystem.
 .TP
+.BI \-p " mmp_check_interval"
+Set the desired MMP check interval in seconds. It is 5 seconds by default.
+.TP
 .BI \-r " reserved-blocks-count"
 Set the number of reserved filesystem blocks.
 .TP
misc/mke2fs.c +15 -2
Index: e2fsprogs-1.41.1/misc/mke2fs.c
===================================================================
@@ -840,7 +840,8 @@ static __u32 ok_features[3] = {
 		EXT3_FEATURE_INCOMPAT_EXTENTS|
 		EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|
 		EXT2_FEATURE_INCOMPAT_META_BG|
-		EXT4_FEATURE_INCOMPAT_FLEX_BG,
+		EXT4_FEATURE_INCOMPAT_FLEX_BG|
+		EXT4_FEATURE_INCOMPAT_MMP,
 	/* R/O compat */
 	EXT2_FEATURE_RO_COMPAT_LARGE_FILE|
 		EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
@@ -2068,8 +2069,20 @@ int main (int argc, char *argv[])
 	}
 no_journal:
 
-	if (!super_only)
+	if (!super_only) {
 		ext2fs_set_gdt_csum(fs);
+		if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) {
+			retval = ext2fs_mmp_init(fs);
+			if (retval) {
+				fprintf(stderr, _("\nError while enabling "
+					"multiple mount protection feature."));
+				exit(1);
+			}
+			printf(_("Multiple mount protection has been enabled "
+				 "with update interval %d seconds.\n"),
+				 fs->super->s_mmp_update_interval);
+		}
+	}
 	if (!quiet)
 		printf(_("Writing superblocks and "
 		       "filesystem accounting information: "));
e2fsck/util.c +22 -0
Index: e2fsprogs-1.41.1/e2fsck/util.c
===================================================================
@@ -593,3 +593,25 @@ errcode_t e2fsck_zero_blocks(ext2_filsys
 	}
 	return 0;
 }
+
+void dump_mmp_msg(struct mmp_struct *mmp, const char *msg)
+{
+	printf("MMP check failed: %s\n", msg);
+	printf("MMP failure info: last update time: %llu node: %s device: %s\n",
+	       (long long)mmp->mmp_time, mmp->mmp_nodename, mmp->mmp_bdevname);
+}
+
+errcode_t e2fsck_mmp_update(ext2_filsys fs)
+{
+	errcode_t retval;
+
+	retval = ext2fs_mmp_update(fs);
+	if (retval == EXT2_ET_MMP_CHANGE_ABORT)
+		dump_mmp_msg((struct mmp_struct *)fs->mmp_cmp,
+			     _("\n UNEXPECTED INCONSISTENCY: "
+			     "Unexpected MMP structure read from disk.\n"
+			     "It seems the filesystem is being modified while "
+			     "fsck is running.\n"));
+
+	return retval;
+}
lib/ext2fs/bitops.h +6 -0
Index: e2fsprogs-1.41.1/lib/ext2fs/bitops.h
===================================================================
@@ -357,6 +357,12 @@ _INLINE_ __u32 ext2fs_swab32(__u32 val)
 		((val<<8)&0xFF0000) | (val<<24));
 }
 
+_INLINE_ __u64 ext2fs_swab64(__u64 val)
+{
+	return (ext2fs_swab32(val >> 32) |
+		(((__u64)ext2fs_swab32(val & 0xFFFFFFFFUL)) << 32));
+}
+
 #endif /* !_EXT2_HAVE_ASM_SWAB */
 
 #if !defined(_EXT2_HAVE_ASM_FINDBIT_)
debugfs/set_fields.c +1 -1
Index: e2fsprogs-1.41.1/debugfs/set_fields.c
===================================================================
@@ -129,7 +129,7 @@ static struct field_set_info super_field
 	{ "flags", &set_sb.s_flags, 4, parse_uint },
 	{ "raid_stride", &set_sb.s_raid_stride, 2, parse_uint },
 	{ "min_extra_isize", &set_sb.s_min_extra_isize, 4, parse_uint },
-	{ "mmp_interval", &set_sb.s_mmp_interval, 2, parse_uint },
+	{ "mmp_update_interval", &set_sb.s_mmp_update_interval, 2, parse_uint },
 	{ "mmp_block", &set_sb.s_mmp_block, 8, parse_uint },
 	{ "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 },
lib/e2p/ls.c +6 -0
Index: e2fsprogs-1.41.1/lib/e2p/ls.c
===================================================================
@@ -310,6 +310,12 @@ void list_super2(struct ext2_super_block
 			fprintf(f, "type %u\n", sb->s_jnl_backup_type);
 		}
 	}
+	if (sb->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) {
+		fprintf(f, "MMP block number:         %llu\n",
+			(long long)sb->s_mmp_block);
+		fprintf(f, "MMP update interval:      %u\n",
+			sb->s_mmp_update_interval);
+	}
 }
 
 void list_super (struct ext2_super_block * s)
lib/ext2fs/freefs.c +5 -0
Index: e2fsprogs-1.41.1/lib/ext2fs/freefs.c
===================================================================
@@ -53,6 +53,11 @@ void ext2fs_free(ext2_filsys fs)
 	if (fs->icache)
 		ext2fs_free_inode_cache(fs->icache);
 
+	if (fs->mmp_buf)
+		ext2fs_free_mem(&fs->mmp_buf);
+	if (fs->mmp_cmp)
+		ext2fs_free_mem(&fs->mmp_cmp);
+
 	fs->magic = 0;
 
 	ext2fs_free_mem(&fs);