#define _XOPEN_SOURCE 600 /* for inclusion of sa_handler in Solaris */
+#include "config.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
{
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;
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);
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);
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);
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);
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;
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");
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,
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) {
printf(_(" (check in %ld mounts)"), next_check);
}
fputc('\n', stdout);
+skip:
ext2fs_close(fs);
ctx->fs = NULL;
e2fsck_free_context(ctx);
#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"),
break;
}
}
- close(fd);
}
#ifdef HAVE_SIGNAL_H
} 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);
"Valid extended options are:\n"), stderr);
fputs(("\tea_ver=<ea_version (1 or 2)>\n"), stderr);
fputs(("\tfragcheck\n"), stderr);
+ fputs(("\tjournal_only\n"), stderr);
+ fputs(("\tdiscard\n"), stderr);
+ fputs(("\tnodiscard\n"), stderr);
fputc('\n', stderr);
exit(1);
}
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':
/* 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;
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);
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;
char *cp;
clear_problem_context(&pctx);
+ sigcatcher_setup();
#ifdef MTRACE
mtrace();
#endif
_("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")) {
} 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)
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 "
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 "
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;
* 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);
}
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);
} 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) {