X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=e2fsck%2Funix.c;h=c95caddffd216915b794c499fd5ed59fe848998b;hb=4259052093da329907e255b11bf3e799872828c7;hp=d53921a5e81dbbcfe55c82fc0d6e0f145ec9c680;hpb=c5b23f6c0e17503630455fb16d1b2035f844acc9;p=tools%2Fe2fsprogs.git diff --git a/e2fsck/unix.c b/e2fsck/unix.c index d53921a..c95cadd 100644 --- a/e2fsck/unix.c +++ b/e2fsck/unix.c @@ -11,6 +11,7 @@ #define _XOPEN_SOURCE 600 /* for inclusion of sa_handler in Solaris */ +#include "config.h" #include #ifdef HAVE_STDLIB_H #include @@ -101,9 +102,9 @@ static void show_stats(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; ext2_ino_t inodes, inodes_used; - blk_t blocks, blocks_used; - int dir_links; - int num_files, num_links; + blk64_t blocks, blocks_used; + unsigned int dir_links; + unsigned int num_files, num_links; int frag_percent_file, frag_percent_dir, frag_percent_total; int i, j; @@ -129,7 +130,7 @@ static void show_stats(e2fsck_t ctx) frag_percent_total = (frag_percent_total + 5) / 10; if (!verbose) { - printf(_("%s: %u/%u files (%0d.%d%% non-contiguous), %u/%u blocks\n"), + printf(_("%s: %u/%u files (%0d.%d%% non-contiguous), %llu/%llu blocks\n"), ctx->device_name, inodes_used, inodes, frag_percent_total / 10, frag_percent_total % 10, blocks_used, blocks); @@ -163,7 +164,8 @@ static void show_stats(e2fsck_t ctx) fputc('\n', stdout); } - printf (P_("%8u block used (%2.2f%%)\n", "%8u blocks used (%2.2f%%)\n", + printf (P_("%8llu block used (%2.2f%%)\n", + "%8llu blocks used (%2.2f%%)\n", blocks_used), blocks_used, 100.0 * blocks_used / blocks); printf (P_("%8u bad block\n", "%8u bad blocks\n", ctx->fs_badblocks_count), ctx->fs_badblocks_count); @@ -232,7 +234,7 @@ static void check_mount(e2fsck_t ctx) printf(_("\n\n\007\007\007\007WARNING!!! " "The filesystem is mounted. If you continue you ***WILL***\n" "cause ***SEVERE*** filesystem damage.\007\007\007\n\n")); - cont = ask_yn(_("Do you really want to continue"), -1); + cont = ask_yn(_("Do you really want to continue"), 0); if (!cont) { printf (_("check aborted.\n")); exit (0); @@ -286,13 +288,19 @@ static int is_on_batt(void) static void check_if_skip(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; + struct problem_context pctx; const char *reason = NULL; unsigned int reason_arg = 0; long next_check; int batt = is_on_batt(); int defer_check_on_battery; + int broken_system_clock; time_t lastcheck; + profile_get_boolean(ctx->profile, "options", "broken_system_clock", + 0, 0, &broken_system_clock); + if (ctx->flags & E2F_FLAG_TIME_INSANE) + broken_system_clock = 1; profile_get_boolean(ctx->profile, "options", "defer_check_on_battery", 0, 1, &defer_check_on_battery); @@ -302,6 +310,9 @@ static void check_if_skip(e2fsck_t ctx) if ((ctx->options & E2F_OPT_FORCE) || bad_blocks_file || cflag) return; + if (ctx->options & E2F_OPT_JOURNAL_ONLY) + goto skip; + lastcheck = fs->super->s_lastcheck; if (lastcheck > ctx->now) lastcheck -= ctx->time_fudge; @@ -320,11 +331,12 @@ static void check_if_skip(e2fsck_t ctx) if (batt && (fs->super->s_mnt_count < (unsigned) fs->super->s_max_mnt_count*2)) reason = 0; - } else if (fs->super->s_checkinterval && (ctx->now < lastcheck)) { + } else if (!broken_system_clock && fs->super->s_checkinterval && + (ctx->now < lastcheck)) { reason = _(" has filesystem last checked time in the future"); if (batt) reason = 0; - } else if (fs->super->s_checkinterval && + } else if (!broken_system_clock && fs->super->s_checkinterval && ((ctx->now - lastcheck) >= ((time_t) fs->super->s_checkinterval))) { reason = _(" has gone %u days without being checked"); @@ -339,6 +351,36 @@ static void check_if_skip(e2fsck_t ctx) fputs(_(", check forced.\n"), stdout); return; } + + /* + * Update the global counts from the block group counts. This + * is needed since modern kernels don't update the global + * counts so as to avoid locking the entire file system. So + * if the filesystem is not unmounted cleanly, the global + * counts may not be accurate. Update them here if we can, + * for the benefit of users who might examine the file system + * using dumpe2fs. (This is for cosmetic reasons only.) + */ + clear_problem_context(&pctx); + pctx.ino = fs->super->s_free_inodes_count; + pctx.ino2 = ctx->free_inodes; + if ((pctx.ino != pctx.ino2) && + !(ctx->options & E2F_OPT_READONLY) && + fix_problem(ctx, PR_0_FREE_INODE_COUNT, &pctx)) { + fs->super->s_free_inodes_count = ctx->free_inodes; + ext2fs_mark_super_dirty(fs); + } + clear_problem_context(&pctx); + pctx.blk = ext2fs_free_blocks_count(fs->super); + pctx.blk2 = ctx->free_blocks; + if ((pctx.blk != pctx.blk2) && + !(ctx->options & E2F_OPT_READONLY) && + fix_problem(ctx, PR_0_FREE_BLOCK_COUNT, &pctx)) { + ext2fs_free_blocks_count_set(fs->super, ctx->free_blocks); + ext2fs_mark_super_dirty(fs); + } + + /* Print the summary message when we're skipping a full check */ printf(_("%s: clean, %u/%u files, %llu/%llu blocks"), ctx->device_name, fs->super->s_inodes_count - fs->super->s_free_inodes_count, fs->super->s_inodes_count, @@ -351,7 +393,7 @@ static void check_if_skip(e2fsck_t ctx) if (next_check <= 0) next_check = 1; } - if (fs->super->s_checkinterval && + if (!broken_system_clock && fs->super->s_checkinterval && ((ctx->now - fs->super->s_lastcheck) >= fs->super->s_checkinterval)) next_check = 1; if (next_check <= 5) { @@ -365,6 +407,7 @@ static void check_if_skip(e2fsck_t ctx) printf(_(" (check in %ld mounts)"), next_check); } fputc('\n', stdout); +skip: ext2fs_close(fs); ctx->fs = NULL; e2fsck_free_context(ctx); @@ -500,14 +543,16 @@ static int e2fsck_update_progress(e2fsck_t ctx, int pass, #define PATH_SET "PATH=/sbin" +/* + * Make sure 0,1,2 file descriptors are open, so that we don't open + * the filesystem using the same file descriptor as stdout or stderr. + */ static void reserve_stdio_fds(void) { - int fd; + int fd = 0; - while (1) { + while (fd <= 2) { fd = open("/dev/null", O_RDWR); - if (fd > 2) - break; if (fd < 0) { fprintf(stderr, _("ERROR: Couldn't open " "/dev/null (%s)\n"), @@ -515,7 +560,6 @@ static void reserve_stdio_fds(void) break; } } - close(fd); } #ifdef HAVE_SIGNAL_H @@ -587,6 +631,18 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts) } else if (strcmp(token, "fragcheck") == 0) { ctx->options |= E2F_OPT_FRAGCHECK; continue; + } else if (strcmp(token, "journal_only") == 0) { + if (arg) { + extended_usage++; + continue; + } + ctx->options |= E2F_OPT_JOURNAL_ONLY; + } else if (strcmp(token, "discard") == 0) { + ctx->options |= E2F_OPT_DISCARD; + continue; + } else if (strcmp(token, "nodiscard") == 0) { + ctx->options &= ~E2F_OPT_DISCARD; + continue; } else { fprintf(stderr, _("Unknown extended option: %s\n"), token); @@ -602,6 +658,9 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts) "Valid extended options are:\n"), stderr); fputs(("\tea_ver=\n"), stderr); fputs(("\tfragcheck\n"), stderr); + fputs(("\tjournal_only\n"), stderr); + fputs(("\tdiscard\n"), stderr); + fputs(("\tnodiscard\n"), stderr); fputc('\n', stderr); exit(1); } @@ -660,6 +719,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx) ctx->program_name = *argv; else ctx->program_name = "e2fsck"; + while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF) switch (c) { case 'C': @@ -731,7 +791,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx) /* What we do by default, anyway! */ break; case 'b': - res = sscanf(optarg, "%u", &ctx->use_superblock); + res = sscanf(optarg, "%llu", &ctx->use_superblock); if (res != 1) goto sscanf_err; ctx->flags |= E2F_FLAG_SB_SPECIFIED; @@ -745,7 +805,14 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx) goto sscanf_err; break; case 'j': - ctx->journal_name = string_copy(ctx, optarg, 0); + ctx->journal_name = blkid_get_devname(ctx->blkid, + optarg, NULL); + if (!ctx->journal_name) { + com_err(ctx->program_name, 0, + _("Unable to resolve '%s'"), + optarg); + fatal_error(ctx, 0); + } break; case 'P': res = sscanf(optarg, "%d", &ctx->process_inode_size); @@ -936,10 +1003,91 @@ static errcode_t try_open_fs(e2fsck_t ctx, int flags, io_manager io_ptr, return retval; } - static const char *my_ver_string = E2FSPROGS_VERSION; static const char *my_ver_date = E2FSPROGS_DATE; +int e2fsck_check_mmp(ext2_filsys fs, e2fsck_t ctx) +{ + struct mmp_struct *mmp_s; + unsigned int mmp_check_interval; + errcode_t retval = 0; + struct problem_context pctx; + unsigned int wait_time = 0; + + clear_problem_context(&pctx); + if (fs->mmp_buf == NULL) { + retval = ext2fs_get_mem(fs->blocksize, &fs->mmp_buf); + if (retval) + goto check_error; + } + + retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf); + if (retval) + goto check_error; + + mmp_s = fs->mmp_buf; + + mmp_check_interval = fs->super->s_mmp_update_interval; + if (mmp_check_interval < EXT4_MMP_MIN_CHECK_INTERVAL) + mmp_check_interval = EXT4_MMP_MIN_CHECK_INTERVAL; + + /* + * If check_interval in MMP block is larger, use that instead of + * check_interval from the superblock. + */ + if (mmp_s->mmp_check_interval > mmp_check_interval) + mmp_check_interval = mmp_s->mmp_check_interval; + + wait_time = mmp_check_interval * 2 + 1; + + if (mmp_s->mmp_seq == EXT4_MMP_SEQ_CLEAN) + retval = 0; + else if (mmp_s->mmp_seq == EXT4_MMP_SEQ_FSCK) + retval = EXT2_ET_MMP_FSCK_ON; + else if (mmp_s->mmp_seq > EXT4_MMP_SEQ_MAX) + retval = EXT2_ET_MMP_UNKNOWN_SEQ; + + if (retval) + goto check_error; + + /* Print warning if e2fck will wait for more than 20 secs. */ + if (verbose || wait_time > EXT4_MMP_MIN_CHECK_INTERVAL * 4) { + printf("MMP interval is %u seconds and total wait time is %u " + "seconds. Please wait...\n", + mmp_check_interval, wait_time * 2); + } + + return 0; + +check_error: + + if (retval == EXT2_ET_MMP_BAD_BLOCK) { + if (fix_problem(ctx, PR_0_MMP_INVALID_BLK, &pctx)) { + fs->super->s_mmp_block = 0; + ext2fs_mark_super_dirty(fs); + retval = 0; + } + } else if (retval == EXT2_ET_MMP_FAILED) { + com_err(ctx->program_name, retval, + _("while checking MMP block")); + dump_mmp_msg(fs->mmp_buf, NULL); + } else if (retval == EXT2_ET_MMP_FSCK_ON || + retval == EXT2_ET_MMP_UNKNOWN_SEQ) { + com_err(ctx->program_name, retval, + _("while checking MMP block")); + 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")); + } else if (retval == EXT2_ET_MMP_MAGIC_INVALID) { + if (fix_problem(ctx, PR_0_MMP_INVALID_MAGIC, &pctx)) { + ext2fs_mmp_clear(fs); + retval = 0; + } + } + return retval; +} + int main (int argc, char *argv[]) { errcode_t retval = 0, retval2 = 0, orig_retval = 0; @@ -960,6 +1108,7 @@ int main (int argc, char *argv[]) char *cp; clear_problem_context(&pctx); + sigcatcher_setup(); #ifdef MTRACE mtrace(); #endif @@ -1009,6 +1158,8 @@ int main (int argc, char *argv[]) _("need terminal for interactive repairs")); } ctx->superblock = ctx->use_superblock; + + flags = EXT2_FLAG_SKIP_MMP; restart: #ifdef CONFIG_TESTIO_DEBUG if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) { @@ -1017,7 +1168,7 @@ restart: } else #endif io_ptr = unix_io_manager; - flags = EXT2_FLAG_NOFREE_ON_ERROR; + flags |= EXT2_FLAG_NOFREE_ON_ERROR; profile_get_boolean(ctx->profile, "options", "old_bitmaps", 0, 0, &old_bitmaps); if (!old_bitmaps) @@ -1054,6 +1205,8 @@ restart: orig_retval = retval; retval = try_open_fs(ctx, flags, io_ptr, &fs); if ((orig_retval == 0) && retval != 0) { + if (fs) + ext2fs_close(fs); com_err(ctx->program_name, retval, "when using the backup blocks"); printf(_("%s: going back to original " @@ -1100,6 +1253,8 @@ failure: else if (retval == EBUSY) printf(_("Filesystem mounted or opened exclusively " "by another program?\n")); + else if (retval == ENOENT) + printf(_("Possibly non-existent device?\n")); #ifdef EROFS else if (retval == EROFS) printf(_("Disk write-protected; use the -n option " @@ -1186,6 +1341,22 @@ failure: ehandler_init(fs->io); + if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) && + (flags & EXT2_FLAG_SKIP_MMP)) { + if (e2fsck_check_mmp(fs, ctx)) + fatal_error(ctx, 0); + } + + /* + * Restart in order to reopen fs but this time start mmp. + */ + if (flags & EXT2_FLAG_SKIP_MMP) { + ext2fs_close(fs); + ctx->fs = NULL; + flags &= ~EXT2_FLAG_SKIP_MMP; + goto restart; + } + if ((ctx->mount_flags & EXT2_MF_MOUNTED) && !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER)) goto skip_journal; @@ -1337,10 +1508,23 @@ print_unsupp_features: * find the default journal size. */ if (sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) - journal_size = sb->s_jnl_blocks[16] >> 20; + journal_size = (sb->s_jnl_blocks[15] << (32 - 20)) | + (sb->s_jnl_blocks[16] >> 20); else journal_size = -1; + if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_QUOTA) { + int qtype; + /* Quotas were enabled. Do quota accounting during fsck. */ + if ((sb->s_usr_quota_inum && sb->s_grp_quota_inum) || + (!sb->s_usr_quota_inum && !sb->s_grp_quota_inum)) + qtype = -1; + else + qtype = sb->s_usr_quota_inum ? USRQUOTA : GRPQUOTA; + + init_quota_context(&ctx->qctx, ctx->fs, qtype); + } + run_result = e2fsck_run(ctx); e2fsck_clear_progbar(ctx); @@ -1373,6 +1557,11 @@ print_unsupp_features: } no_journal: + if (ctx->qctx) { + write_quota_inode(ctx->qctx, -1); + release_quota_context(&ctx->qctx); + } + if (run_result == E2F_FLAG_RESTART) { printf(_("Restarting e2fsck from the beginning...\n")); retval = e2fsck_reset_context(ctx); @@ -1439,12 +1628,16 @@ no_journal: } else sb->s_state &= ~EXT2_VALID_FS; sb->s_mnt_count = 0; - sb->s_lastcheck = ctx->now; + if (!(ctx->flags & E2F_FLAG_TIME_INSANE)) + sb->s_lastcheck = ctx->now; + memset(((char *) sb) + EXT4_S_ERR_START, 0, + EXT4_S_ERR_LEN); ext2fs_mark_super_dirty(fs); } } - if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM && + if ((run_result & E2F_FLAG_CANCEL) == 0 && + sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM && !(ctx->options & E2F_OPT_READONLY)) { retval = ext2fs_set_gdt_csum(ctx->fs); if (retval) {