From 73193347133e750faf27f88fd3ab31ce43aff062 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 21 May 2024 06:59:33 +0200 Subject: Adding upstream version 1.47.1. Signed-off-by: Daniel Baumann --- e2fsck/e2fsck.8.in | 112 ++++++++++++++++++---------------- e2fsck/e2fsck.h | 9 ++- e2fsck/ea_refcount.c | 4 +- e2fsck/ehandler.c | 10 ++- e2fsck/extents.c | 9 ++- e2fsck/iscan.c | 3 +- e2fsck/journal.c | 5 +- e2fsck/message.c | 2 +- e2fsck/pass1.c | 104 ++++++++++++++++++++++++------- e2fsck/pass2.c | 168 +++++++++++++++++++++++++++++++++------------------ e2fsck/pass3.c | 8 ++- e2fsck/pass4.c | 53 ++++++++++------ e2fsck/problem.c | 22 ++++++- e2fsck/problem.h | 12 ++++ e2fsck/rehash.c | 27 +++++++-- e2fsck/sigcatcher.c | 2 +- e2fsck/super.c | 25 ++++---- e2fsck/unix.c | 13 ++-- e2fsck/util.c | 92 ++++++++++++++++------------ 19 files changed, 454 insertions(+), 226 deletions(-) (limited to 'e2fsck') diff --git a/e2fsck/e2fsck.8.in b/e2fsck/e2fsck.8.in index dc6a585..ea0f514 100644 --- a/e2fsck/e2fsck.8.in +++ b/e2fsck/e2fsck.8.in @@ -44,8 +44,9 @@ e2fsck \- check a Linux ext2/ext3/ext4 file system is used to check the ext2/ext3/ext4 family of file systems. For ext3 and ext4 file systems that use a journal, if the system has been shut down uncleanly without any errors, normally, after replaying the -committed transactions in the journal, the file system should be -marked as clean. Hence, for file systems that use journaling, +committed transactions in the journal, the file system should be +marked as clean. +Hence, for file systems that use journaling, .B e2fsck will normally replay the journal and exit, unless its superblock indicates that further checking is required. @@ -63,12 +64,14 @@ option is specified, and .BR \-c , .BR \-l , or -.B -L +.B \-L options are .I not -specified. However, even if it is safe to do so, the results printed by +specified. +However, even if it is safe to do so, the results printed by .B e2fsck -are not valid if the file system is mounted. If +are not valid if the file system is mounted. +If .B e2fsck asks whether or not you should check a file system which is mounted, the only correct answer is ``no''. Only experts who really know what @@ -80,10 +83,10 @@ is run in interactive mode (meaning that none of .BR \-y , .BR \-n , or -.BR \-p +.B \-p are specified), the program will ask the user to fix each problem found in the -file system. A response of 'y' will fix the error; 'n' will leave the error -unfixed; and 'a' will fix the problem and all subsequent problems; pressing +file system. A response of \&'y' will fix the error; \&'n' will leave the error +unfixed; and \&'a' will fix the problem and all subsequent problems; pressing Enter will proceed with the default response, which is printed before the question mark. Pressing Control-C terminates e2fsck immediately. .SH OPTIONS @@ -113,7 +116,7 @@ program using the option to print out where the superblocks exist, supposing .B mke2fs is supplied with arguments that are consistent with the file system's layout -(e.g. blocksize, blocks per group, +(e.g.\& blocksize, blocks per group, .BR sparse_super , etc.). .IP @@ -170,7 +173,7 @@ Print debugging output (useless unless you are debugging .B \-D Optimize directories in file system. This option causes e2fsck to try to optimize all directories, either by re-indexing them if the -file system supports directory indexing, or by sorting and compressing +file system supports directory indexing, or by sorting and compressing directories for smaller directories, or for file systems using traditional linear directories. .IP @@ -204,86 +207,92 @@ Set the version of the extended attribute blocks which will require while checking the file system. The version number may be 1 or 2. The default extended attribute version format is 2. .TP -.BI journal_only +.B journal_only Only replay the journal if required, but do not perform any further checks or repairs. .TP -.BI fragcheck +.B fragcheck During pass 1, print a detailed report of any discontiguous blocks for files in the file system. .TP -.BI discard +.B discard Attempt to discard free blocks and unused inode blocks after the full file system check (discarding blocks is useful on solid state devices and sparse -/ thin-provisioned storage). Note that discard is done in pass 5 AFTER the +/ thin-provisioned storage). +Note that discard is done in pass 5 AFTER the file system has been fully checked and only if it does not contain recognizable -errors. However there might be cases where +errors. +However there might be cases where .B e2fsck does not fully recognize a problem and hence in this case this option may prevent you from further manual data recovery. .TP -.BI nodiscard -Do not attempt to discard free blocks and unused inode blocks. This option is -exactly the opposite of discard option. This is set as default. +.B nodiscard +Do not attempt to discard free blocks and unused inode blocks. +This option is exactly the opposite of discard option. +This is set as default. .TP -.BI no_optimize_extents +.B no_optimize_extents Do not offer to optimize the extent tree by eliminating unnecessary width or depth. This can also be enabled in the options section of .BR /etc/e2fsck.conf . .TP -.BI optimize_extents +.B optimize_extents Offer to optimize the extent tree by eliminating unnecessary width or depth. This is the default unless otherwise specified in .BR /etc/e2fsck.conf . .TP -.BI inode_count_fullmap +.B inode_count_fullmap Trade off using memory for speed when checking a file system with a large number of hard-linked files. The amount of memory required is proportional to the number of inodes in the file system. For large file -systems, this can be gigabytes of memory. (For example, a 40TB file system +systems, this can be gigabytes of memory. (For example, a 40\ TB file system with 2.8 billion inodes will consume an additional 5.7 GB memory if this optimization is enabled.) This optimization can also be enabled in the options section of .BR /etc/e2fsck.conf . .TP -.BI no_inode_count_fullmap +.B no_inode_count_fullmap Disable the .B inode_count_fullmap optimization. This is the default unless otherwise specified in .BR /etc/e2fsck.conf . .TP -.BI readahead_kb +.B readahead_kb Use this many KiB of memory to pre-fetch metadata in the hopes of reducing e2fsck runtime. By default, this is set to the size of two block groups' inode -tables (typically 4MiB on a regular ext4 file system); if this amount is more +tables (typically 4\ MiB on a regular ext4 file system); if this amount is more than 1/50th of total physical memory, readahead is disabled. Set this to zero to disable readahead entirely. .TP -.BI bmap2extent +.B bmap2extent Convert block-mapped files to extent-mapped files. .TP -.BI fixes_only +.B fixes_only Only fix damaged metadata; do not optimize htree directories or compress -extent trees. This option is incompatible with the -D and -E bmap2extent +extent trees. This option is incompatible with the \-D and \-E bmap2extent options. .TP -.BI check_encoding +.B check_encoding Force verification of encoded filenames in case-insensitive directories. This is the default mode if the file system has the strict flag enabled. .TP -.BI unshare_blocks +.B unshare_blocks If the file system has shared blocks, with the shared blocks read-only feature enabled, then this will unshare all shared blocks and unset the read-only -feature bit. If there is not enough free space then the operation will fail. +feature bit. +If there is not enough free space then the operation will fail. If the file system does not have the read-only feature bit, but has shared -blocks anyway, then this option will have no effect. Note when using this +blocks anyway, then this option will have no effect. +Note when using this option, if there is no free space to clone blocks, there is no prompt to delete files and instead the operation will fail. .IP -Note that unshare_blocks implies the "-f" option to ensure that all passes -are run. Additionally, if "-n" is also specified, e2fsck will simulate trying -to allocate enough space to deduplicate. If this fails, the exit code will -be non-zero. +Note that unshare_blocks implies the "\-f" option to ensure that all passes +are run. +Additionally, if "\-n" is also specified, e2fsck will simulate trying +to allocate enough space to deduplicate. +If this fails, the exit code will be non-zero. .RE .TP .B \-f @@ -299,7 +308,7 @@ time trials. @JDEV@Set the pathname where the external-journal for this file system can be @JDEV@found. .TP -.BI \-k +.B \-k When combined with the .B \-c option, any existing bad blocks in the bad blocks list are preserved, @@ -318,7 +327,7 @@ of the file system. Hence, .BR badblocks (8) must be given the blocksize of the file system in order to obtain correct results. As a result, it is much simpler and safer to use the -.B -c +.B \-c option to .BR e2fsck , since it will assure that the correct parameters are passed to the @@ -391,7 +400,9 @@ options. .TP .BI \-z " undo_file" Before overwriting a file system block, write the old contents of the block to -an undo file. This undo file can be used with e2undo(8) to restore the old +an undo file. This undo file can be used with +.BR e2undo (8) +to restore the old contents of the file system should something go wrong. If the empty string is passed as the undo_file argument, the undo file will be written to a file named e2fsck-\fIdevice\fR.e2undo in the directory specified via the @@ -403,24 +414,23 @@ The exit code returned by .B e2fsck is the sum of the following conditions: .br -\ 0\ \-\ No errors -.br -\ 1\ \-\ File system errors corrected + 0 \-\ No errors .br -\ 2\ \-\ File system errors corrected, system should + 1 \-\ File system errors corrected .br -\ \ \ \ be rebooted + 2 \-\ File system errors corrected, system should .br -\ 4\ \-\ File system errors left uncorrected + \ \ be rebooted .br -\ 8\ \-\ Operational error + 4 \-\ File system errors left uncorrected .br -\ 16\ \-\ Usage or syntax error + 8 \-\ Operational error .br -\ 32\ \-\ E2fsck canceled by user request + 16 \-\ Usage or syntax error .br -\ 128\ \-\ Shared library error + 32 \-\ E2fsck canceled by user request .br + 128 \-\ Shared library error .SH SIGNALS The following signals have the following effect when sent to .BR e2fsck . @@ -454,7 +464,7 @@ the messages printed by are in English; if your system has been configured so that .BR e2fsck 's -messages have been translated into another language, please set the the +messages have been translated into another language, please set the .B LC_ALL environment variable to .B C @@ -492,7 +502,7 @@ Always include the full version string which displays when it is run, so I know which version you are running. .SH ENVIRONMENT .TP -.BI E2FSCK_CONFIG +.B E2FSCK_CONFIG Determines the location of the configuration file (see .BR e2fsck.conf (5)). .SH AUTHOR diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index 3f2dc30..ae1273d 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -533,6 +533,12 @@ extern struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, typedef __u64 ea_key_t; typedef __u64 ea_value_t; +/* + * Special refcount value we use for inodes which have EA_INODE flag set but we + * do not yet know about any references. + */ +#define EA_INODE_NO_REFS (~(ea_value_t)0) + extern errcode_t ea_refcount_create(size_t size, ext2_refcount_t *ret); extern void ea_refcount_free(ext2_refcount_t refcount); extern errcode_t ea_refcount_fetch(ext2_refcount_t refcount, ea_key_t ea_key, @@ -663,7 +669,8 @@ extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned long size, const char *description); extern int ask(e2fsck_t ctx, const char * string, int def); extern int ask_yn(e2fsck_t ctx, const char * string, int def); -extern void fatal_error(e2fsck_t ctx, const char * fmt_string); +extern void fatal_error(e2fsck_t ctx, const char * fmt_string) + E2FSCK_ATTR((noreturn)); extern void log_out(e2fsck_t ctx, const char *fmt, ...) E2FSCK_ATTR((format(printf, 2, 3))); extern void log_err(e2fsck_t ctx, const char *fmt, ...) diff --git a/e2fsck/ea_refcount.c b/e2fsck/ea_refcount.c index 7154b47..7ca5669 100644 --- a/e2fsck/ea_refcount.c +++ b/e2fsck/ea_refcount.c @@ -120,7 +120,7 @@ static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount, if (refcount->count >= refcount->size) { new_size = refcount->size + 100; #ifdef DEBUG - printf("Reallocating refcount %d entries...\n", new_size); + printf("Reallocating refcount %zu entries...\n", new_size); #endif retval = ext2fs_resize_mem((size_t) refcount->size * sizeof(struct ea_refcount_el), @@ -177,7 +177,7 @@ retry: if (ea_key == refcount->list[refcount->cursor].ea_key) return &refcount->list[refcount->cursor++]; #ifdef DEBUG - printf("Non-cursor get_refcount_el: %u\n", ea_key); + printf("Non-cursor get_refcount_el: %llu\n", (unsigned long long) ea_key); #endif while (low <= high) { mid = (low+high)/2; diff --git a/e2fsck/ehandler.c b/e2fsck/ehandler.c index 71ca301..14e9549 100644 --- a/e2fsck/ehandler.c +++ b/e2fsck/ehandler.c @@ -31,6 +31,7 @@ static errcode_t e2fsck_handle_read_error(io_channel channel, int i; char *p; ext2_filsys fs = (ext2_filsys) channel->app_data; + errcode_t retval; e2fsck_t ctx; ctx = (e2fsck_t) fs->priv_data; @@ -64,8 +65,13 @@ static errcode_t e2fsck_handle_read_error(io_channel channel, return 0; if (ask(ctx, _("Ignore error"), 1)) { - if (ask(ctx, _("Force rewrite"), 1)) - io_channel_write_blk64(channel, block, count, data); + if (ask(ctx, _("Force rewrite"), 1)) { + retval = io_channel_write_blk64(channel, block, + count, data); + if (retval) + printf(_("Error rewriting block %lu (%s)\n"), + block, error_message(retval)); + } return 0; } diff --git a/e2fsck/extents.c b/e2fsck/extents.c index 70798f3..652e938 100644 --- a/e2fsck/extents.c +++ b/e2fsck/extents.c @@ -201,7 +201,10 @@ static errcode_t rewrite_extent_replay(e2fsck_t ctx, struct extent_list *list, { errcode_t retval; ext2_extent_handle_t handle; - unsigned int i, ext_written; + unsigned int i; +#if defined(DEBUG) || defined(DEBUG_SUMMARY) + unsigned int ext_written = 0; +#endif struct ext2fs_extent *ex, extent; blk64_t start_val, delta; @@ -223,8 +226,6 @@ static errcode_t rewrite_extent_replay(e2fsck_t ctx, struct extent_list *list, if (retval) return retval; - ext_written = 0; - start_val = ext2fs_get_stat_i_blocks(ctx->fs, EXT2_INODE(inode)); for (i = 0, ex = list->extents; i < list->count; i++, ex++) { @@ -263,7 +264,9 @@ static errcode_t rewrite_extent_replay(e2fsck_t ctx, struct extent_list *list, retval = ext2fs_extent_fix_parents(handle); if (retval) goto err; +#if defined(DEBUG) || defined(DEBUG_SUMMARY) ext_written++; +#endif } delta = ext2fs_get_stat_i_blocks(ctx->fs, EXT2_INODE(inode)) - diff --git a/e2fsck/iscan.c b/e2fsck/iscan.c index 33c6a4c..1253f52 100644 --- a/e2fsck/iscan.c +++ b/e2fsck/iscan.c @@ -120,7 +120,8 @@ void print_resource_track(const char *desc, } else #elif defined HAVE_MALLINFO /* don't use mallinfo() if over 2GB used, since it returns "int" */ - if ((char *)sbrk(0) - (char *)track->brk_start < 2LL << 30) { + if ((unsigned long)((char *)sbrk(0) - (char *)track->brk_start) < + 2UL << 30) { struct mallinfo malloc_info = mallinfo(); printf("Memory used: %lluk/%lluk (%lluk/%lluk), ", diff --git a/e2fsck/journal.c b/e2fsck/journal.c index c7868d8..19d68b4 100644 --- a/e2fsck/journal.c +++ b/e2fsck/journal.c @@ -1683,6 +1683,7 @@ errcode_t e2fsck_run_ext3_journal(e2fsck_t ctx) errcode_t retval, recover_retval; io_stats stats = 0; unsigned long long kbytes_written = 0; + __u16 s_error_state; printf(_("%s: recovering journal\n"), ctx->device_name); if (ctx->options & E2F_OPT_READONLY) { @@ -1705,6 +1706,7 @@ errcode_t e2fsck_run_ext3_journal(e2fsck_t ctx) ctx->fs->io->manager->get_stats(ctx->fs->io, &stats); if (stats && stats->bytes_written) kbytes_written = stats->bytes_written >> 10; + s_error_state = ctx->fs->super->s_state & EXT2_ERROR_FS; ext2fs_mmp_stop(ctx->fs); ext2fs_free(ctx->fs); @@ -1721,6 +1723,7 @@ errcode_t e2fsck_run_ext3_journal(e2fsck_t ctx) ctx->fs->now = ctx->now; ctx->fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; ctx->fs->super->s_kbytes_written += kbytes_written; + ctx->fs->super->s_state |= s_error_state; /* Set the superblock flags */ e2fsck_clear_recover(ctx, recover_retval != 0); @@ -1841,7 +1844,7 @@ void e2fsck_move_ext3_journal(e2fsck_t ctx) ext2fs_mark_super_dirty(fs); fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; inode.i_links_count = 0; - inode.i_dtime = ctx->now; + ext2fs_set_dtime(fs, &inode); if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0) goto err_out; diff --git a/e2fsck/message.c b/e2fsck/message.c index ba38038..9c42b13 100644 --- a/e2fsck/message.c +++ b/e2fsck/message.c @@ -301,7 +301,7 @@ static _INLINE_ void expand_inode_expression(FILE *f, ext2_filsys fs, char ch, fprintf(f, "0%o", inode->i_mode); break; case 'M': - print_time(f, inode->i_mtime); + print_time(f, ext2fs_inode_xtime_get(inode, i_mtime)); break; case 'F': fprintf(f, "%u", inode->i_faddr); diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index a341c72..eb73922 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -387,34 +387,71 @@ static problem_t check_large_ea_inode(e2fsck_t ctx, return 0; } +static int alloc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx) +{ + pctx->errcode = ea_refcount_create(0, &ctx->ea_inode_refs); + if (pctx->errcode) { + pctx->num = 4; + fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return 0; + } + return 1; +} + static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx, struct ext2_ext_attr_entry *first, void *end) { struct ext2_ext_attr_entry *entry = first; struct ext2_ext_attr_entry *np = EXT2_EXT_ATTR_NEXT(entry); + ea_value_t refs; while ((void *) entry < end && (void *) np < end && !EXT2_EXT_IS_LAST_ENTRY(entry)) { if (!entry->e_value_inum) goto next; - if (!ctx->ea_inode_refs) { - pctx->errcode = ea_refcount_create(0, - &ctx->ea_inode_refs); - if (pctx->errcode) { - pctx->num = 4; - fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx); - ctx->flags |= E2F_FLAG_ABORT; - return; - } - } - ea_refcount_increment(ctx->ea_inode_refs, entry->e_value_inum, - 0); + if (!ctx->ea_inode_refs && !alloc_ea_inode_refs(ctx, pctx)) + return; + ea_refcount_fetch(ctx->ea_inode_refs, entry->e_value_inum, + &refs); + if (refs == EA_INODE_NO_REFS) + refs = 1; + else + refs += 1; + ea_refcount_store(ctx->ea_inode_refs, entry->e_value_inum, refs); next: entry = np; np = EXT2_EXT_ATTR_NEXT(entry); } } +/* + * Make sure inode is tracked as EA inode. We use special EA_INODE_NO_REFS + * value if we didn't find any xattrs referencing this inode yet. + */ +static int track_ea_inode(e2fsck_t ctx, struct problem_context *pctx, + ext2_ino_t ino) +{ + ea_value_t refs; + + if (!ctx->ea_inode_refs && !alloc_ea_inode_refs(ctx, pctx)) + return 0; + + ea_refcount_fetch(ctx->ea_inode_refs, ino, &refs); + if (refs > 0) + return 1; + + pctx->errcode = ea_refcount_store(ctx->ea_inode_refs, ino, + EA_INODE_NO_REFS); + if (pctx->errcode) { + pctx->num = 5; + fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return 0; + } + return 1; +} + static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx, struct ea_quota *ea_ibody_quota) { @@ -510,6 +547,12 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx, } else { blk64_t quota_blocks; + if (!ext2fs_has_feature_ea_inode(sb) && + fix_problem(ctx, PR_1_EA_INODE_FEATURE, pctx)) { + ext2fs_set_feature_ea_inode(sb); + ext2fs_mark_super_dirty(ctx->fs); + } + problem = check_large_ea_inode(ctx, entry, pctx, "a_blocks); if (problem != 0) @@ -1181,6 +1224,7 @@ void e2fsck_pass1(e2fsck_t ctx) ext2_ino_t ino_threshold = 0; dgrp_t ra_group = 0; struct ea_quota ea_ibody_quota; + time_t tm; init_resource_track(&rtrack, ctx->fs->io); clear_problem_context(&pctx); @@ -1357,12 +1401,13 @@ void e2fsck_pass1(e2fsck_t ctx) if (ctx->progress && ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))) goto endit; - if ((fs->super->s_wtime && - fs->super->s_wtime < fs->super->s_inodes_count) || - (fs->super->s_mtime && - fs->super->s_mtime < fs->super->s_inodes_count) || - (fs->super->s_mkfs_time && - fs->super->s_mkfs_time < fs->super->s_inodes_count)) + + if (((tm = ext2fs_get_tstamp(fs->super, s_wtime)) && + tm < fs->super->s_inodes_count) || + ((tm = ext2fs_get_tstamp(fs->super, s_mtime)) && + tm < fs->super->s_inodes_count) || + ((tm = ext2fs_get_tstamp(fs->super, s_mkfs_time)) && + tm < fs->super->s_inodes_count)) low_dtime_check = 0; if (ext2fs_has_feature_mmp(fs->super) && @@ -1481,7 +1526,7 @@ void e2fsck_pass1(e2fsck_t ctx) if (!inode->i_dtime && inode->i_mode) { if (fix_problem(ctx, PR_1_ZERO_DTIME, &pctx)) { - inode->i_dtime = ctx->now; + ext2fs_set_dtime(fs, inode); e2fsck_write_inode(ctx, ino, inode, "pass1"); failed_csum = 0; @@ -1500,6 +1545,17 @@ void e2fsck_pass1(e2fsck_t ctx) e2fsck_write_inode(ctx, ino, inode, "pass1"); } + if (inode->i_flags & EXT4_EA_INODE_FL) { + if (!LINUX_S_ISREG(inode->i_mode) && + fix_problem(ctx, PR_1_EA_INODE_NONREG, &pctx)) { + inode->i_flags &= ~EXT4_EA_INODE_FL; + e2fsck_write_inode(ctx, ino, inode, "pass1"); + } + if (inode->i_flags & EXT4_EA_INODE_FL) + if (!track_ea_inode(ctx, &pctx, ino)) + continue; + } + /* Conflicting inlinedata/extents inode flags? */ if ((inode->i_flags & EXT4_INLINE_DATA_FL) && (inode->i_flags & EXT4_EXTENTS_FL)) { @@ -2076,7 +2132,7 @@ void e2fsck_pass1(e2fsck_t ctx) if (!pctx.errcode) { e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode, "recreate inode"); - inode->i_mtime = ctx->now; + ext2fs_inode_xtime_set(inode, i_mtime, ctx->now); e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode, "recreate inode"); } @@ -2620,6 +2676,12 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, problem_t problem; blk64_t entry_quota_blocks; + if (!ext2fs_has_feature_ea_inode(fs->super) && + fix_problem(ctx, PR_1_EA_INODE_FEATURE, pctx)) { + ext2fs_set_feature_ea_inode(fs->super); + ext2fs_mark_super_dirty(fs); + } + problem = check_large_ea_inode(ctx, entry, pctx, &entry_quota_blocks); if (problem && fix_problem(ctx, problem, pctx)) @@ -2791,7 +2853,7 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino, inode->i_flags = 0; inode->i_links_count = 0; ext2fs_icount_store(ctx->inode_link_info, ino, 0); - inode->i_dtime = ctx->now; + ext2fs_set_dtime(ctx->fs, inode); /* * If a special inode has such rotten block mappings that we diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 410edd1..036c002 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -1341,57 +1341,68 @@ skip_checksum: (rec_len < min_dir_len) || ((rec_len % 4) != 0) || ((ext2fs_dir_rec_len(ext2fs_dirent_name_len(dirent), - extended)) > rec_len)) { - if (fix_problem(ctx, PR_2_DIR_CORRUPTED, - &cd->pctx)) { + extended)) > rec_len)) + problem = PR_2_DIR_CORRUPTED; + if (problem) { + if ((offset == 0) && + (rec_len == fs->blocksize) && + (dirent->inode == 0) && + e2fsck_dir_will_be_rehashed(ctx, ino)) { + problem = 0; + max_block_size = fs->blocksize; + } + } + if (problem) { #ifdef WORDS_BIGENDIAN - /* - * On big-endian systems, if the dirent - * swap routine finds a rec_len that it - * doesn't like, it continues - * processing the block as if rec_len - * == EXT2_DIR_ENTRY_HEADER_LEN. This means that the name - * field gets byte swapped, which means - * that salvage will not detect the - * correct name length (unless the name - * has a length that's an exact - * multiple of four bytes), and it'll - * discard the entry (unnecessarily) - * and the rest of the dirent block. - * Therefore, swap the rest of the - * block back to disk order, run - * salvage, and re-swap anything after - * the salvaged dirent. - */ - int need_reswab = 0; - if (rec_len < EXT2_DIR_ENTRY_HEADER_LEN || rec_len % 4) { - need_reswab = 1; - ext2fs_dirent_swab_in2(fs, - ((char *)dirent) + EXT2_DIR_ENTRY_HEADER_LEN, - max_block_size - offset - EXT2_DIR_ENTRY_HEADER_LEN, - 0); - } + int need_reswab = 0; #endif - salvage_directory(fs, dirent, prev, - &offset, - max_block_size, - hash_in_dirent); + + if (!fix_problem(ctx, PR_2_DIR_CORRUPTED, + &cd->pctx)) + goto abort_free_dict; #ifdef WORDS_BIGENDIAN - if (need_reswab) { - unsigned int len; - - (void) ext2fs_get_rec_len(fs, - dirent, &len); - len += offset; - if (max_block_size > len) - ext2fs_dirent_swab_in2(fs, - ((char *)dirent) + len, max_block_size - len, 0); - } + /* + * On big-endian systems, if the dirent + * swap routine finds a rec_len that it + * doesn't like, it continues processing + * the block as if rec_len == + * EXT2_DIR_ENTRY_HEADER_LEN. This means + * that the name field gets byte swapped, + * which means that salvage will not detect + * the correct name length (unless the name + * has a length that's an exact multiple of + * four bytes), and it'll discard the entry + * (unnecessarily) and the rest of the + * dirent block. Therefore, swap the rest + * of the block back to disk order, run + * salvage, and re-swap anything after the + * salvaged dirent. + */ + if (rec_len < EXT2_DIR_ENTRY_HEADER_LEN || + rec_len % 4) { + need_reswab = 1; + ext2fs_dirent_swab_in2(fs, + ((char *)dirent) + EXT2_DIR_ENTRY_HEADER_LEN, + max_block_size - offset - EXT2_DIR_ENTRY_HEADER_LEN, 0); + } #endif - dir_modified++; - continue; - } else - goto abort_free_dict; + salvage_directory(fs, dirent, prev, &offset, + max_block_size, + hash_in_dirent); +#ifdef WORDS_BIGENDIAN + if (need_reswab) { + unsigned int len; + + (void) ext2fs_get_rec_len(fs, dirent, + &len); + len += offset; + if (max_block_size > len) + ext2fs_dirent_swab_in2(fs, + ((char *)dirent) + len, max_block_size - len, 0); + } +#endif + dir_modified++; + continue; } } else { if (dot_state == 0) { @@ -1490,6 +1501,21 @@ skip_checksum: problem = PR_2_NULL_NAME; } + /* + * Check if inode was tracked as EA inode and has actual + * references from xattrs. In that case dir entry is likely + * bogus and we want to clear it. The case of EA inode without + * references from xattrs will be handled in pass 4. + */ + if (!problem && ctx->ea_inode_refs) { + ea_value_t refs; + + ea_refcount_fetch(ctx->ea_inode_refs, dirent->inode, + &refs); + if (refs && refs != EA_INODE_NO_REFS) + problem = PR_2_EA_INODE_DIR_LINK; + } + if (problem) { if (fix_problem(ctx, problem, &cd->pctx)) { dirent->inode = 0; @@ -1513,7 +1539,7 @@ skip_checksum: dirent->inode)) { if (e2fsck_process_bad_inode(ctx, ino, dirent->inode, - buf + fs->blocksize)) { + cd->buf + fs->blocksize)) { dirent->inode = 0; dir_modified++; goto next; @@ -1574,7 +1600,8 @@ skip_checksum: */ if (!(ctx->flags & E2F_FLAG_RESTART_LATER) && !(ext2fs_test_inode_bitmap2(ctx->inode_used_map, - dirent->inode))) + dirent->inode)) + ) problem = PR_2_UNUSED_INODE; if (problem) { @@ -1842,17 +1869,26 @@ static int deallocate_inode_block(ext2_filsys fs, } /* - * This function deallocates an inode + * This function reverts various counters and bitmaps incremented in + * pass1 for the inode, blocks, and quotas before it was decided the + * inode was corrupt and needed to be cleared. This avoids the need + * to run e2fsck a second time (or have it restart itself) to repair + * these counters. + * + * It does not modify any on-disk state, so even if the inode is bad + * it _should_ reset in-memory state to before the inode was first + * processed. */ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) { ext2_filsys fs = ctx->fs; - struct ext2_inode inode; + struct ext2_inode_large inode; struct problem_context pctx; __u32 count; struct del_block del_block; - e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); + e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode), + sizeof(inode), "deallocate_inode"); clear_problem_context(&pctx); pctx.ino = ino; @@ -1862,29 +1898,29 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) e2fsck_read_bitmaps(ctx); ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); - if (ext2fs_file_acl_block(fs, &inode) && + if (ext2fs_file_acl_block(fs, EXT2_INODE(&inode)) && ext2fs_has_feature_xattr(fs->super)) { pctx.errcode = ext2fs_adjust_ea_refcount3(fs, - ext2fs_file_acl_block(fs, &inode), + ext2fs_file_acl_block(fs, EXT2_INODE(&inode)), block_buf, -1, &count, ino); if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { pctx.errcode = 0; count = 1; } if (pctx.errcode) { - pctx.blk = ext2fs_file_acl_block(fs, &inode); + pctx.blk = ext2fs_file_acl_block(fs, EXT2_INODE(&inode)); fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } if (count == 0) { ext2fs_block_alloc_stats2(fs, - ext2fs_file_acl_block(fs, &inode), -1); + ext2fs_file_acl_block(fs, EXT2_INODE(&inode)), -1); } - ext2fs_file_acl_block_set(fs, &inode, 0); + ext2fs_file_acl_block_set(fs, EXT2_INODE(&inode), 0); } - if (!ext2fs_inode_has_valid_blocks2(fs, &inode)) + if (!ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(&inode))) goto clear_inode; /* Inline data inodes don't have blocks to iterate */ @@ -1909,10 +1945,22 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) ctx->flags |= E2F_FLAG_ABORT; return; } + + if ((ino != quota_type2inum(PRJQUOTA, fs->super)) && + (ino != fs->super->s_orphan_file_inum) && + (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) && + !(inode.i_flags & EXT4_EA_INODE_FL)) { + if (del_block.num > 0) + quota_data_sub(ctx->qctx, &inode, ino, + del_block.num * EXT2_CLUSTER_SIZE(fs->super)); + quota_data_inodes(ctx->qctx, (struct ext2_inode_large *)&inode, + ino, -1); + } + clear_inode: /* Inode may have changed by block_iterate, so reread it */ - e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); - e2fsck_clear_inode(ctx, ino, &inode, 0, "deallocate_inode"); + e2fsck_read_inode(ctx, ino, EXT2_INODE(&inode), "deallocate_inode"); + e2fsck_clear_inode(ctx, ino, EXT2_INODE(&inode), 0, "deallocate_inode"); } /* diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c index 16d243f..ba79416 100644 --- a/e2fsck/pass3.c +++ b/e2fsck/pass3.c @@ -212,7 +212,9 @@ skip_new_block: memset(&inode, 0, sizeof(inode)); inode.i_mode = 040755; inode.i_size = fs->blocksize; - inode.i_atime = inode.i_ctime = inode.i_mtime = ctx->now; + ext2fs_inode_xtime_set(&inode, i_atime, ctx->now); + ext2fs_inode_xtime_set(&inode, i_ctime, ctx->now); + ext2fs_inode_xtime_set(&inode, i_mtime, ctx->now); inode.i_links_count = 2; ext2fs_iblk_set(fs, iptr, 1); inode.i_block[0] = blk; @@ -528,7 +530,9 @@ skip_new_block: memset(&inode, 0, sizeof(inode)); inode.i_mode = 040700; inode.i_size = fs->blocksize; - inode.i_atime = inode.i_ctime = inode.i_mtime = ctx->now; + ext2fs_inode_xtime_set(&inode, i_atime, ctx->now); + ext2fs_inode_xtime_set(&inode, i_ctime, ctx->now); + ext2fs_inode_xtime_set(&inode, i_mtime, ctx->now); inode.i_links_count = 2; ext2fs_iblk_set(fs, EXT2_INODE(&inode), 1); inode.i_block[0] = blk; diff --git a/e2fsck/pass4.c b/e2fsck/pass4.c index d2dda02..cf0cf7c 100644 --- a/e2fsck/pass4.c +++ b/e2fsck/pass4.c @@ -96,9 +96,10 @@ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i, ext2_ino_t *last_ino, * an xattr inode at all. Return immediately if EA_INODE flag is not set. */ static void check_ea_inode(e2fsck_t ctx, ext2_ino_t i, ext2_ino_t *last_ino, - struct ext2_inode_large *inode, __u16 *link_counted) + struct ext2_inode_large *inode, __u16 *link_counted, + ea_value_t actual_refs) { - __u64 actual_refs = 0; + struct problem_context pctx; __u64 ref_count; if (*last_ino != i) { @@ -107,13 +108,26 @@ static void check_ea_inode(e2fsck_t ctx, ext2_ino_t i, ext2_ino_t *last_ino, "pass4: check_ea_inode"); *last_ino = i; } - if (!(inode->i_flags & EXT4_EA_INODE_FL)) - return; - if (ctx->ea_inode_refs) - ea_refcount_fetch(ctx->ea_inode_refs, i, &actual_refs); - if (!actual_refs) + clear_problem_context(&pctx); + pctx.ino = i; + pctx.inode = EXT2_INODE(inode); + + /* No references to the inode from xattrs? */ + if (actual_refs == EA_INODE_NO_REFS) { + /* + * No references from directory hierarchy either? Inode will + * will get attached to lost+found so clear EA_INODE_FL. + * Otherwise this is likely a spuriously set flag so clear it. + */ + if (*link_counted == 0 || + fix_problem(ctx, PR_4_EA_INODE_SPURIOUS_FLAG, &pctx)) { + /* Clear EA_INODE_FL (likely a normal file) */ + inode->i_flags &= ~EXT4_EA_INODE_FL; + e2fsck_write_inode(ctx, i, EXT2_INODE(inode), "pass4"); + } return; + } /* * There are some attribute references, link_counted is now considered @@ -127,10 +141,6 @@ static void check_ea_inode(e2fsck_t ctx, ext2_ino_t i, ext2_ino_t *last_ino, * However, their i_ctime and i_atime should be the same. */ if (ref_count != actual_refs && inode->i_ctime != inode->i_atime) { - struct problem_context pctx; - - clear_problem_context(&pctx); - pctx.ino = i; pctx.num = ref_count; pctx.num2 = actual_refs; if (fix_problem(ctx, PR_4_EA_INODE_REF_COUNT, &pctx)) { @@ -188,6 +198,7 @@ void e2fsck_pass4(e2fsck_t ctx) /* Protect loop from wrap-around if s_inodes_count maxed */ for (i = 1; i <= fs->super->s_inodes_count && i > 0; i++) { ext2_ino_t last_ino = 0; + ea_value_t ea_refs; int isdir; if (ctx->flags & E2F_FLAG_SIGNAL_MASK) @@ -211,13 +222,19 @@ void e2fsck_pass4(e2fsck_t ctx) ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); - if (link_counted == 0) { - /* - * link_counted is expected to be 0 for an ea_inode. - * check_ea_inode() will update link_counted if - * necessary. - */ - check_ea_inode(ctx, i, &last_ino, inode, &link_counted); + if (ctx->ea_inode_refs) { + ea_refcount_fetch(ctx->ea_inode_refs, i, &ea_refs); + if (ea_refs) { + /* + * Final consolidation of EA inodes. We either + * decide the inode is fine and set link_counted + * to one, or we decide this is actually a + * normal file and clear EA_INODE flag, or + * decide the inode should just be deleted. + */ + check_ea_inode(ctx, i, &last_ino, inode, + &link_counted, ea_refs); + } } if (link_counted == 0) { diff --git a/e2fsck/problem.c b/e2fsck/problem.c index 6ad6fb8..e433281 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -1309,6 +1309,16 @@ static struct e2fsck_problem problem_table[] = { N_("Orphan file @i %i is not in use, but contains data. "), PROMPT_CLEAR, PR_PREEN_OK }, + /* EA_INODE flag set on a non-regular file */ + { PR_1_EA_INODE_NONREG, + N_("@i %i has the ea_inode flag set but is not a regular file. "), + PROMPT_CLEAR_FLAG, 0, 0, 0, 0 }, + + /* EA_INODE present but the file system is missing the ea_inode feature */ + { PR_1_EA_INODE_FEATURE, + N_("@i %i references EA inode but @S is missing EA_INODE feature\n"), + PROMPT_FIX, PR_PREEN_OK, 0, 0, 0 }, + /* Pass 1b errors */ /* Pass 1B: Rescan for duplicate/bad blocks */ @@ -1860,6 +1870,10 @@ static struct e2fsck_problem problem_table[] = { N_("Duplicate filename @E found. "), PROMPT_CLEAR, 0, 0, 0, 0 }, + /* Directory filename is null */ + { PR_2_EA_INODE_DIR_LINK, + N_("@E references EA @i %Di.\n"), + PROMPT_CLEAR, 0, 0, 0, 0 }, /* Pass 3 errors */ @@ -2102,6 +2116,10 @@ static struct e2fsck_problem problem_table[] = { N_("@d @i %i ref count set to overflow but could be exact value %N. "), PROMPT_FIX, PR_PREEN_OK, 0, 0, 0 }, + { PR_4_EA_INODE_SPURIOUS_FLAG, + N_("Regular @f @i %i has EA_INODE flag set. "), + PROMPT_CLEAR, PR_PREEN_OK, 0, 0, 0 }, + /* Pass 5 errors */ /* Pass 5: Checking group summary information */ @@ -2327,7 +2345,7 @@ static struct e2fsck_problem problem_table[] = { /* orphan_present set but orphan file is empty */ { PR_6_ORPHAN_PRESENT_CLEAN_FILE, N_("Feature orphan_present is set but orphan file is clean.\n"), - PROMPT_CLEAR, PR_PREEN_OK }, + PROMPT_CLEAR, PR_PREEN_OK | PR_NO_OK | PR_PREEN_NOMSG }, /* orphan_present set but orphan_file is not */ { PR_6_ORPHAN_PRESENT_NO_FILE, @@ -2689,7 +2707,7 @@ void print_e2fsck_message(FILE *f, e2fsck_t ctx, const char *msg, void fatal_error(e2fsck_t ctx, const char *msg) { - return; + exit(0); } void preenhalt(e2fsck_t ctx) diff --git a/e2fsck/problem.h b/e2fsck/problem.h index b47b0c6..ef15b8c 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -734,6 +734,12 @@ struct problem_context { /* Orphan file inode is not in use, but contains data */ #define PR_1_ORPHAN_FILE_NOT_CLEAR 0x010090 +/* Inode has EA_INODE_FL set but is not a regular file */ +#define PR_1_EA_INODE_NONREG 0x010091 + +/* Inode references EA inode but ea_inode feature is not enabled */ +#define PR_1_EA_INODE_FEATURE 0x010092 + /* * Pass 1b errors */ @@ -1061,6 +1067,9 @@ struct problem_context { /* Non-unique filename found, but can't rename */ #define PR_2_NON_UNIQUE_FILE_NO_RENAME 0x020054 +/* EA inode referenced from directory */ +#define PR_2_EA_INODE_DIR_LINK 0x020055 + /* * Pass 3 errors */ @@ -1203,6 +1212,9 @@ struct problem_context { /* Directory ref count set to overflow but it doesn't have to be */ #define PR_4_DIR_OVERFLOW_REF_COUNT 0x040007 +/* EA_INODE_FL set on normal file linked from directory hierarchy */ +#define PR_4_EA_INODE_SPURIOUS_FLAG 0x040008 + /* * Pass 5 errors */ diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c index c1da7d5..4847d17 100644 --- a/e2fsck/rehash.c +++ b/e2fsck/rehash.c @@ -987,14 +987,18 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino, { ext2_filsys fs = ctx->fs; errcode_t retval; - struct ext2_inode inode; + struct ext2_inode_large inode; char *dir_buf = 0; struct fill_dir_struct fd = { NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0 }; struct out_dir outdir = { 0, 0, 0, 0 }; - struct name_cmp_ctx name_cmp_ctx = {0, NULL}; + struct name_cmp_ctx name_cmp_ctx = {0, NULL}; + __u64 osize; - e2fsck_read_inode(ctx, ino, &inode, "rehash_dir"); + e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode), + sizeof(inode), "rehash_dir"); + + osize = EXT2_I_SIZE(&inode); if (ext2fs_has_feature_inline_data(fs->super) && (inode.i_flags & EXT4_INLINE_DATA_FL)) @@ -1013,7 +1017,7 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino, fd.ino = ino; fd.ctx = ctx; fd.buf = dir_buf; - fd.inode = &inode; + fd.inode = EXT2_INODE(&inode); fd.dir = ino; if (!ext2fs_has_feature_dir_index(fs->super) || (inode.i_size / fs->blocksize) < 2) @@ -1092,14 +1096,25 @@ resort: goto errout; } - retval = write_directory(ctx, fs, &outdir, ino, &inode, fd.compress); + retval = write_directory(ctx, fs, &outdir, ino, EXT2_INODE(&inode), + fd.compress); if (retval) goto errout; + if ((osize > EXT2_I_SIZE(&inode)) && + (ino != quota_type2inum(PRJQUOTA, fs->super)) && + (ino != fs->super->s_orphan_file_inum) && + (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) && + !(inode.i_flags & EXT4_EA_INODE_FL)) { + quota_data_sub(ctx->qctx, &inode, + ino, osize - EXT2_I_SIZE(&inode)); + } + if (ctx->options & E2F_OPT_CONVERT_BMAP) retval = e2fsck_rebuild_extents_later(ctx, ino); else - retval = e2fsck_check_rebuild_extents(ctx, ino, &inode, pctx); + retval = e2fsck_check_rebuild_extents(ctx, ino, + EXT2_INODE(&inode), pctx); errout: ext2fs_free_mem(&dir_buf); ext2fs_free_mem(&fd.harray); diff --git a/e2fsck/sigcatcher.c b/e2fsck/sigcatcher.c index a9d3b7f..e202734 100644 --- a/e2fsck/sigcatcher.c +++ b/e2fsck/sigcatcher.c @@ -413,7 +413,7 @@ int main(int argc, char** argv) struct sigaction sa; char *p = 0; int i, c; - volatile x=0; + volatile int x = 0; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_sigaction = die_signal_handler; diff --git a/e2fsck/super.c b/e2fsck/super.c index 9495e02..04d6dde 100644 --- a/e2fsck/super.c +++ b/e2fsck/super.c @@ -196,7 +196,7 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, __u32 count; if (!ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(inode))) - return 0; + goto release_acl; pb.buf = block_buf + 3 * ctx->fs->blocksize; pb.ctx = ctx; @@ -235,7 +235,7 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, if (pb.truncated_blocks) ext2fs_iblk_sub_blocks(fs, EXT2_INODE(inode), pb.truncated_blocks); - +release_acl: blk = ext2fs_file_acl_block(fs, EXT2_INODE(inode)); if (blk) { retval = ext2fs_adjust_ea_refcount3(fs, blk, block_buf, -1, @@ -348,7 +348,7 @@ static int release_orphan_inode(e2fsck_t ctx, ext2_ino_t *ino, char *block_buf) ext2fs_inode_alloc_stats2(fs, *ino, -1, LINUX_S_ISDIR(inode.i_mode)); ctx->free_inodes++; - inode.i_dtime = ctx->now; + ext2fs_set_dtime(fs, EXT2_INODE(&inode)); } else { inode.i_dtime = 0; } @@ -1320,25 +1320,25 @@ void check_super_block(e2fsck_t ctx) */ if (((ctx->options & E2F_OPT_FORCE) || fs->super->s_checkinterval) && !broken_system_clock && !(ctx->flags & E2F_FLAG_TIME_INSANE) && - (fs->super->s_mtime > (__u32) ctx->now)) { - pctx.num = fs->super->s_mtime; + (ext2fs_get_tstamp(fs->super, s_mtime) > ctx->now)) { + pctx.num = ext2fs_get_tstamp(fs->super, s_mtime); problem = PR_0_FUTURE_SB_LAST_MOUNT; - if (fs->super->s_mtime <= (__u32) ctx->now + ctx->time_fudge) + if ((time_t) pctx.num <= ctx->now + ctx->time_fudge) problem = PR_0_FUTURE_SB_LAST_MOUNT_FUDGED; if (fix_problem(ctx, problem, &pctx)) { - fs->super->s_mtime = ctx->now; + ext2fs_set_tstamp(fs->super, s_mtime, ctx->now); fs->flags |= EXT2_FLAG_DIRTY; } } if (((ctx->options & E2F_OPT_FORCE) || fs->super->s_checkinterval) && !broken_system_clock && !(ctx->flags & E2F_FLAG_TIME_INSANE) && - (fs->super->s_wtime > (__u32) ctx->now)) { - pctx.num = fs->super->s_wtime; + (ext2fs_get_tstamp(fs->super, s_wtime) > ctx->now)) { + pctx.num = ext2fs_get_tstamp(fs->super, s_wtime); problem = PR_0_FUTURE_SB_LAST_WRITE; - if (fs->super->s_wtime <= (__u32) ctx->now + ctx->time_fudge) + if ((time_t) pctx.num <= ctx->now + ctx->time_fudge) problem = PR_0_FUTURE_SB_LAST_WRITE_FUDGED; if (fix_problem(ctx, problem, &pctx)) { - fs->super->s_wtime = ctx->now; + ext2fs_set_tstamp(fs->super, s_wtime, ctx->now); fs->flags |= EXT2_FLAG_DIRTY; } } @@ -1388,7 +1388,8 @@ void check_super_block(e2fsck_t ctx) * away. */ #define FEATURE_RO_COMPAT_IGNORE (EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ - EXT4_FEATURE_RO_COMPAT_DIR_NLINK) + EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ + EXT4_FEATURE_RO_COMPAT_ORPHAN_PRESENT) #define FEATURE_INCOMPAT_IGNORE (EXT3_FEATURE_INCOMPAT_EXTENTS| \ EXT3_FEATURE_INCOMPAT_RECOVER) diff --git a/e2fsck/unix.c b/e2fsck/unix.c index e5b672a..de20b21 100644 --- a/e2fsck/unix.c +++ b/e2fsck/unix.c @@ -342,6 +342,7 @@ static int is_on_batt(void) static void check_if_skip(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; + struct ext2_super_block *sb = fs->super; struct problem_context pctx; const char *reason = NULL; unsigned int reason_arg = 0; @@ -370,7 +371,7 @@ static void check_if_skip(e2fsck_t ctx) if (ctx->options & E2F_OPT_JOURNAL_ONLY) goto skip; - lastcheck = fs->super->s_lastcheck; + lastcheck = ext2fs_get_tstamp(sb, s_lastcheck); if (lastcheck > ctx->now) lastcheck -= ctx->time_fudge; if ((fs->super->s_state & EXT2_ERROR_FS) || @@ -397,8 +398,9 @@ static void check_if_skip(e2fsck_t ctx) ((ctx->now - lastcheck) >= ((time_t) fs->super->s_checkinterval))) { reason = _(" has gone %u days without being checked"); - reason_arg = (ctx->now - fs->super->s_lastcheck)/(3600*24); - if (batt && ((ctx->now - fs->super->s_lastcheck) < + reason_arg = (ctx->now - ext2fs_get_tstamp(sb, s_lastcheck)) / + (3600*24); + if (batt && ((ctx->now - ext2fs_get_tstamp(sb, s_lastcheck)) < fs->super->s_checkinterval*2)) reason = 0; } else if (broken_system_clock && fs->super->s_checkinterval) { @@ -457,7 +459,8 @@ static void check_if_skip(e2fsck_t ctx) next_check = 1; } if (!broken_system_clock && fs->super->s_checkinterval && - ((ctx->now - fs->super->s_lastcheck) >= fs->super->s_checkinterval)) + ((ctx->now - ext2fs_get_tstamp(sb, s_lastcheck)) >= + fs->super->s_checkinterval)) next_check = 1; if (next_check <= 5) { if (next_check == 1) { @@ -2080,7 +2083,7 @@ cleanup: } else sb->s_state &= ~EXT2_VALID_FS; if (!(ctx->flags & E2F_FLAG_TIME_INSANE)) - sb->s_lastcheck = ctx->now; + ext2fs_set_tstamp(sb, s_lastcheck, ctx->now); sb->s_mnt_count = 0; memset(((char *) sb) + EXT4_S_ERR_START, 0, EXT4_S_ERR_LEN); pctx.errcode = ext2fs_set_gdt_csum(ctx->fs); diff --git a/e2fsck/util.c b/e2fsck/util.c index 42740d9..27b3a0d 100644 --- a/e2fsck/util.c +++ b/e2fsck/util.c @@ -441,7 +441,8 @@ void print_resource_track(e2fsck_t ctx, const char *desc, } else #elif defined HAVE_MALLINFO /* don't use mallinfo() if over 2GB used, since it returns "int" */ - if ((char *)sbrk(0) - (char *)track->brk_start < 2LL << 30) { + if ((unsigned long)((char *)sbrk(0) - (char *)track->brk_start) < + 2UL << 30) { struct mallinfo malloc_info = mallinfo(); log_out(ctx, _("Memory used: %lluk/%lluk (%lluk/%lluk), "), @@ -559,29 +560,20 @@ blk64_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name, struct ext2_super_block *sb; io_channel io = NULL; void *buf = NULL; - int blocksize; - blk64_t superblock, ret_sb = 8193; + unsigned int blocksize = EXT2_MIN_BLOCK_SIZE; + int blocksize_known = 0; + blk_t bpg = 0; + blk64_t ret_sb = 8193; if (fs && fs->super) { - ret_sb = (fs->super->s_blocks_per_group + - fs->super->s_first_data_block); - if (ctx) { - ctx->superblock = ret_sb; - ctx->blocksize = fs->blocksize; - } - return ret_sb; + blocksize = fs->blocksize; + blocksize_known = 1; + bpg = fs->super->s_blocks_per_group; } - if (ctx) { - if (ctx->blocksize) { - ret_sb = ctx->blocksize * 8; - if (ctx->blocksize == 1024) - ret_sb++; - ctx->superblock = ret_sb; - return ret_sb; - } - ctx->superblock = ret_sb; - ctx->blocksize = 1024; + if (ctx && ctx->blocksize) { + blocksize = ctx->blocksize; + blocksize_known = 1; } if (!name || !manager) @@ -594,28 +586,54 @@ blk64_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name, goto cleanup; sb = (struct ext2_super_block *) buf; - for (blocksize = EXT2_MIN_BLOCK_SIZE; - blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) { - superblock = blocksize*8; - if (blocksize == 1024) - superblock++; + for (; blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) { + dgrp_t grp, three = 1, five = 5, seven = 7; + dgrp_t limit; + blk_t this_bpg = bpg ? bpg : blocksize * 8; + blk64_t num_blocks; + + if (fs && fs->super) { + limit = ext2fs_blocks_count(fs->super) / this_bpg; + } else if (ctx && ext2fs_get_device_size2(ctx->filesystem_name, + blocksize, + &num_blocks) == 0) { + limit = num_blocks / this_bpg; + } else { + /* If we can't figure out the device size, + * arbitrarily set a limit which is enough for + * 8 block groups or so... + */ + limit = 128; + } + io_channel_set_blksize(io, blocksize); - if (io_channel_read_blk64(io, superblock, - -SUPERBLOCK_SIZE, buf)) - continue; + + while ((grp = ext2fs_list_backups(NULL, &three, + &five, &seven)) <= limit) { + blk64_t superblock = (blk64_t)grp * this_bpg; + + if (blocksize == 1024) + superblock++; + if (io_channel_read_blk64(io, superblock, + -SUPERBLOCK_SIZE, buf)) + continue; #ifdef WORDS_BIGENDIAN - if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) - ext2fs_swap_super(sb); + if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) + ext2fs_swap_super(sb); #endif - if ((sb->s_magic == EXT2_SUPER_MAGIC) && - (EXT2_BLOCK_SIZE(sb) == blocksize)) { - ret_sb = superblock; - if (ctx) { - ctx->superblock = superblock; - ctx->blocksize = blocksize; + if ((sb->s_magic == EXT2_SUPER_MAGIC) && + ((unsigned) EXT2_BLOCK_SIZE(sb) == blocksize)) { + ret_sb = superblock; + if (ctx) { + ctx->superblock = superblock; + ctx->blocksize = blocksize; + } + goto cleanup; } - break; } + + if (blocksize_known) + break; } cleanup: -- cgit v1.2.3