]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
misc: add e2mmpstatus utility via dumpe2fs
authorShuichi Ihara <sihara@ddn.com>
Wed, 2 May 2018 04:26:06 +0000 (22:26 -0600)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 22 Jun 2018 22:22:37 +0000 (18:22 -0400)
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>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
22 files changed:
.gitignore
configure
configure.ac
e2fsck/e2fsck.h
e2fsck/problem.c
e2fsck/unix.c
e2fsck/util.c
e2fsprogs.spec.in
lib/config.h.in
lib/ext2fs/ext2_err.et.in
lib/ext2fs/mmp.c
misc/Makefile.in
misc/dumpe2fs.8.in
misc/dumpe2fs.c
misc/e2mmpstatus.8.in [new file with mode: 0644]
tests/f_mmp/script
tests/m_mmp/expect.1
tests/m_mmp_bad_csum/expect
tests/m_mmp_bad_csum/script
tests/m_mmp_bad_magic/expect
tests/m_mmp_bad_magic/script
tests/test_config

index df4021c9088f57a3b50b1ee405b93f820683cdd3..528883640967bd4ef87121832e0a6db88fc97b00 100644 (file)
@@ -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
index b2701d22098ae44602acba03edcc71651944bf71..c7853d1c95d5d056846201c10fd66c84f83ac3d9 100755 (executable)
--- 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"
index 739295919a3915842b4accfd1f7240c060cdb3df..5e837c9c3365a0ae4d12b6ad7fe8afa20139dcd4 100644 (file)
@@ -1124,6 +1124,7 @@ AC_CHECK_FUNCS(m4_flatten([
        futimes
        getcwd
        getdtablesize
+       gethostname
        getmntinfo
        getpwuid_r
        getrlimit
index d1ee367c79b0917dbe68fa97c7eeaf4451a1839a..e930d816ffcbdf373703732f17836d7172b3a271 100644 (file)
@@ -635,7 +635,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,
index a98bf8c835704a6f14ac86829ed3adca42473d47..a0a3cfec9a20755a29affe46621df7e8ae51db17 100644 (file)
@@ -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 */
index 581fd62d724de2324f240428de0a084876177a41..61396f7f1a15cccd8bdcaaa7901855e9b3643b9d 100644 (file)
@@ -1255,7 +1255,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);
index ed9470257469537502ce95a1559581a6d48c16da..5793b65a6a44b7c00ba3d4e72d08a6e4593470b2 100644 (file)
@@ -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);
        }
 }
 
index b188b751f32e4f9d6437bc8e12a512141c4603ae..f42c4be68eb0114a7c91143392178add23ea6278 100644 (file)
@@ -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*
index 9cc07938700071062c437f37d9a6acfb9184f4c5..67a05481ce8226759f3e4c4df1ebe76b321bb446 100644 (file)
 /* 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
 
 /* 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
 
 /* 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
 
 /* 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
 
 /* 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
 
index 16abd23d8133daebff81a1055f1662dd1cf34f1f..b2ba71ad2fd594d05b42f982f386c588a6cc6c8d 100644 (file)
@@ -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"
index 9a771de79929a65ad4a3cc453151616b32e23348..0cf0d0da9f56cfaf9e34549ef05fd7a63ffdd182 100644 (file)
@@ -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)
index efc0e0bceb9ddc6989267bf9377939853478118c..f50027626ac626dc5ff31e239e1a32bf3c355bd3 100644 (file)
@@ -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  
@@ -545,6 +550,8 @@ install: all $(SMANPAGES) $(UMANPAGES) installdirs
                (cd $(DESTDIR)$(root_sbindir); \
                        $(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 \
@@ -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 \
index da78d4fc7bc9c452a02439c536d4236af9571018..ce3214f3bd25f566f7981166ac2594046bed1c4a 100644 (file)
@@ -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)
index 395ea9ee03a0093987aa6cc2c3ecc5f7ee84586a..384ce925378e6a47c306bbb4461f6405da5b4e58 100644 (file)
@@ -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 (file)
index 0000000..f7d9557
--- /dev/null
@@ -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),
index 9ff16c956f9c8fb8de7cb12718c80d8eef9b37f1..07ae23215820671f596b60d3546c8192c7538ff5 100644 (file)
@@ -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
index a1452e6ba7958be7314c14b304fad542b035ba6e..9d8a5a3cd8c84108c9f97656b318743a505c0e93 100644 (file)
@@ -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)
index e15e7b4403085b08b9b89479371f27e9668783d2..a0678acd819e1754d53a3ec222f6def4d7f441d0 100644 (file)
@@ -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
index 09e870c05d924f5d69689cfe178f0a3b7bad4998..4c8fe1657b5ed5e834459ff1787d614479e6a6e0 100644 (file)
@@ -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
index b5dfb89702c28339fb6e74ed50069ef6738b869b..d5fa98cc258fa33a446a2e4428b372d5d298c7e4 100644 (file)
@@ -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
index 09e870c05d924f5d69689cfe178f0a3b7bad4998..4c8fe1657b5ed5e834459ff1787d614479e6a6e0 100644 (file)
@@ -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
index 595567fc5af42d4aab8f829d16fc1ea0ca0f3c62..81fbf95e3f3b03be42881068992c8348f3a30abc 100644 (file)
@@ -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