On Tue, May 01, 2018 at 10:26:06PM -0600, Andreas Dilger wrote: > From: Shuichi Ihara <sihara@xxxxxxx> > > 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@xxxxxxx> > Signed-off-by: Li Xi <lixi@xxxxxxx> > Signed-off-by: Wang Shilong <wshilong@xxxxxxx> > > 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@xxxxxxxxx> > --- > .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 >