[10/10] misc: add e2mmpstatus utility via debugfs

Message ID 1525235166-6448-11-git-send-email-adilger@dilger.ca
State Accepted, archived
Headers show
Series
  • test cleanups and minor improvements
Related show

Commit Message

Andreas Dilger May 2, 2018, 4:26 a.m.
From: Shuichi Ihara <sihara@ddn.com>

e2mmpstatus is a Multi-Mount Protection (MMP) helper utility to read
an MMP block to see if it is being updated.  It can also output the
latest update time, nodename, and device from the MMP block.

This is useful for HA and other maintenance scripts to determine if
the filesystem is in use on another node, and which node it is.

Signed-off-by: Shuichi Ihara <sihara@ddn.com>
Signed-off-by: Li Xi <lixi@ddn.com>
Signed-off-by: Wang Shilong <wshilong@ddn.com>

Moved e2mmpstatus checking/dumping code to be part of dumpe2fs rather
than a standalone program, using the "-m" option to check MMP status,
and "-i" to dump info.  If dumpe2fs is called as "e2mmpstatus" (and
also "mmpstatus" for compatibility reasons), assume "-m" is specified.

Re-use the existing MMP block handing routines (with some changes) to
check and dump the MMP block, rather than adding duplicate versions.

Modify dumpe2fs to exit with a non-zero error code if there is an
error while reading the filesystem metadata or MMP block, or if
"-m" is used with the "mmp" feature and is in use by another node.

Add a configure check for gethostname() rather than depending on
_BSD_SOURCE or _XOPEN_SOURCE to be set.

Update the f_mmp, m_mmp, m_mmp_bad_csum, and m_mmp_bad_magic tests
to use e2mmpstatus to check and dump the MMP state before and after
e2fsck is run to verify that the tool is working correctly.

Signed-off-by: Andreas Dilger <adilger@dilger.ca>
---
 .gitignore                   |   2 +
 configure                    |   2 +-
 configure.ac                 |   1 +
 e2fsck/e2fsck.h              |   2 +-
 e2fsck/problem.c             |   2 +-
 e2fsck/unix.c                |   3 +-
 e2fsck/util.c                |  22 +++--
 e2fsprogs.spec.in            |   2 +
 lib/config.h.in              |  15 +++-
 lib/ext2fs/ext2_err.et.in    |   4 +-
 lib/ext2fs/mmp.c             |  12 ++-
 misc/Makefile.in             |  11 ++-
 misc/dumpe2fs.8.in           |  22 ++++-
 misc/dumpe2fs.c              | 202 ++++++++++++++++++++++++++++++++++---------
 misc/e2mmpstatus.8.in        |  59 +++++++++++++
 tests/f_mmp/script           |   7 ++
 tests/m_mmp/expect.1         |   8 ++
 tests/m_mmp_bad_csum/expect  |  16 +++-
 tests/m_mmp_bad_csum/script  |   9 +-
 tests/m_mmp_bad_magic/expect |  13 +++
 tests/m_mmp_bad_magic/script |   9 +-
 tests/test_config            |   1 +
 22 files changed, 360 insertions(+), 64 deletions(-)
 create mode 100644 misc/e2mmpstatus.8.in

Comments

Lukas Czerner May 4, 2018, 10:06 a.m. | #1
On Tue, May 01, 2018 at 10:26:06PM -0600, Andreas Dilger wrote:
> From: Shuichi Ihara <sihara@ddn.com>
> 
> e2mmpstatus is a Multi-Mount Protection (MMP) helper utility to read
> an MMP block to see if it is being updated.  It can also output the
> latest update time, nodename, and device from the MMP block.
> 
> This is useful for HA and other maintenance scripts to determine if
> the filesystem is in use on another node, and which node it is.
> 
> Signed-off-by: Shuichi Ihara <sihara@ddn.com>
> Signed-off-by: Li Xi <lixi@ddn.com>
> Signed-off-by: Wang Shilong <wshilong@ddn.com>
> 
> Moved e2mmpstatus checking/dumping code to be part of dumpe2fs rather
> than a standalone program, using the "-m" option to check MMP status,
> and "-i" to dump info.  If dumpe2fs is called as "e2mmpstatus" (and
> also "mmpstatus" for compatibility reasons), assume "-m" is specified.

Hmm, why do we need an alias to dumpe2fs options ? For what
compatibility you need mmpstatus as well ? I do not like it, see below.

> 
> Re-use the existing MMP block handing routines (with some changes) to
> check and dump the MMP block, rather than adding duplicate versions.
> 
> Modify dumpe2fs to exit with a non-zero error code if there is an
> error while reading the filesystem metadata or MMP block, or if
> "-m" is used with the "mmp" feature and is in use by another node.
> 
> Add a configure check for gethostname() rather than depending on
> _BSD_SOURCE or _XOPEN_SOURCE to be set.
> 
> Update the f_mmp, m_mmp, m_mmp_bad_csum, and m_mmp_bad_magic tests
> to use e2mmpstatus to check and dump the MMP state before and after
> e2fsck is run to verify that the tool is working correctly.
> 
> Signed-off-by: Andreas Dilger <adilger@dilger.ca>
> ---
>  .gitignore                   |   2 +
>  configure                    |   2 +-
>  configure.ac                 |   1 +
>  e2fsck/e2fsck.h              |   2 +-
>  e2fsck/problem.c             |   2 +-
>  e2fsck/unix.c                |   3 +-
>  e2fsck/util.c                |  22 +++--
>  e2fsprogs.spec.in            |   2 +
>  lib/config.h.in              |  15 +++-
>  lib/ext2fs/ext2_err.et.in    |   4 +-
>  lib/ext2fs/mmp.c             |  12 ++-
>  misc/Makefile.in             |  11 ++-
>  misc/dumpe2fs.8.in           |  22 ++++-
>  misc/dumpe2fs.c              | 202 ++++++++++++++++++++++++++++++++++---------
>  misc/e2mmpstatus.8.in        |  59 +++++++++++++
>  tests/f_mmp/script           |   7 ++
>  tests/m_mmp/expect.1         |   8 ++
>  tests/m_mmp_bad_csum/expect  |  16 +++-
>  tests/m_mmp_bad_csum/script  |   9 +-
>  tests/m_mmp_bad_magic/expect |  13 +++
>  tests/m_mmp_bad_magic/script |   9 +-
>  tests/test_config            |   1 +
>  22 files changed, 360 insertions(+), 64 deletions(-)
>  create mode 100644 misc/e2mmpstatus.8.in
> 
> diff --git a/.gitignore b/.gitignore
> index ac5c2c1..3352ccc 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -170,6 +170,8 @@ misc/e2image
>  misc/e2image.8
>  misc/e2initrd_helper
>  misc/e2label.8
> +misc/e2mmpstatus
> +misc/e2mmpstatus.8
>  misc/e2undo
>  misc/e2undo.8
>  misc/e4crypt
> diff --git a/configure b/configure
> index b2701d2..c7853d1 100755
> --- a/configure
> +++ b/configure
> @@ -13097,7 +13097,7 @@ fi
>  if test -n "$DLOPEN_LIB" ; then
>     ac_cv_func_dlopen=yes
>  fi
> -for ac_func in  	__secure_getenv 	add_key 	backtrace 	blkid_probe_get_topology 	blkid_probe_enable_partitions 	chflags 	dlopen 	fadvise64 	fallocate 	fallocate64 	fchown 	fcntl 	fdatasync 	fstat64 	fsync 	ftruncate64 	futimes 	getcwd 	getdtablesize 	getmntinfo 	getpwuid_r 	getrlimit 	getrusage 	jrand48 	keyctl 	llistxattr 	llseek 	lseek64 	mallinfo 	mbstowcs 	memalign 	mempcpy 	mmap 	msync 	nanosleep 	open64 	pathconf 	posix_fadvise 	posix_fadvise64 	posix_memalign 	prctl 	pread 	pwrite 	pread64 	pwrite64 	secure_getenv 	setmntent 	setresgid 	setresuid 	snprintf 	srandom 	stpcpy 	strcasecmp 	strdup 	strnlen 	strptime 	strtoull 	sync_file_range 	sysconf 	usleep 	utime 	utimes 	valloc
> +for ac_func in  	__secure_getenv 	add_key 	backtrace 	blkid_probe_get_topology 	blkid_probe_enable_partitions 	chflags 	dlopen 	fadvise64 	fallocate 	fallocate64 	fchown 	fcntl 	fdatasync 	fstat64 	fsync 	ftruncate64 	futimes 	getcwd 	getdtablesize 	gethostname 	getmntinfo 	getpwuid_r 	getrlimit 	getrusage 	jrand48 	keyctl 	llistxattr 	llseek 	lseek64 	mallinfo 	mbstowcs 	memalign 	mempcpy 	mmap 	msync 	nanosleep 	open64 	pathconf 	posix_fadvise 	posix_fadvise64 	posix_memalign 	prctl 	pread 	pwrite 	pread64 	pwrite64 	secure_getenv 	setmntent 	setresgid 	setresuid 	snprintf 	srandom 	stpcpy 	strcasecmp 	strdup 	strnlen 	strptime 	strtoull 	sync_file_range 	sysconf 	usleep 	utime 	utimes 	valloc
>  do :
>    as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
>  ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
> diff --git a/configure.ac b/configure.ac
> index 7392959..5e837c9 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -1124,6 +1124,7 @@ AC_CHECK_FUNCS(m4_flatten([
>  	futimes
>  	getcwd
>  	getdtablesize
> +	gethostname
>  	getmntinfo
>  	getpwuid_r
>  	getrlimit
> diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
> index 5269650..d151bfd 100644
> --- a/e2fsck/e2fsck.h
> +++ b/e2fsck/e2fsck.h
> @@ -634,7 +634,7 @@ extern blk64_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
>  			   const char *name, io_manager manager);
>  extern int ext2_file_type(unsigned int mode);
>  extern int write_all(int fd, char *buf, size_t count);
> -void dump_mmp_msg(struct mmp_struct *mmp, const char *msg);
> +void dump_mmp_msg(struct mmp_struct *mmp, const char *fmt, ...);
>  errcode_t e2fsck_mmp_update(ext2_filsys fs);
>  
>  extern void e2fsck_set_bitmap_type(ext2_filsys fs,
> diff --git a/e2fsck/problem.c b/e2fsck/problem.c
> index ff289b4..be79fdb 100644
> --- a/e2fsck/problem.c
> +++ b/e2fsck/problem.c
> @@ -450,7 +450,7 @@ static struct e2fsck_problem problem_table[] = {
>  
>  	/* Superblock MMP block checksum does not match MMP block. */
>  	{ PR_0_MMP_CSUM_INVALID,
> -	  N_("@S MMP @b checksum does not match MMP @b.  "),
> +	  N_("@S MMP @b checksum does not match.  "),
>  	  PROMPT_FIX, PR_PREEN_OK | PR_NO_OK},
>  
>  	/* Superblock 64bit filesystem needs extents to access the whole disk */
> diff --git a/e2fsck/unix.c b/e2fsck/unix.c
> index cbe5ec5..beeaed2 100644
> --- a/e2fsck/unix.c
> +++ b/e2fsck/unix.c
> @@ -1250,7 +1250,8 @@ check_error:
>  		dump_mmp_msg(fs->mmp_buf,
>  			     _("If you are sure the filesystem is not "
>  			       "in use on any node, run:\n"
> -			       "'tune2fs -f -E clear_mmp {device}'\n"));
> +			       "'tune2fs -f -E clear_mmp %s'\n"),
> +			     ctx->device_name);
>  	} else if (retval == EXT2_ET_MMP_MAGIC_INVALID) {
>  		if (fix_problem(ctx, PR_0_MMP_INVALID_MAGIC, &pctx)) {
>  			ext2fs_mmp_clear(fs);
> diff --git a/e2fsck/util.c b/e2fsck/util.c
> index ed94702..5793b65 100644
> --- a/e2fsck/util.c
> +++ b/e2fsck/util.c
> @@ -756,16 +756,28 @@ int write_all(int fd, char *buf, size_t count)
>  	return c;
>  }
>  
> -void dump_mmp_msg(struct mmp_struct *mmp, const char *msg)
> +void dump_mmp_msg(struct mmp_struct *mmp, const char *fmt, ...)
>  {
> +	va_list pvar;
>  
> -	if (msg)
> -		printf("MMP check failed: %s\n", msg);
> +	if (fmt) {
> +		printf("MMP check failed: ");
> +		va_start(pvar, fmt);
> +		vprintf(fmt, pvar);
> +		va_end(pvar);
> +	}
>  	if (mmp) {
>  		time_t t = mmp->mmp_time;
>  
> -		printf("MMP error info: last update: %s node: %s device: %s\n",
> -		       ctime(&t), mmp->mmp_nodename, mmp->mmp_bdevname);
> +		printf("MMP_block:\n");
> +		printf("    mmp_magic: 0x%x\n", mmp->mmp_magic);
> +		printf("    mmp_check_interval: %d\n",
> +		       mmp->mmp_check_interval);
> +		printf("    mmp_sequence: %08x\n", mmp->mmp_seq);
> +		printf("    mmp_update_date: %s", ctime(&t));
> +		printf("    mmp_update_time: %lld\n", mmp->mmp_time);
> +		printf("    mmp_node_name: %s\n", mmp->mmp_nodename);
> +		printf("    mmp_device_name: %s\n", mmp->mmp_bdevname);
>  	}
>  }
>  
> diff --git a/e2fsprogs.spec.in b/e2fsprogs.spec.in
> index b188b75..f42c4be 100644
> --- a/e2fsprogs.spec.in
> +++ b/e2fsprogs.spec.in
> @@ -116,6 +116,7 @@ exit 0
>  %{_root_sbindir}/e2fsck
>  %{_root_sbindir}/e2image
>  %{_root_sbindir}/e2label
> +%{_root_sbindir}/e2mmpstatus
>  %{_root_sbindir}/e2undo
>  %{_root_sbindir}/findfs
>  %{_root_sbindir}/fsck
> @@ -167,6 +168,7 @@ exit 0
>  %{_mandir}/man8/fsck.ext4dev.8*
>  %{_mandir}/man8/e2image.8*
>  %{_mandir}/man8/e2label.8*
> +%{_mandir}/man8/e2mmpstatus.8*
>  %{_mandir}/man8/e2undo.8*
>  %{_mandir}/man8/fsck.8*
>  %{_mandir}/man8/logsave.8*
> diff --git a/lib/config.h.in b/lib/config.h.in
> index 9cc0793..67a0548 100644
> --- a/lib/config.h.in
> +++ b/lib/config.h.in
> @@ -147,6 +147,9 @@
>  /* Define to 1 if you have the `fchown' function. */
>  #undef HAVE_FCHOWN
>  
> +/* Define to 1 if you have the `fcntl' function. */
> +#undef HAVE_FCNTL
> +
>  /* Define to 1 if you have the `fdatasync' function. */
>  #undef HAVE_FDATASYNC
>  
> @@ -156,6 +159,9 @@
>  /* Define to 1 if you have the `fstat64' function. */
>  #undef HAVE_FSTAT64
>  
> +/* Define to 1 if you have the `fsync' function. */
> +#undef HAVE_FSYNC
> +
>  /* Define to 1 if you have the `ftruncate64' function. */
>  #undef HAVE_FTRUNCATE64
>  
> @@ -183,6 +189,9 @@
>  /* Define to 1 if you have the `getgid' function. */
>  #undef HAVE_GETGID
>  
> +/* Define to 1 if you have the `gethostname' function. */
> +#undef HAVE_GETHOSTNAME
> +
>  /* Define to 1 if you have the `getmntinfo' function. */
>  #undef HAVE_GETMNTINFO
>  
> @@ -253,6 +262,9 @@
>  /* Define to 1 if you have the <linux/major.h> header file. */
>  #undef HAVE_LINUX_MAJOR_H
>  
> +/* Define to 1 if you have the <linux/types.h> header file. */
> +#undef HAVE_LINUX_TYPES_H
> +
>  /* Define to 1 if you have the `llistxattr' function. */
>  #undef HAVE_LLISTXATTR
>  
> @@ -470,9 +482,6 @@
>  /* Define to 1 if you have the `sync_file_range' function. */
>  #undef HAVE_SYNC_FILE_RANGE
>  
> -/* Define to 1 if you have the 'fsync' function. */
> -#undef HAVE_FSYNC
> -
>  /* Define to 1 if you have the `sysconf' function. */
>  #undef HAVE_SYSCONF
>  
> diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
> index 16abd23..b2ba71a 100644
> --- a/lib/ext2fs/ext2_err.et.in
> +++ b/lib/ext2fs/ext2_err.et.in
> @@ -429,7 +429,7 @@ ec	EXT2_ET_MMP_FAILED,
>  	"MMP: device currently active"
>  
>  ec	EXT2_ET_MMP_FSCK_ON,
> -	"MMP: fsck being run"
> +	"MMP: e2fsck being run"
>  
>  ec	EXT2_ET_MMP_BAD_BLOCK,
>  	"MMP: block number beyond filesystem range"
> @@ -471,7 +471,7 @@ ec	EXT2_ET_UNKNOWN_CSUM,
>  	"Unknown checksum algorithm"
>  
>  ec	EXT2_ET_MMP_CSUM_INVALID,
> -	"MMP block checksum does not match MMP block"
> +	"MMP block checksum does not match"
>  
>  ec	EXT2_ET_FILE_EXISTS,
>  	"Ext2 file already exists"
> diff --git a/lib/ext2fs/mmp.c b/lib/ext2fs/mmp.c
> index 9a771de..0cf0d0d 100644
> --- a/lib/ext2fs/mmp.c
> +++ b/lib/ext2fs/mmp.c
> @@ -194,7 +194,7 @@ static errcode_t ext2fs_mmp_reset(ext2_filsys fs)
>  	mmp_s->mmp_magic = EXT4_MMP_MAGIC;
>  	mmp_s->mmp_seq = EXT4_MMP_SEQ_CLEAN;
>  	mmp_s->mmp_time = 0;
> -#if _BSD_SOURCE || _XOPEN_SOURCE >= 500
> +#ifdef HAVE_GETHOSTNAME
>  	gethostname(mmp_s->mmp_nodename, sizeof(mmp_s->mmp_nodename));
>  #else
>  	mmp_s->mmp_nodename[0] = '\0';
> @@ -269,6 +269,10 @@ out:
>  #endif
>  }
>  
> +#ifndef min
> +#define min(x, y) ((x) < (y) ? (x) : (y))
> +#endif
> +
>  /*
>   * Make sure that the fs is not mounted or being fsck'ed while opening the fs.
>   */
> @@ -316,7 +320,7 @@ errcode_t ext2fs_mmp_start(ext2_filsys fs)
>  	if (mmp_s->mmp_check_interval > mmp_check_interval)
>  		mmp_check_interval = mmp_s->mmp_check_interval;
>  
> -	sleep(2 * mmp_check_interval + 1);
> +	sleep(min(mmp_check_interval * 2 + 1, mmp_check_interval + 60));
>  
>  	retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
>  	if (retval)
> @@ -332,7 +336,7 @@ clean_seq:
>  		goto mmp_error;
>  
>  	mmp_s->mmp_seq = seq = ext2fs_mmp_new_seq();
> -#if _BSD_SOURCE || _XOPEN_SOURCE >= 500
> +#ifdef HAVE_GETHOSTNAME
>  	gethostname(mmp_s->mmp_nodename, sizeof(mmp_s->mmp_nodename));
>  #else
>  	strcpy(mmp_s->mmp_nodename, "unknown host");
> @@ -344,7 +348,7 @@ clean_seq:
>  	if (retval)
>  		goto mmp_error;
>  
> -	sleep(2 * mmp_check_interval + 1);
> +	sleep(min(2 * mmp_check_interval + 1, mmp_check_interval + 60));
>  
>  	retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
>  	if (retval)
> diff --git a/misc/Makefile.in b/misc/Makefile.in
> index efc0e0b..f500276 100644
> --- a/misc/Makefile.in
> +++ b/misc/Makefile.in
> @@ -39,7 +39,8 @@ USPROGS=	mklost+found filefrag e2freefrag $(UUIDD_PROG) \
>  SMANPAGES=	tune2fs.8 mklost+found.8 mke2fs.8 dumpe2fs.8 badblocks.8 \
>  			e2label.8 $(FINDFS_MAN) $(BLKID_MAN) $(E2IMAGE_MAN) \
>  			logsave.8 filefrag.8 e2freefrag.8 e2undo.8 \
> -			$(UUIDD_MAN) $(E4DEFRAG_MAN) $(E4CRYPT_MAN) @FSCK_MAN@
> +			$(UUIDD_MAN) $(E4DEFRAG_MAN) $(E4CRYPT_MAN) @FSCK_MAN@ \
> +			e2mmpstatus.8
>  FMANPAGES=	mke2fs.conf.5 ext4.5
>  
>  UPROGS=		chattr lsattr @UUID_CMT@ uuidgen
> @@ -475,6 +476,10 @@ dumpe2fs.8: $(DEP_SUBSTITUTE) $(srcdir)/dumpe2fs.8.in
>  	$(E) "	SUBST $@"
>  	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/dumpe2fs.8.in dumpe2fs.8
>  
> +e2mmpstatus.8: $(DEP_SUBSTITUTE) $(srcdir)/e2mmpstatus.8.in
> +	$(E) "	SUBST $@"
> +	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2mmpstatus.8.in e2mmpstatus.8
> +
>  badblocks.8: $(DEP_SUBSTITUTE) $(srcdir)/badblocks.8.in
>  	$(E) "	SUBST $@"
>  	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/badblocks.8.in badblocks.8	
> @@ -546,6 +551,8 @@ install: all $(SMANPAGES) $(UMANPAGES) installdirs
>  			$(LN) $(LINK_INSTALL_FLAGS) mke2fs mkfs.$$i); \
>  	done
>  	$(Q) (cd $(DESTDIR)$(root_sbindir); \
> +		$(LN) $(LINK_INSTALL_FLAGS) dumpe2fs e2mmpstatus)
> +	$(Q) (cd $(DESTDIR)$(root_sbindir); \
>  		$(LN) $(LINK_INSTALL_FLAGS) tune2fs e2label)
>  	$(Q) if test -n "$(FINDFS_LINK)"; then \
>  		$(ES) "	LINK $(root_sbindir)/findfs"; \
> @@ -661,7 +668,7 @@ uninstall:
>  	for i in $(UMANPAGES); do \
>  		$(RM) -f $(DESTDIR)$(man1dir)/$$i; \
>  	done
> -	for i in $(FINDFS_LINK) e2label ; do \
> +	for i in $(FINDFS_LINK) e2label e2mmpstatus ; do \
>  		$(RM) -f $(DESTDIR)$(root_sbindir)/$$i; \
>  	done
>  	for i in $(FMANPAGES); do \
> diff --git a/misc/dumpe2fs.8.in b/misc/dumpe2fs.8.in
> index da78d4f..ce3214f 100644
> --- a/misc/dumpe2fs.8.in
> +++ b/misc/dumpe2fs.8.in
> @@ -69,6 +69,17 @@ using
>  .I device
>  as the pathname to the image file.
>  .TP
> +.B \-m
> +If the
> +.B mmp
> +feature is enabled on the filesystem, check if
> +.I device
> +is in use by another node, see
> +.BR e2mmpstatus (8)
> +for full details.  If used together with the
> +.B \-i
> +option, only the MMP block information is printed.
> +.TP
>  .B \-x
>  print the detailed group information block numbers in hexadecimal format
>  .TP
> @@ -76,8 +87,16 @@ print the detailed group information block numbers in hexadecimal format
>  print the version number of
>  .B dumpe2fs
>  and exit.
> +.SH EXIT CODE
> +.B dumpe2fs
> +exits with a return code of 0 if the operation completed without errors.
> +It will exit with a non-zero return code if there are any errors, such
> +as problems reading a valid superblock, bad checksums, or if the device
> +is in use by another node and
> +.B -m
> +is specified.
>  .SH BUGS
> -You need to know the physical filesystem structure to understand the
> +You may need to know the physical filesystem structure to understand the
>  output.
>  .SH AUTHOR
>  .B dumpe2fs
> @@ -89,6 +108,7 @@ is part of the e2fsprogs package and is available from
>  http://e2fsprogs.sourceforge.net.
>  .SH SEE ALSO
>  .BR e2fsck (8),
> +.BR e2mmpstatus (8),
>  .BR mke2fs (8),
>  .BR tune2fs (8).
>  .BR ext4 (5)
> diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
> index 395ea9e..384ce92 100644
> --- a/misc/dumpe2fs.c
> +++ b/misc/dumpe2fs.c
> @@ -53,7 +53,7 @@ static int blocks64 = 0;
>  
>  static void usage(void)
>  {
> -	fprintf(stderr, _("Usage: %s [-bfghixV] [-o superblock=<num>] "
> +	fprintf(stderr, _("Usage: %s [-bfghimxV] [-o superblock=<num>] "
>  		 "[-o blocksize=<num>] device\n"), program_name);
>  	exit(1);
>  }
> @@ -420,6 +420,79 @@ static void print_journal_information(ext2_filsys fs)
>  	e2p_list_journal_super(stdout, buf, fs->blocksize, 0);
>  }
>  
> +static int check_mmp(ext2_filsys fs)
> +{
> +	int retval;
> +
> +	/* This won't actually start MMP on the filesystem, since fs is opened
> +	 * readonly, but it will do the proper activity checking for us. */
> +	retval = ext2fs_mmp_start(fs);
> +	if (retval) {
> +		com_err(program_name, retval, _("while trying to open %s"),
> +			fs->device_name);
> +		if (retval == EXT2_ET_MMP_FAILED ||
> +		    retval == EXT2_ET_MMP_FSCK_ON ||
> +		    retval == EXT2_ET_MMP_CSUM_INVALID ||
> +		    retval == EXT2_ET_MMP_UNKNOWN_SEQ) {
> +			if (fs->mmp_buf) {
> +				struct mmp_struct *mmp = fs->mmp_buf;
> +				time_t mmp_time = mmp->mmp_time;
> +
> +				fprintf(stderr,
> +					"%s: MMP last updated by '%s' on %s",
> +					program_name, mmp->mmp_nodename,
> +					ctime(&mmp_time));
> +			}
> +			retval = 1;
> +		} else {
> +			retval = 2;
> +		}
> +	} else {
> +		printf("%s: it is safe to mount '%s', MMP is clean\n",
> +		       program_name, fs->device_name);
> +	}
> +
> +	return retval;
> +}
> +
> +static void print_mmp_block(ext2_filsys fs)
> +{
> +	struct mmp_struct *mmp;
> +	time_t mmp_time;
> +	errcode_t retval;
> +
> +	if (fs->mmp_buf == NULL) {
> +		retval = ext2fs_get_mem(fs->blocksize, &fs->mmp_buf);
> +		if (retval) {
> +			com_err(program_name, retval,
> +				_("failed to alloc MMP buffer\n"));
> +			return;
> +		}
> +	}
> +
> +	retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
> +	/* this is only dumping, not checking status, so OK to skip this */
> +	if (retval == EXT2_ET_OP_NOT_SUPPORTED)
> +		return;
> +	if (retval) {
> +		com_err(program_name, retval,
> +			_("reading MMP block %llu from '%s'\n"),
> +			fs->super->s_mmp_block, fs->device_name);
> +		return;
> +	}
> +
> +	mmp = fs->mmp_buf;
> +	mmp_time = mmp->mmp_time;
> +	printf("MMP_block:\n");
> +	printf("    mmp_magic: 0x%x\n", mmp->mmp_magic);
> +	printf("    mmp_check_interval: %d\n", mmp->mmp_check_interval);
> +	printf("    mmp_sequence: %#08x\n", mmp->mmp_seq);
> +	printf("    mmp_update_date: %s", ctime(&mmp_time));
> +	printf("    mmp_update_time: %lld\n", mmp->mmp_time);
> +	printf("    mmp_node_name: %s\n", mmp->mmp_nodename);
> +	printf("    mmp_device_name: %s\n", mmp->mmp_bdevname);
> +}
> +
>  static void parse_extended_opts(const char *opts, blk64_t *superblock,
>  				int *blocksize)
>  {
> @@ -500,11 +573,15 @@ static void parse_extended_opts(const char *opts, blk64_t *superblock,
>  int main (int argc, char ** argv)
>  {
>  	errcode_t	retval;
> +	errcode_t	retval_csum = 0;
> +	const char	*error_csum = NULL;
>  	ext2_filsys	fs;
>  	int		print_badblocks = 0;
>  	blk64_t		use_superblock = 0;
>  	int		use_blocksize = 0;
>  	int		image_dump = 0;
> +	int		mmp_check = 0;
> +	int		mmp_info = 0;
>  	int		force = 0;
>  	int		flags;
>  	int		header_only = 0;
> @@ -519,12 +596,23 @@ int main (int argc, char ** argv)
>  	set_com_err_gettext(gettext);
>  #endif
>  	add_error_table(&et_ext2_error_table);
> -	fprintf (stderr, "dumpe2fs %s (%s)\n", E2FSPROGS_VERSION,
> -		 E2FSPROGS_DATE);
> -	if (argc && *argv)
> -		program_name = *argv;
> +	if (argc && *argv) {
> +		if (strrchr(*argv, '/'))
> +			program_name = strrchr(*argv, '/') + 1;
> +		else
> +			program_name = *argv;
> +
> +		if (strstr(program_name, "mmpstatus") != NULL) {
> +			mmp_check = 1;
> +			header_only = 1;
> +		}
> +	}
>  
> -	while ((c = getopt(argc, argv, "bfghixVo:")) != EOF) {
> +	if (!mmp_check)
> +		fprintf(stderr, "dumpe2fs %s (%s)\n", E2FSPROGS_VERSION,
> +			E2FSPROGS_DATE);
> +
> +	while ((c = getopt(argc, argv, "bfghimxVo:")) != EOF) {
>  		switch (c) {
>  		case 'b':
>  			print_badblocks++;
> @@ -539,7 +627,18 @@ int main (int argc, char ** argv)
>  			header_only++;
>  			break;
>  		case 'i':
> -			image_dump++;
> +			if (mmp_check)
> +				mmp_info++;
> +			else
> +				image_dump++;
> +			break;
> +		case 'm':
> +			mmp_check++;
> +			header_only++;
> +			if (image_dump) {
> +				mmp_info = image_dump;
> +				image_dump = 0;
> +			}

That's fugly, I hate it with passion. The whole idea of making a decision
based on the binary name, changing the meaning of the parameters and on
top of that changing the meanin of "-i" in case we specify "-m" as
well, looks like a horrible mess to me.

Again, why do we need the mmpstatus alias ? why is dumpe2fs not enough ?

-Lukas

>  			break;
>  		case 'o':
>  			parse_extended_opts(optarg, &use_superblock,
> @@ -557,12 +656,12 @@ int main (int argc, char ** argv)
>  			usage();
>  		}
>  	}
> -	if (optind != argc - 1) {
> +	if (optind != argc - 1)
>  		usage();
> -		exit(1);
> -	}
> +
>  	device_name = argv[optind++];
> -	flags = EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_64BITS;
> +	flags = EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES |
> +		EXT2_FLAG_64BITS;
>  	if (force)
>  		flags |= EXT2_FLAG_FORCE;
>  	if (image_dump)
> @@ -579,64 +678,87 @@ try_open_again:
>  			if (!retval)
>  				break;
>  		}
> -	} else
> -		retval = ext2fs_open (device_name, flags, use_superblock,
> -				      use_blocksize, unix_io_manager, &fs);
> -	if (retval && !(flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
> -		flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
> -		goto try_open_again;
> +	} else {
> +		retval = ext2fs_open(device_name, flags, use_superblock,
> +				     use_blocksize, unix_io_manager, &fs);
>  	}
> -	if (!retval && (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS))
> -		printf("%s", _("\n*** Checksum errors detected in filesystem!  Run e2fsck now!\n\n"));
>  	flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
> +	if (retval && !retval_csum) {
> +		retval_csum = retval;
> +		error_csum = _("while trying to open %s");
> +		goto try_open_again;
> +	}
>  	if (retval) {
> -		com_err (program_name, retval, _("while trying to open %s"),
> -			 device_name);
> +		com_err(program_name, retval, _("while trying to open %s"),
> +			device_name);
>  		printf("%s", _("Couldn't find valid filesystem superblock.\n"));
>  		if (retval == EXT2_ET_BAD_MAGIC)
>  			check_plausibility(device_name, CHECK_FS_EXIST, NULL);
> -		exit (1);
> +		goto out;
>  	}
>  	fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;
>  	if (ext2fs_has_feature_64bit(fs->super))
>  		blocks64 = 1;
> -	if (print_badblocks) {
> +	if (mmp_check) {
> +		if (ext2fs_has_feature_mmp(fs->super) &&
> +		    fs->super->s_mmp_block != 0) {
> +			if (mmp_info) {
> +				print_mmp_block(fs);
> +				printf("    mmp_block_number: ");
> +				print_number(fs->super->s_mmp_block);
> +				printf("\n");
> +			} else {
> +				retval = check_mmp(fs);
> +			}
> +			if (!retval && retval_csum)
> +				retval = 2;
> +		} else {
> +			fprintf(stderr, _("%s: MMP feature not enabled.\n"),
> +				program_name);
> +			retval = 2;
> +		}
> +	} else if (print_badblocks) {
>  		list_bad_blocks(fs, 1);
>  	} else {
>  		if (grp_only)
>  			goto just_descriptors;
> -		list_super (fs->super);
> +		list_super(fs->super);
>  		if (ext2fs_has_feature_journal_dev(fs->super)) {
>  			print_journal_information(fs);
> -			ext2fs_close_free(&fs);
> -			exit(0);
> +
> +			goto out_close;
>  		}
>  		if (ext2fs_has_feature_journal(fs->super) &&
>  		    (fs->super->s_journal_inum != 0))
>  			print_inline_journal_information(fs);
> +		if (ext2fs_has_feature_mmp(fs->super) &&
> +		    fs->super->s_mmp_block != 0)
> +			print_mmp_block(fs);
>  		list_bad_blocks(fs, 0);
> -		if (header_only) {
> -			ext2fs_close_free(&fs);
> -			exit (0);
> -		}
> +		if (header_only)
> +			goto out_close;
> +
>  		fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
>  try_bitmaps_again:
> -		retval = ext2fs_read_bitmaps (fs);
> -		if (retval && !(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
> +		retval = ext2fs_read_bitmaps(fs);
> +		if (retval && !retval_csum) {
>  			fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
> +			retval_csum = retval;
> +			error_csum = _("while trying to read '%s' bitmaps\n");
>  			goto try_bitmaps_again;
>  		}
> -		if (!retval && (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS))
> -			printf("%s", _("\n*** Checksum errors detected in bitmaps!  Run e2fsck now!\n\n"));
>  just_descriptors:
>  		list_desc(fs, grp_only);
> -		if (retval) {
> -			printf(_("\n%s: %s: error reading bitmaps: %s\n"),
> -			       program_name, device_name,
> -			       error_message(retval));
> -		}
> +	}
> +out_close:
> +	if (retval_csum) {
> +		com_err(program_name, retval_csum, error_csum, device_name);
> +		printf("%s", _("*** Run e2fsck now!\n\n"));
> +		if (!retval)
> +			retval = retval_csum;
>  	}
>  	ext2fs_close_free(&fs);
>  	remove_error_table(&et_ext2_error_table);
> -	exit (0);
> +out:
> +	return retval;
>  }
> diff --git a/misc/e2mmpstatus.8.in b/misc/e2mmpstatus.8.in
> new file mode 100644
> index 0000000..f7d9557
> --- /dev/null
> +++ b/misc/e2mmpstatus.8.in
> @@ -0,0 +1,59 @@
> +.\" -*- nroff -*-
> +.\" This file may be copied under the terms of the GNU Public License.
> +.\"
> +.TH E2MMPSTATUS 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
> +.SH NAME
> +e2mmpstatus \- Check MMP status of an ext4 filesystem
> +.SH SYNOPSIS
> +.BR e2mmpstatus " [" \-i ]
> +.RI < filesystem >
> +.SH OPTIONS
> +.TP
> +.B \-i
> +prints out the MMP information rather than check it.
> +.SH DESCRIPTION
> +.B e2mmpstatus
> +is used to check Multiple-Mount Protection (MMP) status of an ext4
> +filesystem with the
> +.B mmp
> +feature enabled.  The specified
> +.I filesystem
> +can be a device name (e.g.
> +.IR /dev/hdc1 ", " /dev/sdb2 ),
> +or an ext4 filesystem label or UUID, for example
> +.B UUID=8868abf6-88c5-4a83-98b8-bfc24057f7bd
> +or
> +.BR LABEL=root .
> +By default, the
> +.B e2mmpstatus
> +program checks whether it is safe to mount the filesystem without taking
> +the risk of mounting it more than once.
> +.PP
> +MMP (multiple-mount protection) is a feature that adds protection against
> +the filesystem being modified simultaneously by more than one node.
> +It is NOT safe to mount a filesystem when one of the following conditions
> +is true:
> +.br
> +\	1. e2fsck is running on the filesystem.
> +.br
> +\	2. the filesystem is in use by another node.
> +.br
> +\	3. The MMP block is corrupted or cannot be read for some reason.
> +.br
> +The
> +.B e2mmpstatus
> +program might wait for some time to see whether the MMP block is being
> +updated by any node during this period.  The time taken depends on how
> +frequently the MMP block is being written by the other node.
> +.SH EXIT CODE
> +The exit code returned by
> +.B e2mmpstatus
> +is 0 when it is safe to mount the filesystem, 1 when the MMP block shows
> +the filesystem is in use on another node and it is NOT safe to mount
> +the filesystem, and 2 if some other failure occurred that prevents the
> +check from properly detecting the current MMP status.
> +.SH SEE ALSO
> +.BR dumpe2fs (8),
> +.BR e2fsck (8),
> +.BR fstab (5),
> +.BR fsck (8),
> diff --git a/tests/f_mmp/script b/tests/f_mmp/script
> index 9ff16c9..07ae232 100644
> --- a/tests/f_mmp/script
> +++ b/tests/f_mmp/script
> @@ -43,6 +43,13 @@ rm -f $MARKFILE
>  echo "kill debugfs abruptly (simulates e2fsck failure) ..." >> $test_name.log
>  kill_debugfs
>  
> +$E2MMPSTATUS $TMPFILE > $test_name.log 2>&1
> +status=$?
> +if [ "$status" != 1 ] ; then
> +	echo "$E2MMPSTATUS with EXT2_MMP_SEQ_FSCK passed!" > $test_name.failed
> +	echo "$test_name: $test_description: failed"
> +	return 1
> +fi
>  
>  echo "e2fsck (should fail mmp_seq = EXT2_MMP_SEQ_FSCK) ..." >> $test_name.log
>  $FSCK $FSCK_OPT $TMPFILE >> $test_name.log 2>&1
> diff --git a/tests/m_mmp/expect.1 b/tests/m_mmp/expect.1
> index a1452e6..9d8a5a3 100644
> --- a/tests/m_mmp/expect.1
> +++ b/tests/m_mmp/expect.1
> @@ -46,6 +46,14 @@ Inode size:	          128
>  Default directory hash:   half_md4
>  MMP block number:         1049
>  MMP update interval:      5
> +MMP_block:
> +    mmp_magic: 0x4d4d50
> +    mmp_check_interval: 5
> +    mmp_sequence: 0xff4d4d50
> +    mmp_update_date: test date
> +    mmp_update_time: test_time
> +    mmp_node_name: test_node
> +    mmp_device_name: test.img
>  
>  
>  Group 0: (Blocks 0-32767)
> diff --git a/tests/m_mmp_bad_csum/expect b/tests/m_mmp_bad_csum/expect
> index e15e7b4..a0678ac 100644
> --- a/tests/m_mmp_bad_csum/expect
> +++ b/tests/m_mmp_bad_csum/expect
> @@ -1,4 +1,7 @@
> -Superblock MMP block checksum does not match MMP block.  Fix? yes
> +dumpe2fs: MMP block checksum does not match while trying to open test.img
> +dumpe2fs: MMP last updated by 'test_node' on test date
> +Exit status is 1
> +Superblock MMP block checksum does not match.  Fix? yes
>  
>  Pass 1: Checking inodes, blocks, and sizes
>  Pass 2: Checking directory structure
> @@ -7,3 +10,14 @@ Pass 4: Checking reference counts
>  Pass 5: Checking group summary information
>  test_filesys: 11/128 files (0.0% non-contiguous), 19/512 blocks
>  Exit status is 0
> +dumpe2fs: it is safe to mount 'test.img', MMP is clean
> +Exit status is 0
> +MMP_block:
> +    mmp_magic: 0x4d4d50
> +    mmp_check_interval: 5
> +    mmp_sequence: 0xff4d4d50
> +    mmp_update_date: test date
> +    mmp_update_time: test_time
> +    mmp_node_name: test_node
> +    mmp_device_name: test.img
> +    mmp_block_number: 8
> diff --git a/tests/m_mmp_bad_csum/script b/tests/m_mmp_bad_csum/script
> index 09e870c..4c8fe16 100644
> --- a/tests/m_mmp_bad_csum/script
> +++ b/tests/m_mmp_bad_csum/script
> @@ -12,8 +12,15 @@ gzip -dc < $test_dir/image.gz > $TMPFILE
>  
>  OUT=$test_name.log
>  EXP=$test_dir/expect
> -$FSCK -fy $TMPFILE 2>&1 | sed -f $cmd_dir/filter.sed > $OUT
> +$E2MMPSTATUS $TMPFILE > $OUT 2>&1
>  echo Exit status is $? >> $OUT
> +$FSCK -fy $TMPFILE >> $OUT 2>&1
> +echo Exit status is $? >> $OUT
> +$E2MMPSTATUS $TMPFILE >> $OUT 2>&1
> +echo Exit status is $? >> $OUT
> +$E2MMPSTATUS -i $TMPFILE >> $OUT 2>&1
> +sed -f $cmd_dir/filter.sed $OUT > $OUT.new
> +mv $OUT.new $OUT
>  
>  rm -f $TMPFILE
>  cmp -s $OUT $EXP
> diff --git a/tests/m_mmp_bad_magic/expect b/tests/m_mmp_bad_magic/expect
> index b5dfb89..d5fa98c 100644
> --- a/tests/m_mmp_bad_magic/expect
> +++ b/tests/m_mmp_bad_magic/expect
> @@ -1,3 +1,5 @@
> +dumpe2fs: MMP: invalid magic number while trying to open test.img
> +Exit status is 2
>  Superblock has invalid MMP magic.  Fix? yes
>  
>  Pass 1: Checking inodes, blocks, and sizes
> @@ -7,3 +9,14 @@ Pass 4: Checking reference counts
>  Pass 5: Checking group summary information
>  test_filesys: 11/128 files (0.0% non-contiguous), 19/512 blocks
>  Exit status is 0
> +dumpe2fs: it is safe to mount 'test.img', MMP is clean
> +Exit status is 0
> +MMP_block:
> +    mmp_magic: 0x4d4d50
> +    mmp_check_interval: 5
> +    mmp_sequence: 0xff4d4d50
> +    mmp_update_date: test date
> +    mmp_update_time: test_time
> +    mmp_node_name: test_node
> +    mmp_device_name: test.img
> +    mmp_block_number: 8
> diff --git a/tests/m_mmp_bad_magic/script b/tests/m_mmp_bad_magic/script
> index 09e870c..4c8fe16 100644
> --- a/tests/m_mmp_bad_magic/script
> +++ b/tests/m_mmp_bad_magic/script
> @@ -12,8 +12,15 @@ gzip -dc < $test_dir/image.gz > $TMPFILE
>  
>  OUT=$test_name.log
>  EXP=$test_dir/expect
> -$FSCK -fy $TMPFILE 2>&1 | sed -f $cmd_dir/filter.sed > $OUT
> +$E2MMPSTATUS $TMPFILE > $OUT 2>&1
>  echo Exit status is $? >> $OUT
> +$FSCK -fy $TMPFILE >> $OUT 2>&1
> +echo Exit status is $? >> $OUT
> +$E2MMPSTATUS $TMPFILE >> $OUT 2>&1
> +echo Exit status is $? >> $OUT
> +$E2MMPSTATUS -i $TMPFILE >> $OUT 2>&1
> +sed -f $cmd_dir/filter.sed $OUT > $OUT.new
> +mv $OUT.new $OUT
>  
>  rm -f $TMPFILE
>  cmp -s $OUT $EXP
> diff --git a/tests/test_config b/tests/test_config
> index cf9c79c..2aee6ff 100644
> --- a/tests/test_config
> +++ b/tests/test_config
> @@ -25,6 +25,7 @@ RESIZE2FS_EXE="../resize/resize2fs"
>  RESIZE2FS="$USE_VALGRIND $RESIZE2FS_EXE"
>  E2UNDO_EXE="../misc/e2undo"
>  E2UNDO="$USE_VALGRIND $E2UNDO_EXE"
> +E2MMPSTATUS="$USE_VALGRIND ../misc/dumpe2fs -m"
>  TEST_REL=../tests/progs/test_rel
>  TEST_ICOUNT=../tests/progs/test_icount
>  CRCSUM=../tests/progs/crcsum
> -- 
> 1.8.0
>
Andreas Dilger May 4, 2018, 6:31 p.m. | #2
> Subject: [PATCH 10/10] misc: add e2mmpstatus utility via debugfs

I just noticed that I made an error in the patch summary.  This
should be "dumpe2fs" rather than "debugfs".

On May 4, 2018, at 4:06 AM, Lukas Czerner <lczerner@redhat.com> wrote:
> 
> On Tue, May 01, 2018 at 10:26:06PM -0600, Andreas Dilger wrote:
>> From: Shuichi Ihara <sihara@ddn.com>
>> 
>> e2mmpstatus is a Multi-Mount Protection (MMP) helper utility to read
>> an MMP block to see if it is being updated.  It can also output the
>> latest update time, nodename, and device from the MMP block.
>> 
>> This is useful for HA and other maintenance scripts to determine if
>> the filesystem is in use on another node, and which node it is.
>> 
>> Signed-off-by: Shuichi Ihara <sihara@ddn.com>
>> Signed-off-by: Li Xi <lixi@ddn.com>
>> Signed-off-by: Wang Shilong <wshilong@ddn.com>
>> 
>> Moved e2mmpstatus checking/dumping code to be part of dumpe2fs rather
>> than a standalone program, using the "-m" option to check MMP status,
>> and "-i" to dump info.  If dumpe2fs is called as "e2mmpstatus" (and
>> also "mmpstatus" for compatibility reasons), assume "-m" is specified.
> 
> Hmm, why do we need an alias to dumpe2fs options ? For what
> compatibility you need mmpstatus as well ? I do not like it, see below.

For compatibility with people who are using the old (standalone) version
of mmpstatus on their systems.  I'm trying to reduce the delta between
the Lustre-specific e2fsprogs and upstream, and hopefully one day get rid
of the Lustre version.  The "e2" part of "e2mmpstatus" name was my change,
since it otherwise isn't clear that the tool is related to e2fsprogs at
all, the original tool was named "mmpstatus".  Since the string comparison
is virtually the same I figured not a big deal to allow both.

>> @@ -539,7 +627,18 @@ int main (int argc, char ** argv)
>> 			header_only++;
>> 			break;
>> 		case 'i':
>> -			image_dump++;
>> +			if (mmp_check)
>> +				mmp_info++;
>> +			else
>> +				image_dump++;
>> +			break;
>> +		case 'm':
>> +			mmp_check++;
>> +			header_only++;
>> +			if (image_dump) {
>> +				mmp_info = image_dump;
>> +				image_dump = 0;
>> +			}
> 
> That's fugly, I hate it with passion. The whole idea of making a decision
> based on the binary name changing the meaning of the parameters

Take a look at mke2fs for a similar example:

        if (argc && *argv) {
                program_name = get_progname(*argv);

                /* If called as mkfs.ext3, create a journal inode */
                if (!strcmp(program_name, "mkfs.ext3") ||
                    !strcmp(program_name, "mke3fs"))
                        journal_size = -1;
        }

and the same for tune2fs:

#ifdef CONFIG_BUILD_FINDFS
        if (strcmp(get_progname(argv[0]), "findfs") == 0)
                do_findfs(argc, argv);
#endif
        if (strcmp(get_progname(argv[0]), "e2label") == 0)
                parse_e2label_options(argc, argv);
        else
                parse_tune2fs_options(argc, argv);

and also other utilities, so this isn't wholly unusual:

    0 lrwxrwxrwx 1 root root          5 Jun 19  2011 bunzip2 -> bzip2*
    0 lrwxrwxrwx 1 root root          5 Jun 19  2011 bzcat -> bzip2*


I'd originally submitted e2mmpstatus as a standalone binary, but Ted
asked me to implement it as part of dumpe2fs so that it would also get
this useful functionality, and compatibility could still be kept with
existing deployments.  Another alternative would be to include a one-line
shell script named e2mmpstatus, I don't have a strong opinion, but I
figured I'd stick with the same mechanism (argv[0] checking) used already:

#!/bin/sh
dumpe2fs -m $*

> and on top of that changing the meanin of "-i" in case we specify "-m" as
> well, looks like a horrible mess to me.

I agree that is a tiny bit messy, but '-i' was the argument to mmpstatus
to dump MMP info rather than return a pass/fail check of the MMP block.
I could take the route of e2label vs. tune2fs and have completely separate
argument parsing based on argv[0] so that dumpe2fs options don't bleed
over to e2mmpstatus, but I didn't think that was critical for just one
argument which is unlikely to actually conflict in real life.

Ted, what is your preference here?

Cheers, Andreas

> Again, why do we need the mmpstatus alias ? why is dumpe2fs not enough ?
> 
> -Lukas
> 
>> 			break;
>> 		case 'o':
>> 			parse_extended_opts(optarg, &use_superblock,
>> @@ -557,12 +656,12 @@ int main (int argc, char ** argv)
>> 			usage();
>> 		}
>> 	}
>> -	if (optind != argc - 1) {
>> +	if (optind != argc - 1)
>> 		usage();
>> -		exit(1);
>> -	}
>> +
>> 	device_name = argv[optind++];
>> -	flags = EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_64BITS;
>> +	flags = EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES |
>> +		EXT2_FLAG_64BITS;
>> 	if (force)
>> 		flags |= EXT2_FLAG_FORCE;
>> 	if (image_dump)
>> @@ -579,64 +678,87 @@ try_open_again:
>> 			if (!retval)
>> 				break;
>> 		}
>> -	} else
>> -		retval = ext2fs_open (device_name, flags, use_superblock,
>> -				      use_blocksize, unix_io_manager, &fs);
>> -	if (retval && !(flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
>> -		flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
>> -		goto try_open_again;
>> +	} else {
>> +		retval = ext2fs_open(device_name, flags, use_superblock,
>> +				     use_blocksize, unix_io_manager, &fs);
>> 	}
>> -	if (!retval && (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS))
>> -		printf("%s", _("\n*** Checksum errors detected in filesystem!  Run e2fsck now!\n\n"));
>> 	flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
>> +	if (retval && !retval_csum) {
>> +		retval_csum = retval;
>> +		error_csum = _("while trying to open %s");
>> +		goto try_open_again;
>> +	}
>> 	if (retval) {
>> -		com_err (program_name, retval, _("while trying to open %s"),
>> -			 device_name);
>> +		com_err(program_name, retval, _("while trying to open %s"),
>> +			device_name);
>> 		printf("%s", _("Couldn't find valid filesystem superblock.\n"));
>> 		if (retval == EXT2_ET_BAD_MAGIC)
>> 			check_plausibility(device_name, CHECK_FS_EXIST, NULL);
>> -		exit (1);
>> +		goto out;
>> 	}
>> 	fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;
>> 	if (ext2fs_has_feature_64bit(fs->super))
>> 		blocks64 = 1;
>> -	if (print_badblocks) {
>> +	if (mmp_check) {
>> +		if (ext2fs_has_feature_mmp(fs->super) &&
>> +		    fs->super->s_mmp_block != 0) {
>> +			if (mmp_info) {
>> +				print_mmp_block(fs);
>> +				printf("    mmp_block_number: ");
>> +				print_number(fs->super->s_mmp_block);
>> +				printf("\n");
>> +			} else {
>> +				retval = check_mmp(fs);
>> +			}
>> +			if (!retval && retval_csum)
>> +				retval = 2;
>> +		} else {
>> +			fprintf(stderr, _("%s: MMP feature not enabled.\n"),
>> +				program_name);
>> +			retval = 2;
>> +		}
>> +	} else if (print_badblocks) {
>> 		list_bad_blocks(fs, 1);
>> 	} else {
>> 		if (grp_only)
>> 			goto just_descriptors;
>> -		list_super (fs->super);
>> +		list_super(fs->super);
>> 		if (ext2fs_has_feature_journal_dev(fs->super)) {
>> 			print_journal_information(fs);
>> -			ext2fs_close_free(&fs);
>> -			exit(0);
>> +
>> +			goto out_close;
>> 		}
>> 		if (ext2fs_has_feature_journal(fs->super) &&
>> 		    (fs->super->s_journal_inum != 0))
>> 			print_inline_journal_information(fs);
>> +		if (ext2fs_has_feature_mmp(fs->super) &&
>> +		    fs->super->s_mmp_block != 0)
>> +			print_mmp_block(fs);
>> 		list_bad_blocks(fs, 0);
>> -		if (header_only) {
>> -			ext2fs_close_free(&fs);
>> -			exit (0);
>> -		}
>> +		if (header_only)
>> +			goto out_close;
>> +
>> 		fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
>> try_bitmaps_again:
>> -		retval = ext2fs_read_bitmaps (fs);
>> -		if (retval && !(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
>> +		retval = ext2fs_read_bitmaps(fs);
>> +		if (retval && !retval_csum) {
>> 			fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
>> +			retval_csum = retval;
>> +			error_csum = _("while trying to read '%s' bitmaps\n");
>> 			goto try_bitmaps_again;
>> 		}
>> -		if (!retval && (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS))
>> -			printf("%s", _("\n*** Checksum errors detected in bitmaps!  Run e2fsck now!\n\n"));
>> just_descriptors:
>> 		list_desc(fs, grp_only);
>> -		if (retval) {
>> -			printf(_("\n%s: %s: error reading bitmaps: %s\n"),
>> -			       program_name, device_name,
>> -			       error_message(retval));
>> -		}
>> +	}
>> +out_close:
>> +	if (retval_csum) {
>> +		com_err(program_name, retval_csum, error_csum, device_name);
>> +		printf("%s", _("*** Run e2fsck now!\n\n"));
>> +		if (!retval)
>> +			retval = retval_csum;
>> 	}
>> 	ext2fs_close_free(&fs);
>> 	remove_error_table(&et_ext2_error_table);
>> -	exit (0);
>> +out:
>> +	return retval;
>> }
>> diff --git a/misc/e2mmpstatus.8.in b/misc/e2mmpstatus.8.in
>> new file mode 100644
>> index 0000000..f7d9557
>> --- /dev/null
>> +++ b/misc/e2mmpstatus.8.in
>> @@ -0,0 +1,59 @@
>> +.\" -*- nroff -*-
>> +.\" This file may be copied under the terms of the GNU Public License.
>> +.\"
>> +.TH E2MMPSTATUS 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
>> +.SH NAME
>> +e2mmpstatus \- Check MMP status of an ext4 filesystem
>> +.SH SYNOPSIS
>> +.BR e2mmpstatus " [" \-i ]
>> +.RI < filesystem >
>> +.SH OPTIONS
>> +.TP
>> +.B \-i
>> +prints out the MMP information rather than check it.
>> +.SH DESCRIPTION
>> +.B e2mmpstatus
>> +is used to check Multiple-Mount Protection (MMP) status of an ext4
>> +filesystem with the
>> +.B mmp
>> +feature enabled.  The specified
>> +.I filesystem
>> +can be a device name (e.g.
>> +.IR /dev/hdc1 ", " /dev/sdb2 ),
>> +or an ext4 filesystem label or UUID, for example
>> +.B UUID=8868abf6-88c5-4a83-98b8-bfc24057f7bd
>> +or
>> +.BR LABEL=root .
>> +By default, the
>> +.B e2mmpstatus
>> +program checks whether it is safe to mount the filesystem without taking
>> +the risk of mounting it more than once.
>> +.PP
>> +MMP (multiple-mount protection) is a feature that adds protection against
>> +the filesystem being modified simultaneously by more than one node.
>> +It is NOT safe to mount a filesystem when one of the following conditions
>> +is true:
>> +.br
>> +\	1. e2fsck is running on the filesystem.
>> +.br
>> +\	2. the filesystem is in use by another node.
>> +.br
>> +\	3. The MMP block is corrupted or cannot be read for some reason.
>> +.br
>> +The
>> +.B e2mmpstatus
>> +program might wait for some time to see whether the MMP block is being
>> +updated by any node during this period.  The time taken depends on how
>> +frequently the MMP block is being written by the other node.
>> +.SH EXIT CODE
>> +The exit code returned by
>> +.B e2mmpstatus
>> +is 0 when it is safe to mount the filesystem, 1 when the MMP block shows
>> +the filesystem is in use on another node and it is NOT safe to mount
>> +the filesystem, and 2 if some other failure occurred that prevents the
>> +check from properly detecting the current MMP status.
>> +.SH SEE ALSO
>> +.BR dumpe2fs (8),
>> +.BR e2fsck (8),
>> +.BR fstab (5),
>> +.BR fsck (8),
>> diff --git a/tests/f_mmp/script b/tests/f_mmp/script
>> index 9ff16c9..07ae232 100644
>> --- a/tests/f_mmp/script
>> +++ b/tests/f_mmp/script
>> @@ -43,6 +43,13 @@ rm -f $MARKFILE
>> echo "kill debugfs abruptly (simulates e2fsck failure) ..." >> $test_name.log
>> kill_debugfs
>> 
>> +$E2MMPSTATUS $TMPFILE > $test_name.log 2>&1
>> +status=$?
>> +if [ "$status" != 1 ] ; then
>> +	echo "$E2MMPSTATUS with EXT2_MMP_SEQ_FSCK passed!" > $test_name.failed
>> +	echo "$test_name: $test_description: failed"
>> +	return 1
>> +fi
>> 
>> echo "e2fsck (should fail mmp_seq = EXT2_MMP_SEQ_FSCK) ..." >> $test_name.log
>> $FSCK $FSCK_OPT $TMPFILE >> $test_name.log 2>&1
>> diff --git a/tests/m_mmp/expect.1 b/tests/m_mmp/expect.1
>> index a1452e6..9d8a5a3 100644
>> --- a/tests/m_mmp/expect.1
>> +++ b/tests/m_mmp/expect.1
>> @@ -46,6 +46,14 @@ Inode size:	          128
>> Default directory hash:   half_md4
>> MMP block number:         1049
>> MMP update interval:      5
>> +MMP_block:
>> +    mmp_magic: 0x4d4d50
>> +    mmp_check_interval: 5
>> +    mmp_sequence: 0xff4d4d50
>> +    mmp_update_date: test date
>> +    mmp_update_time: test_time
>> +    mmp_node_name: test_node
>> +    mmp_device_name: test.img
>> 
>> 
>> Group 0: (Blocks 0-32767)
>> diff --git a/tests/m_mmp_bad_csum/expect b/tests/m_mmp_bad_csum/expect
>> index e15e7b4..a0678ac 100644
>> --- a/tests/m_mmp_bad_csum/expect
>> +++ b/tests/m_mmp_bad_csum/expect
>> @@ -1,4 +1,7 @@
>> -Superblock MMP block checksum does not match MMP block.  Fix? yes
>> +dumpe2fs: MMP block checksum does not match while trying to open test.img
>> +dumpe2fs: MMP last updated by 'test_node' on test date
>> +Exit status is 1
>> +Superblock MMP block checksum does not match.  Fix? yes
>> 
>> Pass 1: Checking inodes, blocks, and sizes
>> Pass 2: Checking directory structure
>> @@ -7,3 +10,14 @@ Pass 4: Checking reference counts
>> Pass 5: Checking group summary information
>> test_filesys: 11/128 files (0.0% non-contiguous), 19/512 blocks
>> Exit status is 0
>> +dumpe2fs: it is safe to mount 'test.img', MMP is clean
>> +Exit status is 0
>> +MMP_block:
>> +    mmp_magic: 0x4d4d50
>> +    mmp_check_interval: 5
>> +    mmp_sequence: 0xff4d4d50
>> +    mmp_update_date: test date
>> +    mmp_update_time: test_time
>> +    mmp_node_name: test_node
>> +    mmp_device_name: test.img
>> +    mmp_block_number: 8
>> diff --git a/tests/m_mmp_bad_csum/script b/tests/m_mmp_bad_csum/script
>> index 09e870c..4c8fe16 100644
>> --- a/tests/m_mmp_bad_csum/script
>> +++ b/tests/m_mmp_bad_csum/script
>> @@ -12,8 +12,15 @@ gzip -dc < $test_dir/image.gz > $TMPFILE
>> 
>> OUT=$test_name.log
>> EXP=$test_dir/expect
>> -$FSCK -fy $TMPFILE 2>&1 | sed -f $cmd_dir/filter.sed > $OUT
>> +$E2MMPSTATUS $TMPFILE > $OUT 2>&1
>> echo Exit status is $? >> $OUT
>> +$FSCK -fy $TMPFILE >> $OUT 2>&1
>> +echo Exit status is $? >> $OUT
>> +$E2MMPSTATUS $TMPFILE >> $OUT 2>&1
>> +echo Exit status is $? >> $OUT
>> +$E2MMPSTATUS -i $TMPFILE >> $OUT 2>&1
>> +sed -f $cmd_dir/filter.sed $OUT > $OUT.new
>> +mv $OUT.new $OUT
>> 
>> rm -f $TMPFILE
>> cmp -s $OUT $EXP
>> diff --git a/tests/m_mmp_bad_magic/expect b/tests/m_mmp_bad_magic/expect
>> index b5dfb89..d5fa98c 100644
>> --- a/tests/m_mmp_bad_magic/expect
>> +++ b/tests/m_mmp_bad_magic/expect
>> @@ -1,3 +1,5 @@
>> +dumpe2fs: MMP: invalid magic number while trying to open test.img
>> +Exit status is 2
>> Superblock has invalid MMP magic.  Fix? yes
>> 
>> Pass 1: Checking inodes, blocks, and sizes
>> @@ -7,3 +9,14 @@ Pass 4: Checking reference counts
>> Pass 5: Checking group summary information
>> test_filesys: 11/128 files (0.0% non-contiguous), 19/512 blocks
>> Exit status is 0
>> +dumpe2fs: it is safe to mount 'test.img', MMP is clean
>> +Exit status is 0
>> +MMP_block:
>> +    mmp_magic: 0x4d4d50
>> +    mmp_check_interval: 5
>> +    mmp_sequence: 0xff4d4d50
>> +    mmp_update_date: test date
>> +    mmp_update_time: test_time
>> +    mmp_node_name: test_node
>> +    mmp_device_name: test.img
>> +    mmp_block_number: 8
>> diff --git a/tests/m_mmp_bad_magic/script b/tests/m_mmp_bad_magic/script
>> index 09e870c..4c8fe16 100644
>> --- a/tests/m_mmp_bad_magic/script
>> +++ b/tests/m_mmp_bad_magic/script
>> @@ -12,8 +12,15 @@ gzip -dc < $test_dir/image.gz > $TMPFILE
>> 
>> OUT=$test_name.log
>> EXP=$test_dir/expect
>> -$FSCK -fy $TMPFILE 2>&1 | sed -f $cmd_dir/filter.sed > $OUT
>> +$E2MMPSTATUS $TMPFILE > $OUT 2>&1
>> echo Exit status is $? >> $OUT
>> +$FSCK -fy $TMPFILE >> $OUT 2>&1
>> +echo Exit status is $? >> $OUT
>> +$E2MMPSTATUS $TMPFILE >> $OUT 2>&1
>> +echo Exit status is $? >> $OUT
>> +$E2MMPSTATUS -i $TMPFILE >> $OUT 2>&1
>> +sed -f $cmd_dir/filter.sed $OUT > $OUT.new
>> +mv $OUT.new $OUT
>> 
>> rm -f $TMPFILE
>> cmp -s $OUT $EXP
>> diff --git a/tests/test_config b/tests/test_config
>> index cf9c79c..2aee6ff 100644
>> --- a/tests/test_config
>> +++ b/tests/test_config
>> @@ -25,6 +25,7 @@ RESIZE2FS_EXE="../resize/resize2fs"
>> RESIZE2FS="$USE_VALGRIND $RESIZE2FS_EXE"
>> E2UNDO_EXE="../misc/e2undo"
>> E2UNDO="$USE_VALGRIND $E2UNDO_EXE"
>> +E2MMPSTATUS="$USE_VALGRIND ../misc/dumpe2fs -m"
>> TEST_REL=../tests/progs/test_rel
>> TEST_ICOUNT=../tests/progs/test_icount
>> CRCSUM=../tests/progs/crcsum
>> --
>> 1.8.0
>> 


Cheers, Andreas
Lukas Czerner May 11, 2018, noon | #3
On Fri, May 04, 2018 at 12:31:10PM -0600, Andreas Dilger wrote:
> > Subject: [PATCH 10/10] misc: add e2mmpstatus utility via debugfs
> 
> I just noticed that I made an error in the patch summary.  This
> should be "dumpe2fs" rather than "debugfs".
> 
> On May 4, 2018, at 4:06 AM, Lukas Czerner <lczerner@redhat.com> wrote:
> > 
> > On Tue, May 01, 2018 at 10:26:06PM -0600, Andreas Dilger wrote:
> >> From: Shuichi Ihara <sihara@ddn.com>
> >> 
> >> e2mmpstatus is a Multi-Mount Protection (MMP) helper utility to read
> >> an MMP block to see if it is being updated.  It can also output the
> >> latest update time, nodename, and device from the MMP block.
> >> 
> >> This is useful for HA and other maintenance scripts to determine if
> >> the filesystem is in use on another node, and which node it is.
> >> 
> >> Signed-off-by: Shuichi Ihara <sihara@ddn.com>
> >> Signed-off-by: Li Xi <lixi@ddn.com>
> >> Signed-off-by: Wang Shilong <wshilong@ddn.com>
> >> 
> >> Moved e2mmpstatus checking/dumping code to be part of dumpe2fs rather
> >> than a standalone program, using the "-m" option to check MMP status,
> >> and "-i" to dump info.  If dumpe2fs is called as "e2mmpstatus" (and
> >> also "mmpstatus" for compatibility reasons), assume "-m" is specified.
> > 
> > Hmm, why do we need an alias to dumpe2fs options ? For what
> > compatibility you need mmpstatus as well ? I do not like it, see below.
> 
> For compatibility with people who are using the old (standalone) version
> of mmpstatus on their systems.  I'm trying to reduce the delta between
> the Lustre-specific e2fsprogs and upstream, and hopefully one day get rid
> of the Lustre version.  The "e2" part of "e2mmpstatus" name was my change,
> since it otherwise isn't clear that the tool is related to e2fsprogs at
> all, the original tool was named "mmpstatus".  Since the string comparison
> is virtually the same I figured not a big deal to allow both.
> 
> >> @@ -539,7 +627,18 @@ int main (int argc, char ** argv)
> >> 			header_only++;
> >> 			break;
> >> 		case 'i':
> >> -			image_dump++;
> >> +			if (mmp_check)
> >> +				mmp_info++;
> >> +			else
> >> +				image_dump++;
> >> +			break;
> >> +		case 'm':
> >> +			mmp_check++;
> >> +			header_only++;
> >> +			if (image_dump) {
> >> +				mmp_info = image_dump;
> >> +				image_dump = 0;
> >> +			}
> > 
> > That's fugly, I hate it with passion. The whole idea of making a decision
> > based on the binary name changing the meaning of the parameters
> 
> Take a look at mke2fs for a similar example:
> 
>         if (argc && *argv) {
>                 program_name = get_progname(*argv);
> 
>                 /* If called as mkfs.ext3, create a journal inode */
>                 if (!strcmp(program_name, "mkfs.ext3") ||
>                     !strcmp(program_name, "mke3fs"))
>                         journal_size = -1;
>         }
> 
> and the same for tune2fs:
> 
> #ifdef CONFIG_BUILD_FINDFS
>         if (strcmp(get_progname(argv[0]), "findfs") == 0)
>                 do_findfs(argc, argv);
> #endif
>         if (strcmp(get_progname(argv[0]), "e2label") == 0)
>                 parse_e2label_options(argc, argv);
>         else
>                 parse_tune2fs_options(argc, argv);
> 
> and also other utilities, so this isn't wholly unusual:
> 
>     0 lrwxrwxrwx 1 root root          5 Jun 19  2011 bunzip2 -> bzip2*
>     0 lrwxrwxrwx 1 root root          5 Jun 19  2011 bzcat -> bzip2*
> 
> 
> I'd originally submitted e2mmpstatus as a standalone binary, but Ted
> asked me to implement it as part of dumpe2fs so that it would also get
> this useful functionality, and compatibility could still be kept with
> existing deployments.  Another alternative would be to include a one-line
> shell script named e2mmpstatus, I don't have a strong opinion, but I
> figured I'd stick with the same mechanism (argv[0] checking) used already:
> 
> #!/bin/sh
> dumpe2fs -m $*
> 
> > and on top of that changing the meanin of "-i" in case we specify "-m" as
> > well, looks like a horrible mess to me.
> 
> I agree that is a tiny bit messy, but '-i' was the argument to mmpstatus
> to dump MMP info rather than return a pass/fail check of the MMP block.
> I could take the route of e2label vs. tune2fs and have completely separate
> argument parsing based on argv[0] so that dumpe2fs options don't bleed
> over to e2mmpstatus, but I didn't think that was critical for just one
> argument which is unlikely to actually conflict in real life.
> 
> Ted, what is your preference here?
> 
> Cheers, Andreas

I understand, but I still disagree with it being implemented in this
way. the one-line shell script looks like a simpler more elegant
solution to me, that is if we really want to provide e2mmpstatus
mmpstatus executables.

But anyway the rest of the series apart from this one patch looks
good. You can add

Reviewed-by: Lukas Czerner <lczerner@redhat.com>

Thanks!
-Lukas

> 
> > Again, why do we need the mmpstatus alias ? why is dumpe2fs not enough ?
> > 
> > -Lukas
> > 
> >> 			break;
> >> 		case 'o':
> >> 			parse_extended_opts(optarg, &use_superblock,
> >> @@ -557,12 +656,12 @@ int main (int argc, char ** argv)
> >> 			usage();
> >> 		}
> >> 	}
> >> -	if (optind != argc - 1) {
> >> +	if (optind != argc - 1)
> >> 		usage();
> >> -		exit(1);
> >> -	}
> >> +
> >> 	device_name = argv[optind++];
> >> -	flags = EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_64BITS;
> >> +	flags = EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES |
> >> +		EXT2_FLAG_64BITS;
> >> 	if (force)
> >> 		flags |= EXT2_FLAG_FORCE;
> >> 	if (image_dump)
> >> @@ -579,64 +678,87 @@ try_open_again:
> >> 			if (!retval)
> >> 				break;
> >> 		}
> >> -	} else
> >> -		retval = ext2fs_open (device_name, flags, use_superblock,
> >> -				      use_blocksize, unix_io_manager, &fs);
> >> -	if (retval && !(flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
> >> -		flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
> >> -		goto try_open_again;
> >> +	} else {
> >> +		retval = ext2fs_open(device_name, flags, use_superblock,
> >> +				     use_blocksize, unix_io_manager, &fs);
> >> 	}
> >> -	if (!retval && (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS))
> >> -		printf("%s", _("\n*** Checksum errors detected in filesystem!  Run e2fsck now!\n\n"));
> >> 	flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
> >> +	if (retval && !retval_csum) {
> >> +		retval_csum = retval;
> >> +		error_csum = _("while trying to open %s");
> >> +		goto try_open_again;
> >> +	}
> >> 	if (retval) {
> >> -		com_err (program_name, retval, _("while trying to open %s"),
> >> -			 device_name);
> >> +		com_err(program_name, retval, _("while trying to open %s"),
> >> +			device_name);
> >> 		printf("%s", _("Couldn't find valid filesystem superblock.\n"));
> >> 		if (retval == EXT2_ET_BAD_MAGIC)
> >> 			check_plausibility(device_name, CHECK_FS_EXIST, NULL);
> >> -		exit (1);
> >> +		goto out;
> >> 	}
> >> 	fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;
> >> 	if (ext2fs_has_feature_64bit(fs->super))
> >> 		blocks64 = 1;
> >> -	if (print_badblocks) {
> >> +	if (mmp_check) {
> >> +		if (ext2fs_has_feature_mmp(fs->super) &&
> >> +		    fs->super->s_mmp_block != 0) {
> >> +			if (mmp_info) {
> >> +				print_mmp_block(fs);
> >> +				printf("    mmp_block_number: ");
> >> +				print_number(fs->super->s_mmp_block);
> >> +				printf("\n");
> >> +			} else {
> >> +				retval = check_mmp(fs);
> >> +			}
> >> +			if (!retval && retval_csum)
> >> +				retval = 2;
> >> +		} else {
> >> +			fprintf(stderr, _("%s: MMP feature not enabled.\n"),
> >> +				program_name);
> >> +			retval = 2;
> >> +		}
> >> +	} else if (print_badblocks) {
> >> 		list_bad_blocks(fs, 1);
> >> 	} else {
> >> 		if (grp_only)
> >> 			goto just_descriptors;
> >> -		list_super (fs->super);
> >> +		list_super(fs->super);
> >> 		if (ext2fs_has_feature_journal_dev(fs->super)) {
> >> 			print_journal_information(fs);
> >> -			ext2fs_close_free(&fs);
> >> -			exit(0);
> >> +
> >> +			goto out_close;
> >> 		}
> >> 		if (ext2fs_has_feature_journal(fs->super) &&
> >> 		    (fs->super->s_journal_inum != 0))
> >> 			print_inline_journal_information(fs);
> >> +		if (ext2fs_has_feature_mmp(fs->super) &&
> >> +		    fs->super->s_mmp_block != 0)
> >> +			print_mmp_block(fs);
> >> 		list_bad_blocks(fs, 0);
> >> -		if (header_only) {
> >> -			ext2fs_close_free(&fs);
> >> -			exit (0);
> >> -		}
> >> +		if (header_only)
> >> +			goto out_close;
> >> +
> >> 		fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
> >> try_bitmaps_again:
> >> -		retval = ext2fs_read_bitmaps (fs);
> >> -		if (retval && !(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
> >> +		retval = ext2fs_read_bitmaps(fs);
> >> +		if (retval && !retval_csum) {
> >> 			fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
> >> +			retval_csum = retval;
> >> +			error_csum = _("while trying to read '%s' bitmaps\n");
> >> 			goto try_bitmaps_again;
> >> 		}
> >> -		if (!retval && (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS))
> >> -			printf("%s", _("\n*** Checksum errors detected in bitmaps!  Run e2fsck now!\n\n"));
> >> just_descriptors:
> >> 		list_desc(fs, grp_only);
> >> -		if (retval) {
> >> -			printf(_("\n%s: %s: error reading bitmaps: %s\n"),
> >> -			       program_name, device_name,
> >> -			       error_message(retval));
> >> -		}
> >> +	}
> >> +out_close:
> >> +	if (retval_csum) {
> >> +		com_err(program_name, retval_csum, error_csum, device_name);
> >> +		printf("%s", _("*** Run e2fsck now!\n\n"));
> >> +		if (!retval)
> >> +			retval = retval_csum;
> >> 	}
> >> 	ext2fs_close_free(&fs);
> >> 	remove_error_table(&et_ext2_error_table);
> >> -	exit (0);
> >> +out:
> >> +	return retval;
> >> }
> >> diff --git a/misc/e2mmpstatus.8.in b/misc/e2mmpstatus.8.in
> >> new file mode 100644
> >> index 0000000..f7d9557
> >> --- /dev/null
> >> +++ b/misc/e2mmpstatus.8.in
> >> @@ -0,0 +1,59 @@
> >> +.\" -*- nroff -*-
> >> +.\" This file may be copied under the terms of the GNU Public License.
> >> +.\"
> >> +.TH E2MMPSTATUS 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
> >> +.SH NAME
> >> +e2mmpstatus \- Check MMP status of an ext4 filesystem
> >> +.SH SYNOPSIS
> >> +.BR e2mmpstatus " [" \-i ]
> >> +.RI < filesystem >
> >> +.SH OPTIONS
> >> +.TP
> >> +.B \-i
> >> +prints out the MMP information rather than check it.
> >> +.SH DESCRIPTION
> >> +.B e2mmpstatus
> >> +is used to check Multiple-Mount Protection (MMP) status of an ext4
> >> +filesystem with the
> >> +.B mmp
> >> +feature enabled.  The specified
> >> +.I filesystem
> >> +can be a device name (e.g.
> >> +.IR /dev/hdc1 ", " /dev/sdb2 ),
> >> +or an ext4 filesystem label or UUID, for example
> >> +.B UUID=8868abf6-88c5-4a83-98b8-bfc24057f7bd
> >> +or
> >> +.BR LABEL=root .
> >> +By default, the
> >> +.B e2mmpstatus
> >> +program checks whether it is safe to mount the filesystem without taking
> >> +the risk of mounting it more than once.
> >> +.PP
> >> +MMP (multiple-mount protection) is a feature that adds protection against
> >> +the filesystem being modified simultaneously by more than one node.
> >> +It is NOT safe to mount a filesystem when one of the following conditions
> >> +is true:
> >> +.br
> >> +\	1. e2fsck is running on the filesystem.
> >> +.br
> >> +\	2. the filesystem is in use by another node.
> >> +.br
> >> +\	3. The MMP block is corrupted or cannot be read for some reason.
> >> +.br
> >> +The
> >> +.B e2mmpstatus
> >> +program might wait for some time to see whether the MMP block is being
> >> +updated by any node during this period.  The time taken depends on how
> >> +frequently the MMP block is being written by the other node.
> >> +.SH EXIT CODE
> >> +The exit code returned by
> >> +.B e2mmpstatus
> >> +is 0 when it is safe to mount the filesystem, 1 when the MMP block shows
> >> +the filesystem is in use on another node and it is NOT safe to mount
> >> +the filesystem, and 2 if some other failure occurred that prevents the
> >> +check from properly detecting the current MMP status.
> >> +.SH SEE ALSO
> >> +.BR dumpe2fs (8),
> >> +.BR e2fsck (8),
> >> +.BR fstab (5),
> >> +.BR fsck (8),
> >> diff --git a/tests/f_mmp/script b/tests/f_mmp/script
> >> index 9ff16c9..07ae232 100644
> >> --- a/tests/f_mmp/script
> >> +++ b/tests/f_mmp/script
> >> @@ -43,6 +43,13 @@ rm -f $MARKFILE
> >> echo "kill debugfs abruptly (simulates e2fsck failure) ..." >> $test_name.log
> >> kill_debugfs
> >> 
> >> +$E2MMPSTATUS $TMPFILE > $test_name.log 2>&1
> >> +status=$?
> >> +if [ "$status" != 1 ] ; then
> >> +	echo "$E2MMPSTATUS with EXT2_MMP_SEQ_FSCK passed!" > $test_name.failed
> >> +	echo "$test_name: $test_description: failed"
> >> +	return 1
> >> +fi
> >> 
> >> echo "e2fsck (should fail mmp_seq = EXT2_MMP_SEQ_FSCK) ..." >> $test_name.log
> >> $FSCK $FSCK_OPT $TMPFILE >> $test_name.log 2>&1
> >> diff --git a/tests/m_mmp/expect.1 b/tests/m_mmp/expect.1
> >> index a1452e6..9d8a5a3 100644
> >> --- a/tests/m_mmp/expect.1
> >> +++ b/tests/m_mmp/expect.1
> >> @@ -46,6 +46,14 @@ Inode size:	          128
> >> Default directory hash:   half_md4
> >> MMP block number:         1049
> >> MMP update interval:      5
> >> +MMP_block:
> >> +    mmp_magic: 0x4d4d50
> >> +    mmp_check_interval: 5
> >> +    mmp_sequence: 0xff4d4d50
> >> +    mmp_update_date: test date
> >> +    mmp_update_time: test_time
> >> +    mmp_node_name: test_node
> >> +    mmp_device_name: test.img
> >> 
> >> 
> >> Group 0: (Blocks 0-32767)
> >> diff --git a/tests/m_mmp_bad_csum/expect b/tests/m_mmp_bad_csum/expect
> >> index e15e7b4..a0678ac 100644
> >> --- a/tests/m_mmp_bad_csum/expect
> >> +++ b/tests/m_mmp_bad_csum/expect
> >> @@ -1,4 +1,7 @@
> >> -Superblock MMP block checksum does not match MMP block.  Fix? yes
> >> +dumpe2fs: MMP block checksum does not match while trying to open test.img
> >> +dumpe2fs: MMP last updated by 'test_node' on test date
> >> +Exit status is 1
> >> +Superblock MMP block checksum does not match.  Fix? yes
> >> 
> >> Pass 1: Checking inodes, blocks, and sizes
> >> Pass 2: Checking directory structure
> >> @@ -7,3 +10,14 @@ Pass 4: Checking reference counts
> >> Pass 5: Checking group summary information
> >> test_filesys: 11/128 files (0.0% non-contiguous), 19/512 blocks
> >> Exit status is 0
> >> +dumpe2fs: it is safe to mount 'test.img', MMP is clean
> >> +Exit status is 0
> >> +MMP_block:
> >> +    mmp_magic: 0x4d4d50
> >> +    mmp_check_interval: 5
> >> +    mmp_sequence: 0xff4d4d50
> >> +    mmp_update_date: test date
> >> +    mmp_update_time: test_time
> >> +    mmp_node_name: test_node
> >> +    mmp_device_name: test.img
> >> +    mmp_block_number: 8
> >> diff --git a/tests/m_mmp_bad_csum/script b/tests/m_mmp_bad_csum/script
> >> index 09e870c..4c8fe16 100644
> >> --- a/tests/m_mmp_bad_csum/script
> >> +++ b/tests/m_mmp_bad_csum/script
> >> @@ -12,8 +12,15 @@ gzip -dc < $test_dir/image.gz > $TMPFILE
> >> 
> >> OUT=$test_name.log
> >> EXP=$test_dir/expect
> >> -$FSCK -fy $TMPFILE 2>&1 | sed -f $cmd_dir/filter.sed > $OUT
> >> +$E2MMPSTATUS $TMPFILE > $OUT 2>&1
> >> echo Exit status is $? >> $OUT
> >> +$FSCK -fy $TMPFILE >> $OUT 2>&1
> >> +echo Exit status is $? >> $OUT
> >> +$E2MMPSTATUS $TMPFILE >> $OUT 2>&1
> >> +echo Exit status is $? >> $OUT
> >> +$E2MMPSTATUS -i $TMPFILE >> $OUT 2>&1
> >> +sed -f $cmd_dir/filter.sed $OUT > $OUT.new
> >> +mv $OUT.new $OUT
> >> 
> >> rm -f $TMPFILE
> >> cmp -s $OUT $EXP
> >> diff --git a/tests/m_mmp_bad_magic/expect b/tests/m_mmp_bad_magic/expect
> >> index b5dfb89..d5fa98c 100644
> >> --- a/tests/m_mmp_bad_magic/expect
> >> +++ b/tests/m_mmp_bad_magic/expect
> >> @@ -1,3 +1,5 @@
> >> +dumpe2fs: MMP: invalid magic number while trying to open test.img
> >> +Exit status is 2
> >> Superblock has invalid MMP magic.  Fix? yes
> >> 
> >> Pass 1: Checking inodes, blocks, and sizes
> >> @@ -7,3 +9,14 @@ Pass 4: Checking reference counts
> >> Pass 5: Checking group summary information
> >> test_filesys: 11/128 files (0.0% non-contiguous), 19/512 blocks
> >> Exit status is 0
> >> +dumpe2fs: it is safe to mount 'test.img', MMP is clean
> >> +Exit status is 0
> >> +MMP_block:
> >> +    mmp_magic: 0x4d4d50
> >> +    mmp_check_interval: 5
> >> +    mmp_sequence: 0xff4d4d50
> >> +    mmp_update_date: test date
> >> +    mmp_update_time: test_time
> >> +    mmp_node_name: test_node
> >> +    mmp_device_name: test.img
> >> +    mmp_block_number: 8
> >> diff --git a/tests/m_mmp_bad_magic/script b/tests/m_mmp_bad_magic/script
> >> index 09e870c..4c8fe16 100644
> >> --- a/tests/m_mmp_bad_magic/script
> >> +++ b/tests/m_mmp_bad_magic/script
> >> @@ -12,8 +12,15 @@ gzip -dc < $test_dir/image.gz > $TMPFILE
> >> 
> >> OUT=$test_name.log
> >> EXP=$test_dir/expect
> >> -$FSCK -fy $TMPFILE 2>&1 | sed -f $cmd_dir/filter.sed > $OUT
> >> +$E2MMPSTATUS $TMPFILE > $OUT 2>&1
> >> echo Exit status is $? >> $OUT
> >> +$FSCK -fy $TMPFILE >> $OUT 2>&1
> >> +echo Exit status is $? >> $OUT
> >> +$E2MMPSTATUS $TMPFILE >> $OUT 2>&1
> >> +echo Exit status is $? >> $OUT
> >> +$E2MMPSTATUS -i $TMPFILE >> $OUT 2>&1
> >> +sed -f $cmd_dir/filter.sed $OUT > $OUT.new
> >> +mv $OUT.new $OUT
> >> 
> >> rm -f $TMPFILE
> >> cmp -s $OUT $EXP
> >> diff --git a/tests/test_config b/tests/test_config
> >> index cf9c79c..2aee6ff 100644
> >> --- a/tests/test_config
> >> +++ b/tests/test_config
> >> @@ -25,6 +25,7 @@ RESIZE2FS_EXE="../resize/resize2fs"
> >> RESIZE2FS="$USE_VALGRIND $RESIZE2FS_EXE"
> >> E2UNDO_EXE="../misc/e2undo"
> >> E2UNDO="$USE_VALGRIND $E2UNDO_EXE"
> >> +E2MMPSTATUS="$USE_VALGRIND ../misc/dumpe2fs -m"
> >> TEST_REL=../tests/progs/test_rel
> >> TEST_ICOUNT=../tests/progs/test_icount
> >> CRCSUM=../tests/progs/crcsum
> >> --
> >> 1.8.0
> >> 
> 
> 
> Cheers, Andreas
> 
> 
> 
> 
>

Patch

diff --git a/.gitignore b/.gitignore
index ac5c2c1..3352ccc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -170,6 +170,8 @@  misc/e2image
 misc/e2image.8
 misc/e2initrd_helper
 misc/e2label.8
+misc/e2mmpstatus
+misc/e2mmpstatus.8
 misc/e2undo
 misc/e2undo.8
 misc/e4crypt
diff --git a/configure b/configure
index b2701d2..c7853d1 100755
--- a/configure
+++ b/configure
@@ -13097,7 +13097,7 @@  fi
 if test -n "$DLOPEN_LIB" ; then
    ac_cv_func_dlopen=yes
 fi
-for ac_func in  	__secure_getenv 	add_key 	backtrace 	blkid_probe_get_topology 	blkid_probe_enable_partitions 	chflags 	dlopen 	fadvise64 	fallocate 	fallocate64 	fchown 	fcntl 	fdatasync 	fstat64 	fsync 	ftruncate64 	futimes 	getcwd 	getdtablesize 	getmntinfo 	getpwuid_r 	getrlimit 	getrusage 	jrand48 	keyctl 	llistxattr 	llseek 	lseek64 	mallinfo 	mbstowcs 	memalign 	mempcpy 	mmap 	msync 	nanosleep 	open64 	pathconf 	posix_fadvise 	posix_fadvise64 	posix_memalign 	prctl 	pread 	pwrite 	pread64 	pwrite64 	secure_getenv 	setmntent 	setresgid 	setresuid 	snprintf 	srandom 	stpcpy 	strcasecmp 	strdup 	strnlen 	strptime 	strtoull 	sync_file_range 	sysconf 	usleep 	utime 	utimes 	valloc
+for ac_func in  	__secure_getenv 	add_key 	backtrace 	blkid_probe_get_topology 	blkid_probe_enable_partitions 	chflags 	dlopen 	fadvise64 	fallocate 	fallocate64 	fchown 	fcntl 	fdatasync 	fstat64 	fsync 	ftruncate64 	futimes 	getcwd 	getdtablesize 	gethostname 	getmntinfo 	getpwuid_r 	getrlimit 	getrusage 	jrand48 	keyctl 	llistxattr 	llseek 	lseek64 	mallinfo 	mbstowcs 	memalign 	mempcpy 	mmap 	msync 	nanosleep 	open64 	pathconf 	posix_fadvise 	posix_fadvise64 	posix_memalign 	prctl 	pread 	pwrite 	pread64 	pwrite64 	secure_getenv 	setmntent 	setresgid 	setresuid 	snprintf 	srandom 	stpcpy 	strcasecmp 	strdup 	strnlen 	strptime 	strtoull 	sync_file_range 	sysconf 	usleep 	utime 	utimes 	valloc
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/configure.ac b/configure.ac
index 7392959..5e837c9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1124,6 +1124,7 @@  AC_CHECK_FUNCS(m4_flatten([
 	futimes
 	getcwd
 	getdtablesize
+	gethostname
 	getmntinfo
 	getpwuid_r
 	getrlimit
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 5269650..d151bfd 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -634,7 +634,7 @@  extern blk64_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
 			   const char *name, io_manager manager);
 extern int ext2_file_type(unsigned int mode);
 extern int write_all(int fd, char *buf, size_t count);
-void dump_mmp_msg(struct mmp_struct *mmp, const char *msg);
+void dump_mmp_msg(struct mmp_struct *mmp, const char *fmt, ...);
 errcode_t e2fsck_mmp_update(ext2_filsys fs);
 
 extern void e2fsck_set_bitmap_type(ext2_filsys fs,
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index ff289b4..be79fdb 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -450,7 +450,7 @@  static struct e2fsck_problem problem_table[] = {
 
 	/* Superblock MMP block checksum does not match MMP block. */
 	{ PR_0_MMP_CSUM_INVALID,
-	  N_("@S MMP @b checksum does not match MMP @b.  "),
+	  N_("@S MMP @b checksum does not match.  "),
 	  PROMPT_FIX, PR_PREEN_OK | PR_NO_OK},
 
 	/* Superblock 64bit filesystem needs extents to access the whole disk */
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index cbe5ec5..beeaed2 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1250,7 +1250,8 @@  check_error:
 		dump_mmp_msg(fs->mmp_buf,
 			     _("If you are sure the filesystem is not "
 			       "in use on any node, run:\n"
-			       "'tune2fs -f -E clear_mmp {device}'\n"));
+			       "'tune2fs -f -E clear_mmp %s'\n"),
+			     ctx->device_name);
 	} else if (retval == EXT2_ET_MMP_MAGIC_INVALID) {
 		if (fix_problem(ctx, PR_0_MMP_INVALID_MAGIC, &pctx)) {
 			ext2fs_mmp_clear(fs);
diff --git a/e2fsck/util.c b/e2fsck/util.c
index ed94702..5793b65 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -756,16 +756,28 @@  int write_all(int fd, char *buf, size_t count)
 	return c;
 }
 
-void dump_mmp_msg(struct mmp_struct *mmp, const char *msg)
+void dump_mmp_msg(struct mmp_struct *mmp, const char *fmt, ...)
 {
+	va_list pvar;
 
-	if (msg)
-		printf("MMP check failed: %s\n", msg);
+	if (fmt) {
+		printf("MMP check failed: ");
+		va_start(pvar, fmt);
+		vprintf(fmt, pvar);
+		va_end(pvar);
+	}
 	if (mmp) {
 		time_t t = mmp->mmp_time;
 
-		printf("MMP error info: last update: %s node: %s device: %s\n",
-		       ctime(&t), mmp->mmp_nodename, mmp->mmp_bdevname);
+		printf("MMP_block:\n");
+		printf("    mmp_magic: 0x%x\n", mmp->mmp_magic);
+		printf("    mmp_check_interval: %d\n",
+		       mmp->mmp_check_interval);
+		printf("    mmp_sequence: %08x\n", mmp->mmp_seq);
+		printf("    mmp_update_date: %s", ctime(&t));
+		printf("    mmp_update_time: %lld\n", mmp->mmp_time);
+		printf("    mmp_node_name: %s\n", mmp->mmp_nodename);
+		printf("    mmp_device_name: %s\n", mmp->mmp_bdevname);
 	}
 }
 
diff --git a/e2fsprogs.spec.in b/e2fsprogs.spec.in
index b188b75..f42c4be 100644
--- a/e2fsprogs.spec.in
+++ b/e2fsprogs.spec.in
@@ -116,6 +116,7 @@  exit 0
 %{_root_sbindir}/e2fsck
 %{_root_sbindir}/e2image
 %{_root_sbindir}/e2label
+%{_root_sbindir}/e2mmpstatus
 %{_root_sbindir}/e2undo
 %{_root_sbindir}/findfs
 %{_root_sbindir}/fsck
@@ -167,6 +168,7 @@  exit 0
 %{_mandir}/man8/fsck.ext4dev.8*
 %{_mandir}/man8/e2image.8*
 %{_mandir}/man8/e2label.8*
+%{_mandir}/man8/e2mmpstatus.8*
 %{_mandir}/man8/e2undo.8*
 %{_mandir}/man8/fsck.8*
 %{_mandir}/man8/logsave.8*
diff --git a/lib/config.h.in b/lib/config.h.in
index 9cc0793..67a0548 100644
--- a/lib/config.h.in
+++ b/lib/config.h.in
@@ -147,6 +147,9 @@ 
 /* Define to 1 if you have the `fchown' function. */
 #undef HAVE_FCHOWN
 
+/* Define to 1 if you have the `fcntl' function. */
+#undef HAVE_FCNTL
+
 /* Define to 1 if you have the `fdatasync' function. */
 #undef HAVE_FDATASYNC
 
@@ -156,6 +159,9 @@ 
 /* Define to 1 if you have the `fstat64' function. */
 #undef HAVE_FSTAT64
 
+/* Define to 1 if you have the `fsync' function. */
+#undef HAVE_FSYNC
+
 /* Define to 1 if you have the `ftruncate64' function. */
 #undef HAVE_FTRUNCATE64
 
@@ -183,6 +189,9 @@ 
 /* Define to 1 if you have the `getgid' function. */
 #undef HAVE_GETGID
 
+/* Define to 1 if you have the `gethostname' function. */
+#undef HAVE_GETHOSTNAME
+
 /* Define to 1 if you have the `getmntinfo' function. */
 #undef HAVE_GETMNTINFO
 
@@ -253,6 +262,9 @@ 
 /* Define to 1 if you have the <linux/major.h> header file. */
 #undef HAVE_LINUX_MAJOR_H
 
+/* Define to 1 if you have the <linux/types.h> header file. */
+#undef HAVE_LINUX_TYPES_H
+
 /* Define to 1 if you have the `llistxattr' function. */
 #undef HAVE_LLISTXATTR
 
@@ -470,9 +482,6 @@ 
 /* Define to 1 if you have the `sync_file_range' function. */
 #undef HAVE_SYNC_FILE_RANGE
 
-/* Define to 1 if you have the 'fsync' function. */
-#undef HAVE_FSYNC
-
 /* Define to 1 if you have the `sysconf' function. */
 #undef HAVE_SYSCONF
 
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 16abd23..b2ba71a 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -429,7 +429,7 @@  ec	EXT2_ET_MMP_FAILED,
 	"MMP: device currently active"
 
 ec	EXT2_ET_MMP_FSCK_ON,
-	"MMP: fsck being run"
+	"MMP: e2fsck being run"
 
 ec	EXT2_ET_MMP_BAD_BLOCK,
 	"MMP: block number beyond filesystem range"
@@ -471,7 +471,7 @@  ec	EXT2_ET_UNKNOWN_CSUM,
 	"Unknown checksum algorithm"
 
 ec	EXT2_ET_MMP_CSUM_INVALID,
-	"MMP block checksum does not match MMP block"
+	"MMP block checksum does not match"
 
 ec	EXT2_ET_FILE_EXISTS,
 	"Ext2 file already exists"
diff --git a/lib/ext2fs/mmp.c b/lib/ext2fs/mmp.c
index 9a771de..0cf0d0d 100644
--- a/lib/ext2fs/mmp.c
+++ b/lib/ext2fs/mmp.c
@@ -194,7 +194,7 @@  static errcode_t ext2fs_mmp_reset(ext2_filsys fs)
 	mmp_s->mmp_magic = EXT4_MMP_MAGIC;
 	mmp_s->mmp_seq = EXT4_MMP_SEQ_CLEAN;
 	mmp_s->mmp_time = 0;
-#if _BSD_SOURCE || _XOPEN_SOURCE >= 500
+#ifdef HAVE_GETHOSTNAME
 	gethostname(mmp_s->mmp_nodename, sizeof(mmp_s->mmp_nodename));
 #else
 	mmp_s->mmp_nodename[0] = '\0';
@@ -269,6 +269,10 @@  out:
 #endif
 }
 
+#ifndef min
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
 /*
  * Make sure that the fs is not mounted or being fsck'ed while opening the fs.
  */
@@ -316,7 +320,7 @@  errcode_t ext2fs_mmp_start(ext2_filsys fs)
 	if (mmp_s->mmp_check_interval > mmp_check_interval)
 		mmp_check_interval = mmp_s->mmp_check_interval;
 
-	sleep(2 * mmp_check_interval + 1);
+	sleep(min(mmp_check_interval * 2 + 1, mmp_check_interval + 60));
 
 	retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
 	if (retval)
@@ -332,7 +336,7 @@  clean_seq:
 		goto mmp_error;
 
 	mmp_s->mmp_seq = seq = ext2fs_mmp_new_seq();
-#if _BSD_SOURCE || _XOPEN_SOURCE >= 500
+#ifdef HAVE_GETHOSTNAME
 	gethostname(mmp_s->mmp_nodename, sizeof(mmp_s->mmp_nodename));
 #else
 	strcpy(mmp_s->mmp_nodename, "unknown host");
@@ -344,7 +348,7 @@  clean_seq:
 	if (retval)
 		goto mmp_error;
 
-	sleep(2 * mmp_check_interval + 1);
+	sleep(min(2 * mmp_check_interval + 1, mmp_check_interval + 60));
 
 	retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
 	if (retval)
diff --git a/misc/Makefile.in b/misc/Makefile.in
index efc0e0b..f500276 100644
--- a/misc/Makefile.in
+++ b/misc/Makefile.in
@@ -39,7 +39,8 @@  USPROGS=	mklost+found filefrag e2freefrag $(UUIDD_PROG) \
 SMANPAGES=	tune2fs.8 mklost+found.8 mke2fs.8 dumpe2fs.8 badblocks.8 \
 			e2label.8 $(FINDFS_MAN) $(BLKID_MAN) $(E2IMAGE_MAN) \
 			logsave.8 filefrag.8 e2freefrag.8 e2undo.8 \
-			$(UUIDD_MAN) $(E4DEFRAG_MAN) $(E4CRYPT_MAN) @FSCK_MAN@
+			$(UUIDD_MAN) $(E4DEFRAG_MAN) $(E4CRYPT_MAN) @FSCK_MAN@ \
+			e2mmpstatus.8
 FMANPAGES=	mke2fs.conf.5 ext4.5
 
 UPROGS=		chattr lsattr @UUID_CMT@ uuidgen
@@ -475,6 +476,10 @@  dumpe2fs.8: $(DEP_SUBSTITUTE) $(srcdir)/dumpe2fs.8.in
 	$(E) "	SUBST $@"
 	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/dumpe2fs.8.in dumpe2fs.8
 
+e2mmpstatus.8: $(DEP_SUBSTITUTE) $(srcdir)/e2mmpstatus.8.in
+	$(E) "	SUBST $@"
+	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2mmpstatus.8.in e2mmpstatus.8
+
 badblocks.8: $(DEP_SUBSTITUTE) $(srcdir)/badblocks.8.in
 	$(E) "	SUBST $@"
 	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/badblocks.8.in badblocks.8	
@@ -546,6 +551,8 @@  install: all $(SMANPAGES) $(UMANPAGES) installdirs
 			$(LN) $(LINK_INSTALL_FLAGS) mke2fs mkfs.$$i); \
 	done
 	$(Q) (cd $(DESTDIR)$(root_sbindir); \
+		$(LN) $(LINK_INSTALL_FLAGS) dumpe2fs e2mmpstatus)
+	$(Q) (cd $(DESTDIR)$(root_sbindir); \
 		$(LN) $(LINK_INSTALL_FLAGS) tune2fs e2label)
 	$(Q) if test -n "$(FINDFS_LINK)"; then \
 		$(ES) "	LINK $(root_sbindir)/findfs"; \
@@ -661,7 +668,7 @@  uninstall:
 	for i in $(UMANPAGES); do \
 		$(RM) -f $(DESTDIR)$(man1dir)/$$i; \
 	done
-	for i in $(FINDFS_LINK) e2label ; do \
+	for i in $(FINDFS_LINK) e2label e2mmpstatus ; do \
 		$(RM) -f $(DESTDIR)$(root_sbindir)/$$i; \
 	done
 	for i in $(FMANPAGES); do \
diff --git a/misc/dumpe2fs.8.in b/misc/dumpe2fs.8.in
index da78d4f..ce3214f 100644
--- a/misc/dumpe2fs.8.in
+++ b/misc/dumpe2fs.8.in
@@ -69,6 +69,17 @@  using
 .I device
 as the pathname to the image file.
 .TP
+.B \-m
+If the
+.B mmp
+feature is enabled on the filesystem, check if
+.I device
+is in use by another node, see
+.BR e2mmpstatus (8)
+for full details.  If used together with the
+.B \-i
+option, only the MMP block information is printed.
+.TP
 .B \-x
 print the detailed group information block numbers in hexadecimal format
 .TP
@@ -76,8 +87,16 @@  print the detailed group information block numbers in hexadecimal format
 print the version number of
 .B dumpe2fs
 and exit.
+.SH EXIT CODE
+.B dumpe2fs
+exits with a return code of 0 if the operation completed without errors.
+It will exit with a non-zero return code if there are any errors, such
+as problems reading a valid superblock, bad checksums, or if the device
+is in use by another node and
+.B -m
+is specified.
 .SH BUGS
-You need to know the physical filesystem structure to understand the
+You may need to know the physical filesystem structure to understand the
 output.
 .SH AUTHOR
 .B dumpe2fs
@@ -89,6 +108,7 @@  is part of the e2fsprogs package and is available from
 http://e2fsprogs.sourceforge.net.
 .SH SEE ALSO
 .BR e2fsck (8),
+.BR e2mmpstatus (8),
 .BR mke2fs (8),
 .BR tune2fs (8).
 .BR ext4 (5)
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 395ea9e..384ce92 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -53,7 +53,7 @@  static int blocks64 = 0;
 
 static void usage(void)
 {
-	fprintf(stderr, _("Usage: %s [-bfghixV] [-o superblock=<num>] "
+	fprintf(stderr, _("Usage: %s [-bfghimxV] [-o superblock=<num>] "
 		 "[-o blocksize=<num>] device\n"), program_name);
 	exit(1);
 }
@@ -420,6 +420,79 @@  static void print_journal_information(ext2_filsys fs)
 	e2p_list_journal_super(stdout, buf, fs->blocksize, 0);
 }
 
+static int check_mmp(ext2_filsys fs)
+{
+	int retval;
+
+	/* This won't actually start MMP on the filesystem, since fs is opened
+	 * readonly, but it will do the proper activity checking for us. */
+	retval = ext2fs_mmp_start(fs);
+	if (retval) {
+		com_err(program_name, retval, _("while trying to open %s"),
+			fs->device_name);
+		if (retval == EXT2_ET_MMP_FAILED ||
+		    retval == EXT2_ET_MMP_FSCK_ON ||
+		    retval == EXT2_ET_MMP_CSUM_INVALID ||
+		    retval == EXT2_ET_MMP_UNKNOWN_SEQ) {
+			if (fs->mmp_buf) {
+				struct mmp_struct *mmp = fs->mmp_buf;
+				time_t mmp_time = mmp->mmp_time;
+
+				fprintf(stderr,
+					"%s: MMP last updated by '%s' on %s",
+					program_name, mmp->mmp_nodename,
+					ctime(&mmp_time));
+			}
+			retval = 1;
+		} else {
+			retval = 2;
+		}
+	} else {
+		printf("%s: it is safe to mount '%s', MMP is clean\n",
+		       program_name, fs->device_name);
+	}
+
+	return retval;
+}
+
+static void print_mmp_block(ext2_filsys fs)
+{
+	struct mmp_struct *mmp;
+	time_t mmp_time;
+	errcode_t retval;
+
+	if (fs->mmp_buf == NULL) {
+		retval = ext2fs_get_mem(fs->blocksize, &fs->mmp_buf);
+		if (retval) {
+			com_err(program_name, retval,
+				_("failed to alloc MMP buffer\n"));
+			return;
+		}
+	}
+
+	retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
+	/* this is only dumping, not checking status, so OK to skip this */
+	if (retval == EXT2_ET_OP_NOT_SUPPORTED)
+		return;
+	if (retval) {
+		com_err(program_name, retval,
+			_("reading MMP block %llu from '%s'\n"),
+			fs->super->s_mmp_block, fs->device_name);
+		return;
+	}
+
+	mmp = fs->mmp_buf;
+	mmp_time = mmp->mmp_time;
+	printf("MMP_block:\n");
+	printf("    mmp_magic: 0x%x\n", mmp->mmp_magic);
+	printf("    mmp_check_interval: %d\n", mmp->mmp_check_interval);
+	printf("    mmp_sequence: %#08x\n", mmp->mmp_seq);
+	printf("    mmp_update_date: %s", ctime(&mmp_time));
+	printf("    mmp_update_time: %lld\n", mmp->mmp_time);
+	printf("    mmp_node_name: %s\n", mmp->mmp_nodename);
+	printf("    mmp_device_name: %s\n", mmp->mmp_bdevname);
+}
+
 static void parse_extended_opts(const char *opts, blk64_t *superblock,
 				int *blocksize)
 {
@@ -500,11 +573,15 @@  static void parse_extended_opts(const char *opts, blk64_t *superblock,
 int main (int argc, char ** argv)
 {
 	errcode_t	retval;
+	errcode_t	retval_csum = 0;
+	const char	*error_csum = NULL;
 	ext2_filsys	fs;
 	int		print_badblocks = 0;
 	blk64_t		use_superblock = 0;
 	int		use_blocksize = 0;
 	int		image_dump = 0;
+	int		mmp_check = 0;
+	int		mmp_info = 0;
 	int		force = 0;
 	int		flags;
 	int		header_only = 0;
@@ -519,12 +596,23 @@  int main (int argc, char ** argv)
 	set_com_err_gettext(gettext);
 #endif
 	add_error_table(&et_ext2_error_table);
-	fprintf (stderr, "dumpe2fs %s (%s)\n", E2FSPROGS_VERSION,
-		 E2FSPROGS_DATE);
-	if (argc && *argv)
-		program_name = *argv;
+	if (argc && *argv) {
+		if (strrchr(*argv, '/'))
+			program_name = strrchr(*argv, '/') + 1;
+		else
+			program_name = *argv;
+
+		if (strstr(program_name, "mmpstatus") != NULL) {
+			mmp_check = 1;
+			header_only = 1;
+		}
+	}
 
-	while ((c = getopt(argc, argv, "bfghixVo:")) != EOF) {
+	if (!mmp_check)
+		fprintf(stderr, "dumpe2fs %s (%s)\n", E2FSPROGS_VERSION,
+			E2FSPROGS_DATE);
+
+	while ((c = getopt(argc, argv, "bfghimxVo:")) != EOF) {
 		switch (c) {
 		case 'b':
 			print_badblocks++;
@@ -539,7 +627,18 @@  int main (int argc, char ** argv)
 			header_only++;
 			break;
 		case 'i':
-			image_dump++;
+			if (mmp_check)
+				mmp_info++;
+			else
+				image_dump++;
+			break;
+		case 'm':
+			mmp_check++;
+			header_only++;
+			if (image_dump) {
+				mmp_info = image_dump;
+				image_dump = 0;
+			}
 			break;
 		case 'o':
 			parse_extended_opts(optarg, &use_superblock,
@@ -557,12 +656,12 @@  int main (int argc, char ** argv)
 			usage();
 		}
 	}
-	if (optind != argc - 1) {
+	if (optind != argc - 1)
 		usage();
-		exit(1);
-	}
+
 	device_name = argv[optind++];
-	flags = EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_64BITS;
+	flags = EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES |
+		EXT2_FLAG_64BITS;
 	if (force)
 		flags |= EXT2_FLAG_FORCE;
 	if (image_dump)
@@ -579,64 +678,87 @@  try_open_again:
 			if (!retval)
 				break;
 		}
-	} else
-		retval = ext2fs_open (device_name, flags, use_superblock,
-				      use_blocksize, unix_io_manager, &fs);
-	if (retval && !(flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
-		flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
-		goto try_open_again;
+	} else {
+		retval = ext2fs_open(device_name, flags, use_superblock,
+				     use_blocksize, unix_io_manager, &fs);
 	}
-	if (!retval && (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS))
-		printf("%s", _("\n*** Checksum errors detected in filesystem!  Run e2fsck now!\n\n"));
 	flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+	if (retval && !retval_csum) {
+		retval_csum = retval;
+		error_csum = _("while trying to open %s");
+		goto try_open_again;
+	}
 	if (retval) {
-		com_err (program_name, retval, _("while trying to open %s"),
-			 device_name);
+		com_err(program_name, retval, _("while trying to open %s"),
+			device_name);
 		printf("%s", _("Couldn't find valid filesystem superblock.\n"));
 		if (retval == EXT2_ET_BAD_MAGIC)
 			check_plausibility(device_name, CHECK_FS_EXIST, NULL);
-		exit (1);
+		goto out;
 	}
 	fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;
 	if (ext2fs_has_feature_64bit(fs->super))
 		blocks64 = 1;
-	if (print_badblocks) {
+	if (mmp_check) {
+		if (ext2fs_has_feature_mmp(fs->super) &&
+		    fs->super->s_mmp_block != 0) {
+			if (mmp_info) {
+				print_mmp_block(fs);
+				printf("    mmp_block_number: ");
+				print_number(fs->super->s_mmp_block);
+				printf("\n");
+			} else {
+				retval = check_mmp(fs);
+			}
+			if (!retval && retval_csum)
+				retval = 2;
+		} else {
+			fprintf(stderr, _("%s: MMP feature not enabled.\n"),
+				program_name);
+			retval = 2;
+		}
+	} else if (print_badblocks) {
 		list_bad_blocks(fs, 1);
 	} else {
 		if (grp_only)
 			goto just_descriptors;
-		list_super (fs->super);
+		list_super(fs->super);
 		if (ext2fs_has_feature_journal_dev(fs->super)) {
 			print_journal_information(fs);
-			ext2fs_close_free(&fs);
-			exit(0);
+
+			goto out_close;
 		}
 		if (ext2fs_has_feature_journal(fs->super) &&
 		    (fs->super->s_journal_inum != 0))
 			print_inline_journal_information(fs);
+		if (ext2fs_has_feature_mmp(fs->super) &&
+		    fs->super->s_mmp_block != 0)
+			print_mmp_block(fs);
 		list_bad_blocks(fs, 0);
-		if (header_only) {
-			ext2fs_close_free(&fs);
-			exit (0);
-		}
+		if (header_only)
+			goto out_close;
+
 		fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
 try_bitmaps_again:
-		retval = ext2fs_read_bitmaps (fs);
-		if (retval && !(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
+		retval = ext2fs_read_bitmaps(fs);
+		if (retval && !retval_csum) {
 			fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+			retval_csum = retval;
+			error_csum = _("while trying to read '%s' bitmaps\n");
 			goto try_bitmaps_again;
 		}
-		if (!retval && (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS))
-			printf("%s", _("\n*** Checksum errors detected in bitmaps!  Run e2fsck now!\n\n"));
 just_descriptors:
 		list_desc(fs, grp_only);
-		if (retval) {
-			printf(_("\n%s: %s: error reading bitmaps: %s\n"),
-			       program_name, device_name,
-			       error_message(retval));
-		}
+	}
+out_close:
+	if (retval_csum) {
+		com_err(program_name, retval_csum, error_csum, device_name);
+		printf("%s", _("*** Run e2fsck now!\n\n"));
+		if (!retval)
+			retval = retval_csum;
 	}
 	ext2fs_close_free(&fs);
 	remove_error_table(&et_ext2_error_table);
-	exit (0);
+out:
+	return retval;
 }
diff --git a/misc/e2mmpstatus.8.in b/misc/e2mmpstatus.8.in
new file mode 100644
index 0000000..f7d9557
--- /dev/null
+++ b/misc/e2mmpstatus.8.in
@@ -0,0 +1,59 @@ 
+.\" -*- nroff -*-
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH E2MMPSTATUS 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+e2mmpstatus \- Check MMP status of an ext4 filesystem
+.SH SYNOPSIS
+.BR e2mmpstatus " [" \-i ]
+.RI < filesystem >
+.SH OPTIONS
+.TP
+.B \-i
+prints out the MMP information rather than check it.
+.SH DESCRIPTION
+.B e2mmpstatus
+is used to check Multiple-Mount Protection (MMP) status of an ext4
+filesystem with the
+.B mmp
+feature enabled.  The specified
+.I filesystem
+can be a device name (e.g.
+.IR /dev/hdc1 ", " /dev/sdb2 ),
+or an ext4 filesystem label or UUID, for example
+.B UUID=8868abf6-88c5-4a83-98b8-bfc24057f7bd
+or
+.BR LABEL=root .
+By default, the
+.B e2mmpstatus
+program checks whether it is safe to mount the filesystem without taking
+the risk of mounting it more than once.
+.PP
+MMP (multiple-mount protection) is a feature that adds protection against
+the filesystem being modified simultaneously by more than one node.
+It is NOT safe to mount a filesystem when one of the following conditions
+is true:
+.br
+\	1. e2fsck is running on the filesystem.
+.br
+\	2. the filesystem is in use by another node.
+.br
+\	3. The MMP block is corrupted or cannot be read for some reason.
+.br
+The
+.B e2mmpstatus
+program might wait for some time to see whether the MMP block is being
+updated by any node during this period.  The time taken depends on how
+frequently the MMP block is being written by the other node.
+.SH EXIT CODE
+The exit code returned by
+.B e2mmpstatus
+is 0 when it is safe to mount the filesystem, 1 when the MMP block shows
+the filesystem is in use on another node and it is NOT safe to mount
+the filesystem, and 2 if some other failure occurred that prevents the
+check from properly detecting the current MMP status.
+.SH SEE ALSO
+.BR dumpe2fs (8),
+.BR e2fsck (8),
+.BR fstab (5),
+.BR fsck (8),
diff --git a/tests/f_mmp/script b/tests/f_mmp/script
index 9ff16c9..07ae232 100644
--- a/tests/f_mmp/script
+++ b/tests/f_mmp/script
@@ -43,6 +43,13 @@  rm -f $MARKFILE
 echo "kill debugfs abruptly (simulates e2fsck failure) ..." >> $test_name.log
 kill_debugfs
 
+$E2MMPSTATUS $TMPFILE > $test_name.log 2>&1
+status=$?
+if [ "$status" != 1 ] ; then
+	echo "$E2MMPSTATUS with EXT2_MMP_SEQ_FSCK passed!" > $test_name.failed
+	echo "$test_name: $test_description: failed"
+	return 1
+fi
 
 echo "e2fsck (should fail mmp_seq = EXT2_MMP_SEQ_FSCK) ..." >> $test_name.log
 $FSCK $FSCK_OPT $TMPFILE >> $test_name.log 2>&1
diff --git a/tests/m_mmp/expect.1 b/tests/m_mmp/expect.1
index a1452e6..9d8a5a3 100644
--- a/tests/m_mmp/expect.1
+++ b/tests/m_mmp/expect.1
@@ -46,6 +46,14 @@  Inode size:	          128
 Default directory hash:   half_md4
 MMP block number:         1049
 MMP update interval:      5
+MMP_block:
+    mmp_magic: 0x4d4d50
+    mmp_check_interval: 5
+    mmp_sequence: 0xff4d4d50
+    mmp_update_date: test date
+    mmp_update_time: test_time
+    mmp_node_name: test_node
+    mmp_device_name: test.img
 
 
 Group 0: (Blocks 0-32767)
diff --git a/tests/m_mmp_bad_csum/expect b/tests/m_mmp_bad_csum/expect
index e15e7b4..a0678ac 100644
--- a/tests/m_mmp_bad_csum/expect
+++ b/tests/m_mmp_bad_csum/expect
@@ -1,4 +1,7 @@ 
-Superblock MMP block checksum does not match MMP block.  Fix? yes
+dumpe2fs: MMP block checksum does not match while trying to open test.img
+dumpe2fs: MMP last updated by 'test_node' on test date
+Exit status is 1
+Superblock MMP block checksum does not match.  Fix? yes
 
 Pass 1: Checking inodes, blocks, and sizes
 Pass 2: Checking directory structure
@@ -7,3 +10,14 @@  Pass 4: Checking reference counts
 Pass 5: Checking group summary information
 test_filesys: 11/128 files (0.0% non-contiguous), 19/512 blocks
 Exit status is 0
+dumpe2fs: it is safe to mount 'test.img', MMP is clean
+Exit status is 0
+MMP_block:
+    mmp_magic: 0x4d4d50
+    mmp_check_interval: 5
+    mmp_sequence: 0xff4d4d50
+    mmp_update_date: test date
+    mmp_update_time: test_time
+    mmp_node_name: test_node
+    mmp_device_name: test.img
+    mmp_block_number: 8
diff --git a/tests/m_mmp_bad_csum/script b/tests/m_mmp_bad_csum/script
index 09e870c..4c8fe16 100644
--- a/tests/m_mmp_bad_csum/script
+++ b/tests/m_mmp_bad_csum/script
@@ -12,8 +12,15 @@  gzip -dc < $test_dir/image.gz > $TMPFILE
 
 OUT=$test_name.log
 EXP=$test_dir/expect
-$FSCK -fy $TMPFILE 2>&1 | sed -f $cmd_dir/filter.sed > $OUT
+$E2MMPSTATUS $TMPFILE > $OUT 2>&1
 echo Exit status is $? >> $OUT
+$FSCK -fy $TMPFILE >> $OUT 2>&1
+echo Exit status is $? >> $OUT
+$E2MMPSTATUS $TMPFILE >> $OUT 2>&1
+echo Exit status is $? >> $OUT
+$E2MMPSTATUS -i $TMPFILE >> $OUT 2>&1
+sed -f $cmd_dir/filter.sed $OUT > $OUT.new
+mv $OUT.new $OUT
 
 rm -f $TMPFILE
 cmp -s $OUT $EXP
diff --git a/tests/m_mmp_bad_magic/expect b/tests/m_mmp_bad_magic/expect
index b5dfb89..d5fa98c 100644
--- a/tests/m_mmp_bad_magic/expect
+++ b/tests/m_mmp_bad_magic/expect
@@ -1,3 +1,5 @@ 
+dumpe2fs: MMP: invalid magic number while trying to open test.img
+Exit status is 2
 Superblock has invalid MMP magic.  Fix? yes
 
 Pass 1: Checking inodes, blocks, and sizes
@@ -7,3 +9,14 @@  Pass 4: Checking reference counts
 Pass 5: Checking group summary information
 test_filesys: 11/128 files (0.0% non-contiguous), 19/512 blocks
 Exit status is 0
+dumpe2fs: it is safe to mount 'test.img', MMP is clean
+Exit status is 0
+MMP_block:
+    mmp_magic: 0x4d4d50
+    mmp_check_interval: 5
+    mmp_sequence: 0xff4d4d50
+    mmp_update_date: test date
+    mmp_update_time: test_time
+    mmp_node_name: test_node
+    mmp_device_name: test.img
+    mmp_block_number: 8
diff --git a/tests/m_mmp_bad_magic/script b/tests/m_mmp_bad_magic/script
index 09e870c..4c8fe16 100644
--- a/tests/m_mmp_bad_magic/script
+++ b/tests/m_mmp_bad_magic/script
@@ -12,8 +12,15 @@  gzip -dc < $test_dir/image.gz > $TMPFILE
 
 OUT=$test_name.log
 EXP=$test_dir/expect
-$FSCK -fy $TMPFILE 2>&1 | sed -f $cmd_dir/filter.sed > $OUT
+$E2MMPSTATUS $TMPFILE > $OUT 2>&1
 echo Exit status is $? >> $OUT
+$FSCK -fy $TMPFILE >> $OUT 2>&1
+echo Exit status is $? >> $OUT
+$E2MMPSTATUS $TMPFILE >> $OUT 2>&1
+echo Exit status is $? >> $OUT
+$E2MMPSTATUS -i $TMPFILE >> $OUT 2>&1
+sed -f $cmd_dir/filter.sed $OUT > $OUT.new
+mv $OUT.new $OUT
 
 rm -f $TMPFILE
 cmp -s $OUT $EXP
diff --git a/tests/test_config b/tests/test_config
index cf9c79c..2aee6ff 100644
--- a/tests/test_config
+++ b/tests/test_config
@@ -25,6 +25,7 @@  RESIZE2FS_EXE="../resize/resize2fs"
 RESIZE2FS="$USE_VALGRIND $RESIZE2FS_EXE"
 E2UNDO_EXE="../misc/e2undo"
 E2UNDO="$USE_VALGRIND $E2UNDO_EXE"
+E2MMPSTATUS="$USE_VALGRIND ../misc/dumpe2fs -m"
 TEST_REL=../tests/progs/test_rel
 TEST_ICOUNT=../tests/progs/test_icount
 CRCSUM=../tests/progs/crcsum