diff options
36 files changed, 1518 insertions, 150 deletions
diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in index 637d22b3..5b5329c3 100644 --- a/debugfs/debugfs.8.in +++ b/debugfs/debugfs.8.in @@ -505,7 +505,7 @@ which is a hard link to .IR filespec . Note this does not adjust the inode reference counts. .TP -.BI logdump " [-acsOS] [-b block] [-i filespec] [-f journal_file] [output_file]" +.BI logdump " [-acsOS] [-b block] [-n num_trans ] [-i filespec] [-f journal_file] [output_file]" Dump the contents of the ext3 journal. By default, dump the journal inode as specified in the superblock. However, this can be overridden with the .I \-i @@ -528,7 +528,7 @@ The .I \-a option causes the .B logdump -program to print the contents of all of the descriptor blocks. +to print the contents of all of the descriptor blocks. The .I \-b option causes @@ -548,6 +548,15 @@ The option causes logdump to display old (checkpointed) journal entries. This can be used to try to track down journal problems even after the journal has been replayed. +.IP +The +.I \-n +option causes +.B logdump +to continue past a journal block which is missing a magic number. +Instead, it will stop only when the entire log is printed or after +.I num_trans +transactions. .TP .BI ls " [-l] [-c] [-d] [-p] [-r] filespec" Print a listing of the files in the directory diff --git a/debugfs/logdump.c b/debugfs/logdump.c index 6b0133e0..b600228e 100644 --- a/debugfs/logdump.c +++ b/debugfs/logdump.c @@ -48,6 +48,7 @@ enum journal_location {JOURNAL_IS_INTERNAL, JOURNAL_IS_EXTERNAL}; #define ANY_BLOCK ((blk64_t) -1) static int dump_all, dump_super, dump_old, dump_contents, dump_descriptors; +static int64_t dump_counts; static blk64_t block_to_dump, bitmap_to_dump, inode_block_to_dump; static unsigned int group_to_dump, inode_offset_to_dump; static ext2_ino_t inode_to_dump; @@ -113,9 +114,10 @@ void do_logdump(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), bitmap_to_dump = -1; inode_block_to_dump = ANY_BLOCK; inode_to_dump = -1; + dump_counts = -1; reset_getopt(); - while ((c = getopt (argc, argv, "ab:ci:f:OsS")) != EOF) { + while ((c = getopt (argc, argv, "ab:ci:f:OsSn:")) != EOF) { switch (c) { case 'a': dump_all++; @@ -148,6 +150,14 @@ void do_logdump(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), case 'S': dump_super++; break; + case 'n': + dump_counts = strtol(optarg, &tmp, 10); + if (*tmp) { + com_err(argv[0], 0, + "Bad log counts number - %s", optarg); + return; + } + break; default: goto print_usage; } @@ -289,7 +299,7 @@ cleanup: return; print_usage: - fprintf(stderr, "%s: Usage: logdump [-acsOS] [-b<block>] [-i<filespec>]\n\t" + fprintf(stderr, "%s: Usage: logdump [-acsOS] [-n<num_trans>] [-b<block>] [-i<filespec>]\n\t" "[-f<journal_file>] [output_file]\n", argv[0]); } @@ -366,9 +376,12 @@ static void dump_journal(char *cmdname, FILE *out_file, journal_header_t *header; tid_t transaction; unsigned int blocknr = 0; + unsigned int first_transaction_blocknr; int fc_done; __u64 total_len; __u32 maxlen; + int64_t cur_counts = 0; + bool exist_no_magic = false; /* First, check to see if there's an ext2 superblock header */ retval = read_journal_block(cmdname, source, 0, buf, 2048); @@ -458,7 +471,19 @@ static void dump_journal(char *cmdname, FILE *out_file, blocknr = 1; } + first_transaction_blocknr = blocknr; + while (1) { + if (dump_old && (dump_counts != -1) && (cur_counts >= dump_counts)) + break; + + if ((blocknr == first_transaction_blocknr) && + (cur_counts != 0) && dump_old && (dump_counts != -1)) { + fprintf(out_file, "Dump all %lld journal records.\n", + (long long) cur_counts); + break; + } + retval = read_journal_block(cmdname, source, ((ext2_loff_t) blocknr) * blocksize, buf, blocksize); @@ -472,8 +497,16 @@ static void dump_journal(char *cmdname, FILE *out_file, blocktype = be32_to_cpu(header->h_blocktype); if (magic != JBD2_MAGIC_NUMBER) { - fprintf (out_file, "No magic number at block %u: " - "end of journal.\n", blocknr); + if (exist_no_magic == false) { + exist_no_magic = true; + fprintf(out_file, "No magic number at block %u: " + "end of journal.\n", blocknr); + } + if (dump_old && (dump_counts != -1)) { + blocknr++; + WRAP(jsb, blocknr, maxlen); + continue; + } break; } @@ -500,6 +533,7 @@ static void dump_journal(char *cmdname, FILE *out_file, continue; case JBD2_COMMIT_BLOCK: + cur_counts++; transaction++; blocknr++; WRAP(jsb, blocknr, maxlen); diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c index b0015794..f916deab 100644 --- a/debugfs/set_fields.c +++ b/debugfs/set_fields.c @@ -183,6 +183,7 @@ static struct field_set_info super_fields[] = { { "lpf_ino", &set_sb.s_lpf_ino, NULL, 4, parse_uint }, { "checksum_seed", &set_sb.s_checksum_seed, NULL, 4, parse_uint }, { "encoding", &set_sb.s_encoding, NULL, 2, parse_encoding }, + { "orphan_file_inum", &set_sb.s_orphan_file_inum, NULL, 4, parse_uint }, { 0, 0, 0, 0 } }; diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index b8caa43b..3f2dc308 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -656,6 +656,7 @@ void sigcatcher_setup(void); void check_super_block(e2fsck_t ctx); int check_backup_super_block(e2fsck_t ctx); void check_resize_inode(e2fsck_t ctx); +int check_init_orphan_file(e2fsck_t ctx); /* util.c */ extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned long size, diff --git a/e2fsck/journal.c b/e2fsck/journal.c index 8ae89bf7..c7868d89 100644 --- a/e2fsck/journal.c +++ b/e2fsck/journal.c @@ -610,10 +610,10 @@ static void ext4_fc_flush_extents(e2fsck_t ctx, ext2_ino_t ino) /* Helper struct for dentry replay routines */ struct dentry_info_args { - ext2_ino_t parent_ino; - int dname_len; - ext2_ino_t ino; - char *dname; + ext2_ino_t parent_ino; + ext2_ino_t ino; + int dname_len; + char *dname; }; static inline int tl_to_darg(struct dentry_info_args *darg, diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 78540119..a341c72a 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -918,6 +918,7 @@ static void reserve_block_for_lnf_repair(e2fsck_t ctx) } static errcode_t get_inline_data_ea_size(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, size_t *sz) { void *p; @@ -928,7 +929,8 @@ static errcode_t get_inline_data_ea_size(ext2_filsys fs, ext2_ino_t ino, if (retval) return retval; - retval = ext2fs_xattrs_read(handle); + retval = ext2fs_xattrs_read_inode(handle, + (struct ext2_inode_large *)inode); if (retval) goto err; @@ -1515,7 +1517,8 @@ void e2fsck_pass1(e2fsck_t ctx) (ino >= EXT2_FIRST_INODE(fs->super))) { size_t size = 0; - pctx.errcode = get_inline_data_ea_size(fs, ino, &size); + pctx.errcode = get_inline_data_ea_size(fs, ino, inode, + &size); if (!pctx.errcode && fix_problem(ctx, PR_1_INLINE_DATA_FEATURE, &pctx)) { ext2fs_set_feature_inline_data(sb); @@ -1538,7 +1541,7 @@ void e2fsck_pass1(e2fsck_t ctx) flags = fs->flags; if (failed_csum) fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; - err = get_inline_data_ea_size(fs, ino, &size); + err = get_inline_data_ea_size(fs, ino, inode, &size); fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) | (fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS); @@ -1785,6 +1788,32 @@ void e2fsck_pass1(e2fsck_t ctx) inode_size, "pass1"); failed_csum = 0; } + } else if (ino == fs->super->s_orphan_file_inum) { + ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino); + if (ext2fs_has_feature_orphan_file(fs->super)) { + if (!LINUX_S_ISREG(inode->i_mode) && + fix_problem(ctx, PR_1_ORPHAN_FILE_BAD_MODE, + &pctx)) { + inode->i_mode = LINUX_S_IFREG; + e2fsck_write_inode(ctx, ino, inode, + "pass1"); + failed_csum = 0; + } + check_blocks(ctx, &pctx, block_buf, NULL); + FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum); + continue; + } + if ((inode->i_links_count || + inode->i_blocks || inode->i_block[0]) && + fix_problem(ctx, PR_1_ORPHAN_FILE_NOT_CLEAR, + &pctx)) { + memset(inode, 0, inode_size); + ext2fs_icount_store(ctx->inode_link_info, ino, + 0); + e2fsck_write_inode_full(ctx, ino, inode, + inode_size, "pass1"); + failed_csum = 0; + } } else if (ino < EXT2_FIRST_INODE(fs->super)) { problem_t problem = 0; @@ -3494,6 +3523,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, } 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)) { quota_data_add(ctx->qctx, (struct ext2_inode_large *) inode, diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 28736094..410edd11 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -1446,7 +1446,8 @@ skip_checksum: (dirent->inode > fs->super->s_inodes_count) || (dirent->inode == fs->super->s_usr_quota_inum) || (dirent->inode == fs->super->s_grp_quota_inum) || - (dirent->inode == fs->super->s_prj_quota_inum)) { + (dirent->inode == fs->super->s_prj_quota_inum) || + (dirent->inode == fs->super->s_orphan_file_inum)) { problem = PR_2_BAD_INO; } else if (ctx->inode_bb_map && (ext2fs_test_inode_bitmap2(ctx->inode_bb_map, diff --git a/e2fsck/pass4.c b/e2fsck/pass4.c index 8c2d2f1f..d2dda02a 100644 --- a/e2fsck/pass4.c +++ b/e2fsck/pass4.c @@ -26,7 +26,7 @@ * This subroutine returns 1 then the caller shouldn't bother with the * rest of the pass 4 tests. */ -static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i, +static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i, ext2_ino_t *last_ino, struct ext2_inode_large *inode) { ext2_filsys fs = ctx->fs; @@ -34,9 +34,12 @@ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i, __u32 eamagic = 0; int extra_size = 0; - e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode), - EXT2_INODE_SIZE(fs->super), - "pass4: disconnect_inode"); + if (*last_ino != i) { + e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode), + EXT2_INODE_SIZE(fs->super), + "pass4: disconnect_inode"); + *last_ino = i; + } if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE) extra_size = inode->i_extra_isize; @@ -75,6 +78,7 @@ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i, if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) { if (e2fsck_reconnect_file(ctx, i)) ext2fs_unmark_valid(fs); + *last_ino = 0; } else { /* * If we don't attach the inode, then skip the @@ -87,20 +91,22 @@ static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i, return 0; } -static void check_ea_inode(e2fsck_t ctx, ext2_ino_t i, +/* + * This function is called when link_counted is zero. So this may not be + * 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) { __u64 actual_refs = 0; __u64 ref_count; - /* - * This function is called when link_counted is zero. So this may not - * be an xattr inode at all. Return immediately if EA_INODE flag is not - * set. - */ - e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode), - EXT2_INODE_SIZE(ctx->fs->super), - "pass4: check_ea_inode"); + if (*last_ino != i) { + e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode), + EXT2_INODE_SIZE(ctx->fs->super), + "pass4: check_ea_inode"); + *last_ino = i; + } if (!(inode->i_flags & EXT4_EA_INODE_FL)) return; @@ -180,7 +186,8 @@ void e2fsck_pass4(e2fsck_t ctx) inode = e2fsck_allocate_memory(ctx, inode_size, "scratch inode"); /* Protect loop from wrap-around if s_inodes_count maxed */ - for (i=1; i <= fs->super->s_inodes_count && i > 0; i++) { + for (i = 1; i <= fs->super->s_inodes_count && i > 0; i++) { + ext2_ino_t last_ino = 0; int isdir; if (ctx->flags & E2F_FLAG_SIGNAL_MASK) @@ -192,7 +199,7 @@ void e2fsck_pass4(e2fsck_t ctx) goto errout; } if (i == quota_type2inum(PRJQUOTA, ctx->fs->super) || - i == EXT2_BAD_INO || + i == fs->super->s_orphan_file_inum || i == EXT2_BAD_INO || (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super))) continue; if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, i)) || @@ -210,7 +217,7 @@ void e2fsck_pass4(e2fsck_t ctx) * check_ea_inode() will update link_counted if * necessary. */ - check_ea_inode(ctx, i, inode, &link_counted); + check_ea_inode(ctx, i, &last_ino, inode, &link_counted); } if (link_counted == 0) { @@ -219,7 +226,7 @@ void e2fsck_pass4(e2fsck_t ctx) fs->blocksize, "bad_inode buffer"); if (e2fsck_process_bad_inode(ctx, 0, i, buf)) continue; - if (disconnect_inode(ctx, i, inode)) + if (disconnect_inode(ctx, i, &last_ino, inode)) continue; ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); @@ -239,8 +246,12 @@ void e2fsck_pass4(e2fsck_t ctx) if (link_counted != link_count) { int fix_nlink = 0; - e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode), - inode_size, "pass4"); + if (last_ino != i) { + e2fsck_read_inode_full(ctx, i, + EXT2_INODE(inode), + inode_size, "pass4"); + last_ino = i; + } pctx.ino = i; pctx.inode = EXT2_INODE(inode); if ((link_count != inode->i_links_count) && !isdir && diff --git a/e2fsck/problem.c b/e2fsck/problem.c index e2572f59..6ad6fb84 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -526,6 +526,26 @@ static struct e2fsck_problem problem_table[] = { "not compatible. Resize @i should be disabled. "), PROMPT_FIX, 0, 0, 0, 0 }, + /* Orphan file contains holes */ + { PR_0_ORPHAN_FILE_HOLE, + N_("Orphan file (@i %i) contains hole at @b %b. Terminating orphan file recovery.\n"), + PROMPT_NONE, 0 }, + + /* Orphan file block has wrong magic */ + { PR_0_ORPHAN_FILE_BAD_MAGIC, + N_("Orphan file (@i %i) @b %b contains wrong magic. Terminating orphan file recovery.\n"), + PROMPT_NONE, 0 }, + + /* Orphan file block has wrong checksum */ + { PR_0_ORPHAN_FILE_BAD_CHECKSUM, + N_("Orphan file (@i %i) @b %b contains wrong checksum. Terminating orphan file recovery.\n"), + PROMPT_NONE, 0 }, + + /* Orphan file size isn't multiple of blocks size */ + { PR_0_ORPHAN_FILE_WRONG_SIZE, + N_("Orphan file (@i %i) size is not multiple of block size. Terminating orphan file recovery.\n"), + PROMPT_NONE, 0 }, + /* Pass 1 errors */ /* Pass 1: Checking inodes, blocks, and sizes */ @@ -1279,6 +1299,15 @@ static struct e2fsck_problem problem_table[] = { N_("@h %i uses SipHash, but should not. "), PROMPT_CLEAR_HTREE, PR_PREEN_OK, 0, 0, 0 }, + /* Orphan file has bad mode */ + { PR_1_ORPHAN_FILE_BAD_MODE, + N_("Orphan file @i %i is not regular file. "), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Orphan file inode is not in use, but contains data */ + { PR_1_ORPHAN_FILE_NOT_CLEAR, + N_("Orphan file @i %i is not in use, but contains data. "), + PROMPT_CLEAR, PR_PREEN_OK }, /* Pass 1b errors */ @@ -2265,6 +2294,56 @@ static struct e2fsck_problem problem_table[] = { N_("Error writing quota info for quota type %N: %m\n"), PROMPT_NULL, 0, 0, 0, 0 }, + /* Orphan file without a journal */ + { PR_6_ORPHAN_FILE_WITHOUT_JOURNAL, + N_("@S has orphan file without @j.\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Orphan file truncation failed */ + { PR_6_ORPHAN_FILE_TRUNC_FAILED, + N_("Failed to truncate orphan file.\n"), + PROMPT_NONE, 0 }, + + /* Failed to initialize orphan file */ + { PR_6_ORPHAN_FILE_CORRUPTED, + N_("Failed to initialize orphan file.\n"), + PROMPT_RECREATE, PR_PREEN_OK }, + + /* Cannot fix corrupted orphan file with invalid bitmaps */ + { PR_6_ORPHAN_FILE_BITMAP_INVALID, + N_("Cannot fix corrupted orphan file with invalid bitmaps.\n"), + PROMPT_NONE, 0 }, + + /* Orphan file creation failed */ + { PR_6_ORPHAN_FILE_CREATE_FAILED, + N_("Failed to truncate orphan file (@i %i).\n"), + PROMPT_NONE, 0 }, + + /* Orphan file block contains data */ + { PR_6_ORPHAN_BLOCK_DIRTY, + N_("Orphan file (@i %i) @b %b is not clean.\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* 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 }, + + /* orphan_present set but orphan_file is not */ + { PR_6_ORPHAN_PRESENT_NO_FILE, + N_("Feature orphan_present is set but feature orphan_file is not.\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Orphan file size isn't multiple of blocks size */ + { PR_6_ORPHAN_FILE_WRONG_SIZE, + N_("Orphan file (@i %i) size is not multiple of block size.\n"), + PROMPT_NONE, 0 }, + + /* Orphan file contains holes */ + { PR_6_ORPHAN_FILE_HOLE, + N_("Orphan file (@i %i) contains hole at @b %b.\n"), + PROMPT_NONE, 0 }, + { 0 } }; diff --git a/e2fsck/problem.h b/e2fsck/problem.h index e86bc889..b47b0c63 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -288,6 +288,18 @@ struct problem_context { /* Meta_bg and resize_inode are not compatible, remove resize_inode*/ #define PR_0_DISABLE_RESIZE_INODE 0x000051 +/* Orphan file contains holes */ +#define PR_0_ORPHAN_FILE_HOLE 0x000052 + +/* Orphan file block has wrong magic */ +#define PR_0_ORPHAN_FILE_BAD_MAGIC 0x000053 + +/* Orphan file block has wrong checksum */ +#define PR_0_ORPHAN_FILE_BAD_CHECKSUM 0x000054 + +/* Orphan file size isn't multiple of blocks size */ +#define PR_0_ORPHAN_FILE_WRONG_SIZE 0x000055 + /* * Pass 1 errors */ @@ -716,6 +728,11 @@ struct problem_context { /* Htree directory uses SipHash but should not */ #define PR_1_HTREE_CANNOT_SIPHASH 0x01008E +/* Orphan file inode is not a regular file */ +#define PR_1_ORPHAN_FILE_BAD_MODE 0x01008F + +/* Orphan file inode is not in use, but contains data */ +#define PR_1_ORPHAN_FILE_NOT_CLEAR 0x010090 /* * Pass 1b errors @@ -1296,6 +1313,35 @@ struct problem_context { /* Error updating quota information */ #define PR_6_WRITE_QUOTAS 0x060006 +/* Orphan file without a journal */ +#define PR_6_ORPHAN_FILE_WITHOUT_JOURNAL 0x060007 + +/* Orphan file truncation failed */ +#define PR_6_ORPHAN_FILE_TRUNC_FAILED 0x060008 + +/* Failed to initialize orphan file */ +#define PR_6_ORPHAN_FILE_CORRUPTED 0x060009 + +/* Cannot fix corrupted orphan file with invalid bitmaps */ +#define PR_6_ORPHAN_FILE_BITMAP_INVALID 0x06000A + +/* Orphan file creation failed */ +#define PR_6_ORPHAN_FILE_CREATE_FAILED 0x06000B + +/* Orphan file block contains data */ +#define PR_6_ORPHAN_BLOCK_DIRTY 0x06000C + +/* orphan_present set but orphan file is empty */ +#define PR_6_ORPHAN_PRESENT_CLEAN_FILE 0x06000D + +/* orphan_present set but orphan_file is not */ +#define PR_6_ORPHAN_PRESENT_NO_FILE 0x06000E + +/* Orphan file size isn't multiple of blocks size */ +#define PR_6_ORPHAN_FILE_WRONG_SIZE 0x06000F + +/* Orphan file contains holes */ +#define PR_6_ORPHAN_FILE_HOLE 0x060010 /* * Function declarations diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c index 8235e468..c1da7d52 100644 --- a/e2fsck/rehash.c +++ b/e2fsck/rehash.c @@ -89,9 +89,9 @@ struct fill_dir_struct { }; struct hash_entry { - ext2_dirhash_t hash; - ext2_dirhash_t minor_hash; - ext2_ino_t ino; + ext2_dirhash_t hash; + ext2_dirhash_t minor_hash; + ext2_ino_t ino; struct ext2_dir_entry *dir; }; diff --git a/e2fsck/super.c b/e2fsck/super.c index 123813be..9495e029 100644 --- a/e2fsck/super.c +++ b/e2fsck/super.c @@ -314,6 +314,180 @@ static errcode_t e2fsck_write_all_quotas(e2fsck_t ctx) return pctx.errcode; } +static int release_orphan_inode(e2fsck_t ctx, ext2_ino_t *ino, char *block_buf) +{ + ext2_filsys fs = ctx->fs; + struct problem_context pctx; + struct ext2_inode_large inode; + ext2_ino_t next_ino; + + e2fsck_read_inode_full(ctx, *ino, EXT2_INODE(&inode), + sizeof(inode), "release_orphan_inode"); + clear_problem_context(&pctx); + pctx.ino = *ino; + pctx.inode = EXT2_INODE(&inode); + pctx.str = inode.i_links_count ? _("Truncating") : _("Clearing"); + + fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx); + + next_ino = inode.i_dtime; + if (next_ino && + ((next_ino < EXT2_FIRST_INODE(fs->super)) || + (next_ino > fs->super->s_inodes_count))) { + pctx.ino = next_ino; + fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx); + return 1; + } + + if (release_inode_blocks(ctx, *ino, &inode, block_buf, &pctx)) + return 1; + + if (!inode.i_links_count) { + if (ctx->qctx) + quota_data_inodes(ctx->qctx, &inode, *ino, -1); + ext2fs_inode_alloc_stats2(fs, *ino, -1, + LINUX_S_ISDIR(inode.i_mode)); + ctx->free_inodes++; + inode.i_dtime = ctx->now; + } else { + inode.i_dtime = 0; + } + e2fsck_write_inode_full(ctx, *ino, EXT2_INODE(&inode), + sizeof(inode), "delete_file"); + *ino = next_ino; + return 0; +} + +struct process_orphan_block_data { + e2fsck_t ctx; + char *buf; + char *block_buf; + e2_blkcnt_t blocks; + int abort; + int clear; + errcode_t errcode; + ext2_ino_t ino; + __u32 generation; +}; + +static int process_orphan_block(ext2_filsys fs, + blk64_t *block_nr, + e2_blkcnt_t blockcnt, + blk64_t ref_blk EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct process_orphan_block_data *pd; + e2fsck_t ctx; + struct problem_context pctx; + blk64_t blk = *block_nr; + struct ext4_orphan_block_tail *tail; + int j; + int inodes_per_ob; + __u32 *bdata; + ext2_ino_t ino; + + pd = priv_data; + ctx = pd->ctx; + clear_problem_context(&pctx); + pctx.ino = fs->super->s_orphan_file_inum; + pctx.blk = blockcnt; + + /* Orphan file must not have holes */ + if (!blk) { + if (blockcnt == pd->blocks) + return BLOCK_ABORT; + fix_problem(ctx, PR_0_ORPHAN_FILE_HOLE, &pctx); +return_abort: + pd->abort = 1; + return BLOCK_ABORT; + } + inodes_per_ob = ext2fs_inodes_per_orphan_block(fs); + pd->errcode = io_channel_read_blk64(fs->io, blk, 1, pd->buf); + if (pd->errcode) + goto return_abort; + tail = ext2fs_orphan_block_tail(fs, pd->buf); + if (ext2fs_le32_to_cpu(tail->ob_magic) != + EXT4_ORPHAN_BLOCK_MAGIC) { + fix_problem(ctx, PR_0_ORPHAN_FILE_BAD_MAGIC, &pctx); + goto return_abort; + } + if (!ext2fs_orphan_file_block_csum_verify(fs, + fs->super->s_orphan_file_inum, blk, pd->buf)) { + fix_problem(ctx, PR_0_ORPHAN_FILE_BAD_CHECKSUM, &pctx); + goto return_abort; + } + bdata = (__u32 *)pd->buf; + for (j = 0; j < inodes_per_ob; j++) { + if (!bdata[j]) + continue; + ino = ext2fs_le32_to_cpu(bdata[j]); + if (release_orphan_inode(ctx, &ino, pd->block_buf)) + goto return_abort; + } + return 0; +} + +static int process_orphan_file(e2fsck_t ctx, char *block_buf) +{ + ext2_filsys fs = ctx->fs; + char *orphan_buf; + struct process_orphan_block_data pd; + int ret = 0; + ext2_ino_t orphan_inum = fs->super->s_orphan_file_inum; + struct ext2_inode orphan_inode; + struct problem_context pctx; + errcode_t retval; + + if (!ext2fs_has_feature_orphan_file(fs->super)) + return 0; + + clear_problem_context(&pctx); + pctx.ino = orphan_inum; + + orphan_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4, + "orphan block buffer"); + retval = ext2fs_read_inode(fs, orphan_inum, &orphan_inode); + if (retval < 0) { + com_err("process_orphan_file", retval, + _("while reading inode %d"), orphan_inum); + ret = 1; + goto out; + } + if (EXT2_I_SIZE(&orphan_inode) & (fs->blocksize - 1)) { + fix_problem(ctx, PR_0_ORPHAN_FILE_WRONG_SIZE, &pctx); + ret = 1; + goto out; + } + pd.buf = orphan_buf + 3 * fs->blocksize; + pd.block_buf = block_buf; + pd.blocks = EXT2_I_SIZE(&orphan_inode) / fs->blocksize; + pd.ctx = ctx; + pd.abort = 0; + pd.errcode = 0; + retval = ext2fs_block_iterate3(fs, orphan_inum, + BLOCK_FLAG_DATA_ONLY | BLOCK_FLAG_HOLE, + orphan_buf, process_orphan_block, &pd); + if (retval) { + com_err("process_orphan_block", retval, + _("while calling ext2fs_block_iterate for inode %d"), + orphan_inum); + ret = 1; + goto out; + } + if (pd.abort) { + if (pd.errcode) { + com_err("process_orphan_block", pd.errcode, + _("while reading blocks of inode %d"), + orphan_inum); + } + ret = 1; + } +out: + ext2fs_free_mem(&orphan_buf); + return ret; +} + /* * This function releases all of the orphan inodes. It returns 1 if * it hit some error, and 0 on success. @@ -321,15 +495,17 @@ static errcode_t e2fsck_write_all_quotas(e2fsck_t ctx) static int release_orphan_inodes(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; - ext2_ino_t ino, next_ino; - struct ext2_inode_large inode; + ext2_ino_t ino; struct problem_context pctx; char *block_buf; - if ((ino = fs->super->s_last_orphan) == 0) + if (fs->super->s_last_orphan == 0 && + !ext2fs_has_feature_orphan_present(fs->super)) return 0; clear_problem_context(&pctx); + ino = fs->super->s_last_orphan; + pctx.ino = ino; pctx.errcode = e2fsck_read_all_quotas(ctx); if (pctx.errcode) { fix_problem(ctx, PR_0_QUOTA_INIT_CTX, &pctx); @@ -344,9 +520,10 @@ static int release_orphan_inodes(e2fsck_t ctx) ext2fs_mark_super_dirty(fs); /* - * If the filesystem contains errors, don't run the orphan - * list, since the orphan list can't be trusted; and we're - * going to be running a full e2fsck run anyway... + * If the filesystem contains errors, don't process the orphan list + * or orphan file, since neither can be trusted; and we're going to + * be running a full e2fsck run anyway... We clear orphan file contents + * after filesystem is checked to avoid clearing someone else's data. */ if (fs->super->s_state & EXT2_ERROR_FS) { if (ctx->qctx) @@ -354,10 +531,8 @@ static int release_orphan_inodes(e2fsck_t ctx) return 0; } - if ((ino < EXT2_FIRST_INODE(fs->super)) || - (ino > fs->super->s_inodes_count)) { - clear_problem_context(&pctx); - pctx.ino = ino; + if (ino && ((ino < EXT2_FIRST_INODE(fs->super)) || + (ino > fs->super->s_inodes_count))) { fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx); goto err_qctx; } @@ -366,43 +541,19 @@ static int release_orphan_inodes(e2fsck_t ctx) "block iterate buffer"); e2fsck_read_bitmaps(ctx); + /* First process orphan list */ while (ino) { - e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode), - sizeof(inode), "release_orphan_inodes"); - clear_problem_context(&pctx); - pctx.ino = ino; - pctx.inode = EXT2_INODE(&inode); - pctx.str = inode.i_links_count ? _("Truncating") : - _("Clearing"); - - fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx); - - next_ino = inode.i_dtime; - if (next_ino && - ((next_ino < EXT2_FIRST_INODE(fs->super)) || - (next_ino > fs->super->s_inodes_count))) { - pctx.ino = next_ino; - fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx); + if (release_orphan_inode(ctx, &ino, block_buf)) goto err_buf; - } + } - if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx)) - goto err_buf; + /* Next process orphan file */ + if (ext2fs_has_feature_orphan_present(fs->super) && + !ext2fs_has_feature_orphan_file(fs->super)) + goto err_buf; + if (process_orphan_file(ctx, block_buf)) + goto err_buf; - if (!inode.i_links_count) { - if (ctx->qctx) - quota_data_inodes(ctx->qctx, &inode, ino, -1); - ext2fs_inode_alloc_stats2(fs, ino, -1, - LINUX_S_ISDIR(inode.i_mode)); - ctx->free_inodes++; - inode.i_dtime = ctx->now; - } else { - inode.i_dtime = 0; - } - e2fsck_write_inode_full(ctx, ino, EXT2_INODE(&inode), - sizeof(inode), "delete_file"); - ino = next_ino; - } ext2fs_free_mem(&block_buf); pctx.errcode = e2fsck_write_all_quotas(ctx); if (pctx.errcode) @@ -417,6 +568,134 @@ err: return 1; } +static int reinit_orphan_block(ext2_filsys fs, + blk64_t *block_nr, + e2_blkcnt_t blockcnt, + blk64_t ref_blk EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct process_orphan_block_data *pd; + e2fsck_t ctx; + blk64_t blk = *block_nr; + struct problem_context pctx; + + pd = priv_data; + ctx = pd->ctx; + + /* Orphan file must not have holes */ + if (!blk) { + if (blockcnt == pd->blocks) + return BLOCK_ABORT; + + clear_problem_context(&pctx); + pctx.ino = fs->super->s_orphan_file_inum; + pctx.blk = blockcnt; + fix_problem(ctx, PR_6_ORPHAN_FILE_HOLE, &pctx); +return_abort: + pd->abort = 1; + return BLOCK_ABORT; + } + + if (ext2fs_has_feature_metadata_csum(fs->super)) { + struct ext4_orphan_block_tail *tail; + + tail = ext2fs_orphan_block_tail(fs, pd->buf); + /* + * Update checksum to match expected buffer contents with + * appropriate block number. + */ + tail->ob_checksum = ext2fs_do_orphan_file_block_csum(fs, + pd->ino, pd->generation, blk, pd->buf); + } + if (!pd->clear) { + pd->errcode = io_channel_read_blk64(fs->io, blk, 1, + pd->block_buf); + /* Block is already cleanly initialized? */ + if (!memcmp(pd->block_buf, pd->buf, fs->blocksize)) + return 0; + + clear_problem_context(&pctx); + pctx.ino = fs->super->s_orphan_file_inum; + pctx.blk = blockcnt; + if (!fix_problem(ctx, PR_6_ORPHAN_BLOCK_DIRTY, &pctx)) + goto return_abort; + pd->clear = 1; + } + pd->errcode = io_channel_write_blk64(fs->io, blk, 1, pd->buf); + if (pd->errcode) + goto return_abort; + return 0; +} + +/* + * Check and clear orphan file. We just return non-zero if we hit some + * inconsistency. Caller will truncate & recreate new orphan file. + */ +int check_init_orphan_file(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + char *orphan_buf; + struct process_orphan_block_data pd; + struct ext4_orphan_block_tail *tail; + ext2_ino_t orphan_inum = fs->super->s_orphan_file_inum; + struct ext2_inode orphan_inode; + int ret = 0; + errcode_t retval; + + orphan_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 5, + "orphan block buffer"); + e2fsck_read_inode(ctx, orphan_inum, &orphan_inode, "orphan inode"); + if (EXT2_I_SIZE(&orphan_inode) & (fs->blocksize - 1)) { + struct problem_context pctx; + + clear_problem_context(&pctx); + pctx.ino = orphan_inum; + fix_problem(ctx, PR_6_ORPHAN_FILE_WRONG_SIZE, &pctx); + ret = 1; + goto out; + } + pd.buf = orphan_buf + 3 * fs->blocksize; + pd.block_buf = orphan_buf + 4 * fs->blocksize; + pd.blocks = EXT2_I_SIZE(&orphan_inode) / fs->blocksize; + pd.ctx = ctx; + pd.abort = 0; + pd.clear = 0; + pd.errcode = 0; + pd.ino = orphan_inum; + pd.generation = orphan_inode.i_generation; + /* Initialize buffer to write */ + memset(pd.buf, 0, fs->blocksize); + tail = ext2fs_orphan_block_tail(fs, pd.buf); + tail->ob_magic = ext2fs_cpu_to_le32(EXT4_ORPHAN_BLOCK_MAGIC); + + retval = ext2fs_block_iterate3(fs, orphan_inum, + BLOCK_FLAG_DATA_ONLY | BLOCK_FLAG_HOLE, + orphan_buf, reinit_orphan_block, &pd); + if (retval) { + com_err("reinit_orphan_block", retval, + _("while calling ext2fs_block_iterate for inode %d"), + orphan_inum); + ret = 1; + goto out; + } + if (pd.abort) { + if (pd.errcode) { + com_err("process_orphan_block", pd.errcode, + _("while reading blocks of inode %d"), + orphan_inum); + } + ret = 1; + } + + /* We had to clear some blocks. Report it up. */ + if (ret == 0 && pd.clear) + ret = 2; +out: + ext2fs_free_mem(&orphan_buf); + return ret; +} + /* * Check the resize inode to make sure it is sane. We check both for * the case where on-line resizing is not enabled (in which case the diff --git a/e2fsck/unix.c b/e2fsck/unix.c index 853eb296..e5b672a2 100644 --- a/e2fsck/unix.c +++ b/e2fsck/unix.c @@ -1954,15 +1954,82 @@ print_unsupp_features: _("\n*** journal has been regenerated ***\n")); } } -no_journal: +no_journal: if (run_result & E2F_FLAG_ABORT) { fatal_error(ctx, _("aborted")); } else if (run_result & E2F_FLAG_CANCEL) { log_out(ctx, _("%s: e2fsck canceled.\n"), ctx->device_name ? ctx->device_name : ctx->filesystem_name); exit_value |= FSCK_CANCELED; - } else if (ctx->qctx && !ctx->invalid_bitmaps) { + goto cleanup; + } + + if (ext2fs_has_feature_orphan_file(fs->super)) { + int ret; + + /* No point in orphan file without a journal... */ + if (!ext2fs_has_feature_journal(fs->super) && + fix_problem(ctx, PR_6_ORPHAN_FILE_WITHOUT_JOURNAL, &pctx)) { + retval = ext2fs_truncate_orphan_file(fs); + if (retval) { + /* Huh, failed to delete file */ + fix_problem(ctx, PR_6_ORPHAN_FILE_TRUNC_FAILED, + &pctx); + goto check_quotas; + } + ext2fs_clear_feature_orphan_file(fs->super); + ext2fs_mark_super_dirty(fs); + goto check_quotas; + } + ret = check_init_orphan_file(ctx); + if (ret == 2 || + (ret == 0 && ext2fs_has_feature_orphan_present(fs->super) && + fix_problem(ctx, PR_6_ORPHAN_PRESENT_CLEAN_FILE, &pctx))) { + ext2fs_clear_feature_orphan_present(fs->super); + ext2fs_mark_super_dirty(fs); + } else if (ret == 1 && + fix_problem(ctx, PR_6_ORPHAN_FILE_CORRUPTED, &pctx)) { + int orphan_file_blocks; + + if (ctx->invalid_bitmaps) { + fix_problem(ctx, + PR_6_ORPHAN_FILE_BITMAP_INVALID, + &pctx); + goto check_quotas; + } + + retval = ext2fs_truncate_orphan_file(fs); + if (retval) { + /* Huh, failed to truncate file */ + fix_problem(ctx, PR_6_ORPHAN_FILE_TRUNC_FAILED, + &pctx); + goto check_quotas; + } + + orphan_file_blocks = + ext2fs_default_orphan_file_blocks(fs); + log_out(ctx, _("Creating orphan file (%d blocks): "), + orphan_file_blocks); + fflush(stdout); + retval = ext2fs_create_orphan_file(fs, + orphan_file_blocks); + if (retval) { + log_out(ctx, "%s: while trying to create " + "orphan file\n", error_message(retval)); + fix_problem(ctx, PR_6_ORPHAN_FILE_CREATE_FAILED, + &pctx); + goto check_quotas; + } + log_out(ctx, "%s", _(" Done.\n")); + } + } else if (ext2fs_has_feature_orphan_present(fs->super) && + fix_problem(ctx, PR_6_ORPHAN_PRESENT_NO_FILE, &pctx)) { + ext2fs_clear_feature_orphan_present(fs->super); + ext2fs_mark_super_dirty(fs); + } +check_quotas: + if (ctx->qctx && !ctx->invalid_bitmaps) { int needs_writeout; for (qtype = 0; qtype < MAXQUOTAS; qtype++) { @@ -1997,6 +2064,7 @@ no_journal: goto restart; } +cleanup: #ifdef MTRACE mtrace_print("Cleanup"); #endif diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c index 22910602..29b7b151 100644 --- a/lib/e2p/feature.c +++ b/lib/e2p/feature.c @@ -49,6 +49,8 @@ static struct feature feature_list[] = { "fast_commit" }, { E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_STABLE_INODES, "stable_inodes" }, + { E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_ORPHAN_FILE, + "orphan_file" }, { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, "sparse_super" }, @@ -80,6 +82,8 @@ static struct feature feature_list[] = { "shared_blocks"}, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_VERITY, "verity"}, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_ORPHAN_PRESENT, + "orphan_present" }, { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION, "compression" }, diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c index 9511d2c7..0b74aea2 100644 --- a/lib/e2p/ls.c +++ b/lib/e2p/ls.c @@ -482,6 +482,9 @@ void list_super2(struct ext2_super_block * sb, FILE *f) if (ext2fs_has_feature_casefold(sb)) fprintf(f, "Character encoding: %s\n", e2p_encoding2str(sb->s_encoding)); + if (ext2fs_has_feature_orphan_file(sb)) + fprintf(f, "Orphan file inode: %u\n", + sb->s_orphan_file_inum); } void list_super (struct ext2_super_block * s) diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in index 2dca12ea..f6a050a2 100644 --- a/lib/ext2fs/Makefile.in +++ b/lib/ext2fs/Makefile.in @@ -115,6 +115,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \ newdir.o \ nls_utf8.o \ openfs.o \ + orphan.o \ progress.o \ punch.o \ qcow2.o \ @@ -198,6 +199,7 @@ SRCS= ext2_err.c \ $(srcdir)/newdir.c \ $(srcdir)/nls_utf8.c \ $(srcdir)/openfs.c \ + $(srcdir)/orphan.c \ $(srcdir)/progress.c \ $(srcdir)/punch.c \ $(srcdir)/qcow2.c \ diff --git a/lib/ext2fs/badblocks.c b/lib/ext2fs/badblocks.c index 0f23983b..a306bc06 100644 --- a/lib/ext2fs/badblocks.c +++ b/lib/ext2fs/badblocks.c @@ -103,7 +103,7 @@ errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, /* - * This procedure adds a block to a badblocks list. + * This procedure adds an item to a tracking list (e.g. badblocks or casefold). */ errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk) { diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h index 01d2573b..0fc9c09a 100644 --- a/lib/ext2fs/ext2_fs.h +++ b/lib/ext2fs/ext2_fs.h @@ -773,7 +773,8 @@ struct ext2_super_block { __u8 s_last_error_errcode; /*27c*/ __le16 s_encoding; /* Filename charset encoding */ __le16 s_encoding_flags; /* Filename charset encoding flags */ - __le32 s_reserved[95]; /* Padding to the end of the block */ + __le32 s_orphan_file_inum; /* Inode for tracking orphan inodes */ + __le32 s_reserved[94]; /* Padding to the end of the block */ /*3fc*/ __u32 s_checksum; /* crc32c(superblock) */ }; @@ -828,7 +829,7 @@ struct ext2_super_block { #define EXT4_FEATURE_COMPAT_SPARSE_SUPER2 0x0200 #define EXT4_FEATURE_COMPAT_FAST_COMMIT 0x0400 #define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800 - +#define EXT4_FEATURE_COMPAT_ORPHAN_FILE 0x1000 #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 @@ -851,6 +852,7 @@ struct ext2_super_block { #define EXT4_FEATURE_RO_COMPAT_PROJECT 0x2000 /* Project quota */ #define EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS 0x4000 #define EXT4_FEATURE_RO_COMPAT_VERITY 0x8000 +#define EXT4_FEATURE_RO_COMPAT_ORPHAN_PRESENT 0x10000 #define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 @@ -931,6 +933,7 @@ EXT4_FEATURE_COMPAT_FUNCS(exclude_bitmap, 2, EXCLUDE_BITMAP) EXT4_FEATURE_COMPAT_FUNCS(sparse_super2, 4, SPARSE_SUPER2) EXT4_FEATURE_COMPAT_FUNCS(fast_commit, 4, FAST_COMMIT) EXT4_FEATURE_COMPAT_FUNCS(stable_inodes, 4, STABLE_INODES) +EXT4_FEATURE_COMPAT_FUNCS(orphan_file, 4, ORPHAN_FILE) EXT4_FEATURE_RO_COMPAT_FUNCS(sparse_super, 2, SPARSE_SUPER) EXT4_FEATURE_RO_COMPAT_FUNCS(large_file, 2, LARGE_FILE) @@ -947,6 +950,7 @@ EXT4_FEATURE_RO_COMPAT_FUNCS(readonly, 4, READONLY) EXT4_FEATURE_RO_COMPAT_FUNCS(project, 4, PROJECT) EXT4_FEATURE_RO_COMPAT_FUNCS(shared_blocks, 4, SHARED_BLOCKS) EXT4_FEATURE_RO_COMPAT_FUNCS(verity, 4, VERITY) +EXT4_FEATURE_RO_COMPAT_FUNCS(orphan_present, 4, ORPHAN_PRESENT) EXT4_FEATURE_INCOMPAT_FUNCS(compression, 2, COMPRESSION) EXT4_FEATURE_INCOMPAT_FUNCS(filetype, 2, FILETYPE) @@ -1114,6 +1118,14 @@ static inline unsigned int ext2fs_dir_rec_len(__u8 name_len, return rec_len; } +#define EXT4_ORPHAN_BLOCK_MAGIC 0x0b10ca04 + +/* Structure at the tail of orphan block */ +struct ext4_orphan_block_tail { + __u32 ob_magic; + __u32 ob_checksum; +}; + /* * Constants for ext4's extended time encoding */ diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index b5477c10..72c60d2b 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -532,6 +532,7 @@ typedef struct ext2_struct_inode_scan *ext2_inode_scan; #define EXT2_MF_READONLY 4 #define EXT2_MF_SWAP 8 #define EXT2_MF_BUSY 16 +#define EXT2_MF_EXTFS 32 /* * Ext2/linux mode flags. We define them here so that we don't need @@ -633,7 +634,8 @@ typedef struct ext2_icount *ext2_icount_t; EXT2_FEATURE_COMPAT_EXT_ATTR|\ EXT4_FEATURE_COMPAT_SPARSE_SUPER2|\ EXT4_FEATURE_COMPAT_FAST_COMMIT|\ - EXT4_FEATURE_COMPAT_STABLE_INODES) + EXT4_FEATURE_COMPAT_STABLE_INODES|\ + EXT4_FEATURE_COMPAT_ORPHAN_FILE) #ifdef CONFIG_MMP #define EXT4_LIB_INCOMPAT_MMP EXT4_FEATURE_INCOMPAT_MMP @@ -668,7 +670,8 @@ typedef struct ext2_icount *ext2_icount_t; EXT4_FEATURE_RO_COMPAT_READONLY |\ EXT4_FEATURE_RO_COMPAT_PROJECT |\ EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS |\ - EXT4_FEATURE_RO_COMPAT_VERITY) + EXT4_FEATURE_RO_COMPAT_VERITY |\ + EXT4_FEATURE_RO_COMPAT_ORPHAN_PRESENT) /* * These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed @@ -1295,6 +1298,8 @@ extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk, ext2_ino_t inum); errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle); errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle); +errcode_t ext2fs_xattrs_read_inode(struct ext2_xattr_handle *handle, + struct ext2_inode_large *inode); errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h, int (*func)(char *name, char *value, size_t value_len, void *data), @@ -1706,6 +1711,19 @@ errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io); errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io); errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io); +/* orphan.c */ +extern errcode_t ext2fs_create_orphan_file(ext2_filsys fs, blk_t num_blocks); +extern errcode_t ext2fs_truncate_orphan_file(ext2_filsys fs); +extern e2_blkcnt_t ext2fs_default_orphan_file_blocks(ext2_filsys fs); +extern __u32 ext2fs_do_orphan_file_block_csum(ext2_filsys fs, ext2_ino_t ino, + __u32 gen, blk64_t blk, + char *buf); +extern errcode_t ext2fs_orphan_file_block_csum_set(ext2_filsys fs, + ext2_ino_t ino, blk64_t blk, + char *buf); +extern int ext2fs_orphan_file_block_csum_verify(ext2_filsys fs, ext2_ino_t ino, + blk64_t blk, char *buf); + /* get_pathname.c */ extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, char **name); @@ -1859,7 +1877,9 @@ extern int ext2fs_dirent_file_type(const struct ext2_dir_entry *entry); extern void ext2fs_dirent_set_file_type(struct ext2_dir_entry *entry, int type); extern struct ext2_inode *ext2fs_inode(struct ext2_inode_large * large_inode); extern const struct ext2_inode *ext2fs_const_inode(const struct ext2_inode_large * large_inode); - +extern int ext2fs_inodes_per_orphan_block(ext2_filsys fs); +extern struct ext4_orphan_block_tail *ext2fs_orphan_block_tail(ext2_filsys fs, + char *buf); #endif /* @@ -2169,6 +2189,19 @@ ext2fs_const_inode(const struct ext2_inode_large * large_inode) return (const struct ext2_inode *) large_inode; } +_INLINE_ int ext2fs_inodes_per_orphan_block(ext2_filsys fs) +{ + return (fs->blocksize - sizeof(struct ext4_orphan_block_tail)) / + sizeof(__u32); +} + +_INLINE_ struct ext4_orphan_block_tail * +ext2fs_orphan_block_tail(ext2_filsys fs, char *buf) +{ + return (struct ext4_orphan_block_tail *)(buf + fs->blocksize - + sizeof(struct ext4_orphan_block_tail)); +} + #undef _INLINE_ #endif diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c index 5525045c..34940463 100644 --- a/lib/ext2fs/ext_attr.c +++ b/lib/ext2fs/ext_attr.c @@ -1043,30 +1043,18 @@ static void xattrs_free_keys(struct ext2_xattr_handle *h) h->ibody_count = 0; } -errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle) +/* fetch xattrs from an already-loaded inode */ +errcode_t ext2fs_xattrs_read_inode(struct ext2_xattr_handle *handle, + struct ext2_inode_large *inode) { - struct ext2_inode_large *inode; struct ext2_ext_attr_header *header; __u32 ea_inode_magic; unsigned int storage_size; char *start, *block_buf = NULL; blk64_t blk; - size_t i; - errcode_t err; + errcode_t err = 0; EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE); - i = EXT2_INODE_SIZE(handle->fs->super); - if (i < sizeof(*inode)) - i = sizeof(*inode); - err = ext2fs_get_memzero(i, &inode); - if (err) - return err; - - err = ext2fs_read_inode_full(handle->fs, handle->ino, - (struct ext2_inode *)inode, - EXT2_INODE_SIZE(handle->fs->super)); - if (err) - goto out; xattrs_free_keys(handle); @@ -1102,7 +1090,7 @@ errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle) read_ea_block: /* Look for EA in a separate EA block */ - blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode); + blk = ext2fs_file_acl_block(handle->fs, EXT2_INODE(inode)); if (blk != 0) { if ((blk < handle->fs->super->s_first_data_block) || (blk >= ext2fs_blocks_count(handle->fs->super))) { @@ -1133,20 +1121,39 @@ read_ea_block: err = read_xattrs_from_buffer(handle, inode, (struct ext2_ext_attr_entry *) start, storage_size, block_buf); - if (err) - goto out3; + } +out3: + if (block_buf) ext2fs_free_mem(&block_buf); - } +out: + return err; +} - ext2fs_free_mem(&block_buf); - ext2fs_free_mem(&inode); - return 0; +errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle) +{ + struct ext2_inode_large *inode; + size_t inode_size = EXT2_INODE_SIZE(handle->fs->super); + errcode_t err; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE); + + if (inode_size < sizeof(*inode)) + inode_size = sizeof(*inode); + err = ext2fs_get_memzero(inode_size, &inode); + if (err) + return err; + + err = ext2fs_read_inode_full(handle->fs, handle->ino, EXT2_INODE(inode), + EXT2_INODE_SIZE(handle->fs->super)); + if (err) + goto out; + + err = ext2fs_xattrs_read_inode(handle, inode); -out3: - ext2fs_free_mem(&block_buf); out: ext2fs_free_mem(&inode); + return err; } diff --git a/lib/ext2fs/ismounted.c b/lib/ext2fs/ismounted.c index 8fc0b99d..a7db1a5c 100644 --- a/lib/ext2fs/ismounted.c +++ b/lib/ext2fs/ismounted.c @@ -213,6 +213,12 @@ is_root: close(fd); (void) unlink(TEST_FILE); } + + if (mnt && mnt->mnt_type && + (!strcmp(mnt->mnt_type, "ext4") || + !strcmp(mnt->mnt_type, "ext3") || + !strcmp(mnt->mnt_type, "ext2"))) + *mount_flags |= EXT2_MF_EXTFS; retval = 0; errout: endmntent (f); diff --git a/lib/ext2fs/orphan.c b/lib/ext2fs/orphan.c new file mode 100644 index 00000000..e25f20ca --- /dev/null +++ b/lib/ext2fs/orphan.c @@ -0,0 +1,273 @@ +/* + * orphan.c --- utility function to handle orphan file + * + * Copyright (C) 2015 Jan Kara. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "config.h" +#include <string.h> + +#include "ext2_fs.h" +#include "ext2fsP.h" + +errcode_t ext2fs_truncate_orphan_file(ext2_filsys fs) +{ + struct ext2_inode inode; + errcode_t err; + ext2_ino_t ino = fs->super->s_orphan_file_inum; + + err = ext2fs_read_inode(fs, ino, &inode); + if (err) + return err; + + err = ext2fs_punch(fs, ino, &inode, NULL, 0, ~0ULL); + if (err) + return err; + + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + memset(&inode, 0, sizeof(struct ext2_inode)); + err = ext2fs_write_inode(fs, ino, &inode); + + ext2fs_clear_feature_orphan_file(fs->super); + ext2fs_clear_feature_orphan_present(fs->super); + ext2fs_mark_super_dirty(fs); + /* Need to update group descriptors as well */ + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + + return err; +} + +__u32 ext2fs_do_orphan_file_block_csum(ext2_filsys fs, ext2_ino_t ino, + __u32 gen, blk64_t blk, char *buf) +{ + int inodes_per_ob = ext2fs_inodes_per_orphan_block(fs); + __u32 crc; + + ino = ext2fs_cpu_to_le32(ino); + gen = ext2fs_cpu_to_le32(gen); + blk = ext2fs_cpu_to_le64(blk); + crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&ino, + sizeof(ino)); + crc = ext2fs_crc32c_le(crc, (unsigned char *)&gen, sizeof(gen)); + crc = ext2fs_crc32c_le(crc, (unsigned char *)&blk, sizeof(blk)); + crc = ext2fs_crc32c_le(crc, (unsigned char *)buf, + inodes_per_ob * sizeof(__u32)); + + return ext2fs_cpu_to_le32(crc); +} + +struct mkorphan_info { + char *buf; + char *zerobuf; + blk_t num_blocks; + blk_t alloc_blocks; + blk64_t last_blk; + errcode_t err; + ext2_ino_t ino; + __u32 generation; +}; + +static int mkorphan_proc(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct mkorphan_info *oi = (struct mkorphan_info *)priv_data; + blk64_t new_blk; + errcode_t err; + + /* Can we just continue in currently allocated cluster? */ + if (blockcnt && + EXT2FS_B2C(fs, oi->last_blk) == EXT2FS_B2C(fs, oi->last_blk + 1)) { + new_blk = oi->last_blk + 1; + } else { + err = ext2fs_new_block2(fs, oi->last_blk, 0, &new_blk); + if (err) { + oi->err = err; + return BLOCK_ABORT; + } + ext2fs_block_alloc_stats2(fs, new_blk, +1); + oi->alloc_blocks++; + } + if (blockcnt >= 0) { + if (ext2fs_has_feature_metadata_csum(fs->super)) { + struct ext4_orphan_block_tail *tail; + + tail = ext2fs_orphan_block_tail(fs, oi->buf); + tail->ob_checksum = ext2fs_do_orphan_file_block_csum(fs, + oi->ino, oi->generation, new_blk, oi->buf); + } + err = io_channel_write_blk64(fs->io, new_blk, 1, oi->buf); + } else /* zerobuf is used to initialize new indirect blocks... */ + err = io_channel_write_blk64(fs->io, new_blk, 1, oi->zerobuf); + if (err) { + oi->err = err; + return BLOCK_ABORT; + } + oi->last_blk = new_blk; + *blocknr = new_blk; + if (blockcnt >= 0 && --oi->num_blocks == 0) + return BLOCK_CHANGED | BLOCK_ABORT; + return BLOCK_CHANGED; +} + +errcode_t ext2fs_create_orphan_file(ext2_filsys fs, blk_t num_blocks) +{ + struct ext2_inode inode; + ext2_ino_t ino = fs->super->s_orphan_file_inum; + errcode_t err; + char *buf = NULL, *zerobuf = NULL; + struct mkorphan_info oi; + struct ext4_orphan_block_tail *ob_tail; + + if (!ino) { + err = ext2fs_new_inode(fs, EXT2_ROOT_INO, LINUX_S_IFREG | 0600, + 0, &ino); + if (err) + return err; + ext2fs_inode_alloc_stats2(fs, ino, +1, 0); + ext2fs_mark_ib_dirty(fs); + } + + err = ext2fs_read_inode(fs, ino, &inode); + if (err) + return err; + if (EXT2_I_SIZE(&inode)) { + err = ext2fs_truncate_orphan_file(fs); + if (err) + return err; + } + + memset(&inode, 0, sizeof(struct ext2_inode)); + if (ext2fs_has_feature_extents(fs->super)) { + inode.i_flags |= EXT4_EXTENTS_FL; + err = ext2fs_write_inode(fs, ino, &inode); + if (err) + return err; + } + + err = ext2fs_get_mem(fs->blocksize, &buf); + if (err) + return err; + err = ext2fs_get_mem(fs->blocksize, &zerobuf); + if (err) + goto out; + memset(buf, 0, fs->blocksize); + memset(zerobuf, 0, fs->blocksize); + ob_tail = ext2fs_orphan_block_tail(fs, buf); + ob_tail->ob_magic = ext2fs_cpu_to_le32(EXT4_ORPHAN_BLOCK_MAGIC); + oi.num_blocks = num_blocks; + oi.alloc_blocks = 0; + oi.last_blk = 0; + oi.generation = inode.i_generation; + oi.ino = ino; + oi.buf = buf; + oi.zerobuf = zerobuf; + oi.err = 0; + err = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_APPEND, + 0, mkorphan_proc, &oi); + if (err) + goto out; + if (oi.err) { + err = oi.err; + goto out; + } + + /* Reread inode after blocks were allocated */ + err = ext2fs_read_inode(fs, ino, &inode); + if (err) + goto out; + ext2fs_iblk_set(fs, &inode, 0); + inode.i_atime = inode.i_mtime = + inode.i_ctime = fs->now ? fs->now : time(0); + inode.i_links_count = 1; + inode.i_mode = LINUX_S_IFREG | 0600; + ext2fs_iblk_add_blocks(fs, &inode, oi.alloc_blocks); + err = ext2fs_inode_size_set(fs, &inode, + (unsigned long long)fs->blocksize * num_blocks); + if (err) + goto out; + err = ext2fs_write_new_inode(fs, ino, &inode); + if (err) + goto out; + + fs->super->s_orphan_file_inum = ino; + ext2fs_set_feature_orphan_file(fs->super); + ext2fs_mark_super_dirty(fs); + /* Need to update group descriptors as well */ + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; +out: + if (buf) + ext2fs_free_mem(&buf); + if (zerobuf) + ext2fs_free_mem(&zerobuf); + return err; +} + +/* + * Find reasonable size for orphan file. We choose orphan file size to be + * between 32 and 512 filesystem blocks and not more than 1/4096 of the + * filesystem unless it is really small. + */ +e2_blkcnt_t ext2fs_default_orphan_file_blocks(ext2_filsys fs) +{ + __u64 num_blocks = ext2fs_blocks_count(fs->super); + e2_blkcnt_t blks = 512; + + if (num_blocks < 128 * 1024) + blks = 32; + else if (num_blocks < 2 * 1024 * 1024) + blks = num_blocks / 4096; + return (blks + EXT2FS_CLUSTER_MASK(fs)) & ~EXT2FS_CLUSTER_MASK(fs); +} + +static errcode_t ext2fs_orphan_file_block_csum(ext2_filsys fs, ext2_ino_t ino, + blk64_t blk, char *buf, + __u32 *crcp) +{ + struct ext2_inode inode; + errcode_t retval; + + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + return retval; + *crcp = ext2fs_do_orphan_file_block_csum(fs, ino, inode.i_generation, + blk, buf); + return 0; +} + +errcode_t ext2fs_orphan_file_block_csum_set(ext2_filsys fs, ext2_ino_t ino, + blk64_t blk, char *buf) +{ + struct ext4_orphan_block_tail *tail; + + if (!ext2fs_has_feature_metadata_csum(fs->super)) + return 0; + + tail = ext2fs_orphan_block_tail(fs, buf); + return ext2fs_orphan_file_block_csum(fs, ino, blk, buf, + &tail->ob_checksum); +} + +int ext2fs_orphan_file_block_csum_verify(ext2_filsys fs, ext2_ino_t ino, + blk64_t blk, char *buf) +{ + struct ext4_orphan_block_tail *tail; + __u32 crc; + errcode_t retval; + + if (!ext2fs_has_feature_metadata_csum(fs->super)) + return 1; + retval = ext2fs_orphan_file_block_csum(fs, ino, blk, buf, &crc); + if (retval) + return 0; + tail = ext2fs_orphan_block_tail(fs, buf); + return ext2fs_le32_to_cpu(tail->ob_checksum) == crc; +} diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c index 5e6b22f4..fe764b9e 100644 --- a/lib/ext2fs/swapfs.c +++ b/lib/ext2fs/swapfs.c @@ -131,8 +131,9 @@ void ext2fs_swap_super(struct ext2_super_block * sb) /* s_*_time_hi are __u8 and does not need swabbing */ sb->s_encoding = ext2fs_swab16(sb->s_encoding); sb->s_encoding_flags = ext2fs_swab16(sb->s_encoding_flags); + sb->s_orphan_file_inum = ext2fs_swab32(sb->s_orphan_file_inum); /* catch when new fields are used from s_reserved */ - EXT2FS_BUILD_BUG_ON(sizeof(sb->s_reserved) != 95 * sizeof(__le32)); + EXT2FS_BUILD_BUG_ON(sizeof(sb->s_reserved) != 94 * sizeof(__le32)); sb->s_checksum = ext2fs_swab32(sb->s_checksum); } diff --git a/lib/ext2fs/tst_super_size.c b/lib/ext2fs/tst_super_size.c index 80a5269b..ad452dee 100644 --- a/lib/ext2fs/tst_super_size.c +++ b/lib/ext2fs/tst_super_size.c @@ -152,7 +152,8 @@ int main(int argc, char **argv) check_field(s_last_error_errcode, 1); check_field(s_encoding, 2); check_field(s_encoding_flags, 2); - check_field(s_reserved, 95 * 4); + check_field(s_orphan_file_inum, 4); + check_field(s_reserved, 94 * 4); check_field(s_checksum, 4); do_field("Superblock end", 0, 0, cur_offset, 1024); #endif diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c index e53db333..5b894826 100644 --- a/lib/ext2fs/unix_io.c +++ b/lib/ext2fs/unix_io.c @@ -305,7 +305,6 @@ bounce_read: while (size > 0) { actual = read(data->dev, data->bounce, align_size); if (actual != align_size) { - mutex_unlock(data, BOUNCE_MTX); actual = really_read; buf -= really_read; size += really_read; diff --git a/lib/support/mkquota.c b/lib/support/mkquota.c index c89f8959..9339c994 100644 --- a/lib/support/mkquota.c +++ b/lib/support/mkquota.c @@ -509,7 +509,8 @@ errcode_t quota_compute_usage(quota_ctx_t qctx) continue; if (ino == EXT2_ROOT_INO || (ino >= EXT2_FIRST_INODE(fs->super) && - ino != quota_type2inum(PRJQUOTA, fs->super))) { + ino != quota_type2inum(PRJQUOTA, fs->super) && + ino != fs->super->s_orphan_file_inum)) { space = ext2fs_get_stat_i_blocks(fs, EXT2_INODE(inode)) << 9; quota_data_add(qctx, inode, ino, space); diff --git a/misc/e2image.c b/misc/e2image.c index 8b4c0677..1ae03001 100644 --- a/misc/e2image.c +++ b/misc/e2image.c @@ -1370,7 +1370,8 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags, ino == fs->super->s_journal_inum || ino == quota_type2inum(USRQUOTA, fs->super) || ino == quota_type2inum(GRPQUOTA, fs->super) || - ino == quota_type2inum(PRJQUOTA, fs->super)) { + ino == quota_type2inum(PRJQUOTA, fs->super) || + ino == fs->super->s_orphan_file_inum) { retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY, block_buf, process_dir_block, &pb); diff --git a/misc/e2label.8.in b/misc/e2label.8.in index 1dc96199..fa5294c4 100644 --- a/misc/e2label.8.in +++ b/misc/e2label.8.in @@ -33,7 +33,12 @@ Ext2 volume labels can be at most 16 characters long; if .I volume-label is longer than 16 characters, .B e2label -will truncate it and print a warning message. +will truncate it and print a warning message. For other file systems that +support online label manipulation and are mounted +.B e2label +will work as well, but it will not attempt to truncate the +.I volume-label +at all. .PP It is also possible to set the volume label using the .B \-L diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in index c0b53245..30f97bb5 100644 --- a/misc/mke2fs.8.in +++ b/misc/mke2fs.8.in @@ -365,6 +365,13 @@ small risk if the system crashes before the journal has been overwritten entirely one time. If the option value is omitted, it defaults to 1 to enable lazy journal inode zeroing. .TP +.B assume_storage_prezeroed\fR[\fB= \fI<0 to disable, 1 to enable>\fR] +If enabled, +.BR mke2fs +assumes that the storage device has been prezeroed, skips zeroing the journal +and inode tables, and annotates the block group flags to signal that the inode +table has been zeroed. +.TP .B no_copy_xattrs Normally .B mke2fs @@ -403,6 +410,11 @@ file system to change based on the user running \fBmke2fs\fR. Set a flag in the file system superblock indicating that it may be mounted using experimental kernel code, such as the ext4dev file system. .TP +.BI orphan_file_size= size +Set size of the file for tracking unlinked but still open inodes and inodes +with truncate in progress. Larger file allows for better scalability, reserving +a few blocks per cpu is ideal. +.TP .B discard Attempt to discard blocks at mkfs time (discarding blocks initially is useful on solid state devices and sparse / thin-provisioned storage). When the device diff --git a/misc/mke2fs.c b/misc/mke2fs.c index ba5f179a..4a9c1b09 100644 --- a/misc/mke2fs.c +++ b/misc/mke2fs.c @@ -94,7 +94,9 @@ static gid_t root_gid; int journal_size; int journal_flags; int journal_fc_size; +static e2_blkcnt_t orphan_file_blocks; static int lazy_itable_init; +static int assume_storage_prezeroed; static int packed_meta_blocks; int no_copy_xattrs; static char *bad_blocks_filename = NULL; @@ -1012,6 +1014,11 @@ static void parse_extended_opts(struct ext2_super_block *param, lazy_itable_init = strtoul(arg, &p, 0); else lazy_itable_init = 1; + } else if (!strcmp(token, "assume_storage_prezeroed")) { + if (arg) + assume_storage_prezeroed = strtoul(arg, &p, 0); + else + assume_storage_prezeroed = 1; } else if (!strcmp(token, "lazy_journal_init")) { if (arg) journal_flags |= strtoul(arg, &p, 0) ? @@ -1089,6 +1096,21 @@ static void parse_extended_opts(struct ext2_super_block *param, continue; } encoding_flags = arg; + } else if (!strcmp(token, "orphan_file_size")) { + if (!arg) { + r_usage++; + badopt = token; + continue; + } + orphan_file_blocks = parse_num_blocks2(arg, + fs_param.s_log_block_size); + if (orphan_file_blocks == 0) { + fprintf(stderr, + _("Invalid size of orphan file %s\n"), + arg); + r_usage++; + continue; + } } else { r_usage++; badopt = token; @@ -1115,7 +1137,8 @@ static void parse_extended_opts(struct ext2_super_block *param, "\tnodiscard\n" "\tencoding=<encoding>\n" "\tencoding_flags=<flags>\n" - "\tquotatype=<quota type(s) to be enabled>\n\n"), + "\tquotatype=<quota type(s) to be enabled>\n" + "\tassume_storage_prezeroed=<0 to disable, 1 to enable>\n\n"), badopt ? badopt : ""); free(buf); exit(1); @@ -1156,7 +1179,8 @@ static __u32 ok_features[3] = { EXT2_FEATURE_COMPAT_EXT_ATTR | EXT4_FEATURE_COMPAT_SPARSE_SUPER2 | EXT4_FEATURE_COMPAT_FAST_COMMIT | - EXT4_FEATURE_COMPAT_STABLE_INODES, + EXT4_FEATURE_COMPAT_STABLE_INODES | + EXT4_FEATURE_COMPAT_ORPHAN_FILE, /* Incompat */ EXT2_FEATURE_INCOMPAT_FILETYPE| EXT3_FEATURE_INCOMPAT_EXTENTS| @@ -1551,6 +1575,8 @@ static void PRS(int argc, char *argv[]) int lsector_size = 0, psector_size = 0; int show_version_only = 0, is_device = 0; unsigned long long num_inodes = 0; /* unsigned long long to catch too-large input */ + int default_orphan_file = 0; + int default_csum_seed = 0; errcode_t retval; char * oldpath = getenv("PATH"); char * extended_opts = 0; @@ -2103,8 +2129,24 @@ profile_error: ext2fs_clear_feature_ea_inode(&fs_param); ext2fs_clear_feature_casefold(&fs_param); } - edit_feature(fs_features ? fs_features : tmp, - &fs_param.s_feature_compat); + if (!fs_features && tmp) + edit_feature(tmp, &fs_param.s_feature_compat); + /* + * Now all the defaults are incorporated in fs_param. Check the state + * of orphan_file feature so that we know whether we should silently + * disabled in case journal gets disabled. + */ + if (ext2fs_has_feature_orphan_file(&fs_param)) + default_orphan_file = 1; + if (ext2fs_has_feature_csum_seed(&fs_param)) + default_csum_seed = 1; + if (fs_features) + edit_feature(fs_features, &fs_param.s_feature_compat); + /* Silently disable orphan_file if user chose fs without journal */ + if (default_orphan_file && !ext2fs_has_feature_journal(&fs_param)) + ext2fs_clear_feature_orphan_file(&fs_param); + if (default_csum_seed && !ext2fs_has_feature_metadata_csum(&fs_param)) + ext2fs_clear_feature_csum_seed(&fs_param); if (tmp) free(tmp); (void) ext2fs_free_mem(&fs_features); @@ -3097,6 +3139,18 @@ int main (int argc, char *argv[]) io_channel_set_options(fs->io, opt_string); } + if (assume_storage_prezeroed) { + if (verbose) + printf("%s", + _("Assuming the storage device is prezeroed " + "- skipping inode table and journal wipe\n")); + + lazy_itable_init = 1; + itable_zeroed = 1; + zero_hugefile = 0; + journal_flags |= EXT2_MKJOURNAL_LAZYINIT; + } + /* Can't undo discard ... */ if (!noaction && discard && dev_size && (io_ptr != undo_io_manager)) { retval = mke2fs_discard_device(fs); @@ -3431,6 +3485,9 @@ int main (int argc, char *argv[]) if (!jparams.num_journal_blocks) { ext2fs_clear_feature_journal(fs->super); + ext2fs_clear_feature_orphan_file(fs->super); + ext2fs_clear_feature_journal(&fs_param); + ext2fs_clear_feature_orphan_file(&fs_param); goto no_journal; } if (!quiet) { @@ -3473,6 +3530,23 @@ no_journal: fix_cluster_bg_counts(fs); if (ext2fs_has_feature_quota(&fs_param)) create_quota_inodes(fs); + if (ext2fs_has_feature_orphan_file(&fs_param)) { + if (!ext2fs_has_feature_journal(&fs_param)) { + com_err(program_name, 0, _("cannot set orphan_file " + "feature without a journal.")); + exit(1); + } + if (!orphan_file_blocks) { + orphan_file_blocks = + ext2fs_default_orphan_file_blocks(fs); + } + retval = ext2fs_create_orphan_file(fs, orphan_file_blocks); + if (retval) { + com_err(program_name, retval, + _("while creating orphan file")); + exit(1); + } + } retval = mk_hugefiles(fs, device_name); if (retval) diff --git a/misc/mke2fs.conf.in b/misc/mke2fs.conf.in index 05680992..b7fc95df 100644 --- a/misc/mke2fs.conf.in +++ b/misc/mke2fs.conf.in @@ -11,7 +11,7 @@ features = has_journal } ext4 = { - features = has_journal,extent,huge_file,flex_bg,metadata_csum,64bit,dir_nlink,extra_isize + features = has_journal,extent,huge_file,flex_bg,metadata_csum,metadata_csum_seed,64bit,dir_nlink,extra_isize,orphan_file } small = { blocksize = 1024 diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in index 979f6c5f..dcf108c1 100644 --- a/misc/tune2fs.8.in +++ b/misc/tune2fs.8.in @@ -257,6 +257,11 @@ program. This superblock setting is only honored in 2.6.35+ kernels; and not at all by the ext2 and ext3 file system drivers. .TP +.BI orphan_file_size= size +Set size of the file for tracking unlinked but still open inodes and inodes +with truncate in progress. Larger file allows for better scalability, reserving +a few blocks per cpu is ideal. +.TP .B force_fsck Set a flag in the file system superblock indicating that errors have been found. This will force fsck to run at the next mount. @@ -452,8 +457,12 @@ Ext2 file system labels can be at most 16 characters long; if .I volume-label is longer than 16 characters, .B tune2fs -will truncate it and print a warning. The volume label can be used -by +will truncate it and print a warning. For other file systems that +support online label manipulation and are mounted +.B tune2fs +will work as well, but it will not attempt to truncate the +.I volume-label +at all. The volume label can be used by .BR mount (8), .BR fsck (8), and @@ -584,8 +593,10 @@ Enable the file system to be larger than 2^32 blocks. .TP .B casefold Enable support for file system level casefolding. -.B Tune2fs -currently only supports setting this file system feature. +The option can be cleared only if filesystem has no +directories with +.B F +attribute. .TP .B dir_index Use hashed b-trees to speed up lookups for large directories. diff --git a/misc/tune2fs.c b/misc/tune2fs.c index cb5f575a..458f7cf6 100644 --- a/misc/tune2fs.c +++ b/misc/tune2fs.c @@ -52,6 +52,9 @@ extern int optind; #include <sys/types.h> #include <libgen.h> #include <limits.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif #include "ext2fs/ext2_fs.h" #include "ext2fs/ext2fs.h" @@ -71,11 +74,34 @@ extern int optind; #define QOPT_ENABLE (1) #define QOPT_DISABLE (-1) +#ifndef FS_IOC_SETFSLABEL +#define FSLABEL_MAX 256 +#define FS_IOC_SETFSLABEL _IOW(0x94, 50, char[FSLABEL_MAX]) +#endif + +#ifndef FS_IOC_GETFSLABEL +#define FS_IOC_GETFSLABEL _IOR(0x94, 49, char[FSLABEL_MAX]) +#endif + +struct fsuuid { + __u32 fsu_len; + __u32 fsu_flags; + __u8 fsu_uuid[]; +}; + +#ifndef EXT4_IOC_GETFSUUID +#define EXT4_IOC_GETFSUUID _IOR('f', 44, struct fsuuid) +#endif + +#ifndef EXT4_IOC_SETFSUUID +#define EXT4_IOC_SETFSUUID _IOW('f', 44, struct fsuuid) +#endif + extern int ask_yn(const char *string, int def); const char *program_name = "tune2fs"; char *device_name; -char *new_label, *new_last_mounted, *new_UUID; +char *new_label, *new_last_mounted, *requested_uuid; char *io_options; static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag; static int m_flag, M_flag, Q_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag; @@ -107,6 +133,7 @@ int enabling_casefold; int journal_size, journal_fc_size, journal_flags; char *journal_device; static blk64_t journal_location = ~0LL; +static e2_blkcnt_t orphan_file_blocks; static struct list_head blk_move_list; @@ -153,7 +180,8 @@ static __u32 ok_features[3] = { EXT3_FEATURE_COMPAT_HAS_JOURNAL | EXT2_FEATURE_COMPAT_DIR_INDEX | EXT4_FEATURE_COMPAT_FAST_COMMIT | - EXT4_FEATURE_COMPAT_STABLE_INODES, + EXT4_FEATURE_COMPAT_STABLE_INODES | + EXT4_FEATURE_COMPAT_ORPHAN_FILE, /* Incompat */ EXT2_FEATURE_INCOMPAT_FILETYPE | EXT3_FEATURE_INCOMPAT_EXTENTS | @@ -184,13 +212,15 @@ static __u32 clear_ok_features[3] = { EXT3_FEATURE_COMPAT_HAS_JOURNAL | EXT2_FEATURE_COMPAT_RESIZE_INODE | EXT2_FEATURE_COMPAT_DIR_INDEX | - EXT4_FEATURE_COMPAT_FAST_COMMIT, + EXT4_FEATURE_COMPAT_FAST_COMMIT | + EXT4_FEATURE_COMPAT_ORPHAN_FILE, /* Incompat */ EXT2_FEATURE_INCOMPAT_FILETYPE | EXT4_FEATURE_INCOMPAT_FLEX_BG | EXT4_FEATURE_INCOMPAT_MMP | EXT4_FEATURE_INCOMPAT_64BIT | - EXT4_FEATURE_INCOMPAT_CSUM_SEED, + EXT4_FEATURE_INCOMPAT_CSUM_SEED | + EXT4_FEATURE_INCOMPAT_CASEFOLD, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE | EXT4_FEATURE_RO_COMPAT_HUGE_FILE| @@ -1009,6 +1039,41 @@ out: return retval; } +static int has_casefold_inode(ext2_filsys fs) +{ + int length = EXT2_INODE_SIZE(fs->super); + struct ext2_inode *inode = NULL; + ext2_inode_scan scan; + errcode_t retval; + ext2_ino_t ino; + int found_casefold = 0; + + retval = ext2fs_get_mem(length, &inode); + if (retval) + fatal_err(retval, "while allocating memory"); + + retval = ext2fs_open_inode_scan(fs, 0, &scan); + if (retval) + fatal_err(retval, "while opening inode scan"); + + do { + retval = ext2fs_get_next_inode_full(scan, &ino, inode, length); + if (retval) + fatal_err(retval, "while getting next inode"); + if (!ino) + break; + + if(inode->i_flags & EXT4_CASEFOLD_FL) { + found_casefold = 1; + break; + } + } while(1); + + ext2fs_free_mem(&inode); + ext2fs_close_inode_scan(scan); + return found_casefold; +} + static errcode_t disable_uninit_bg(ext2_filsys fs, __u32 csum_feature_flag) { struct ext2_group_desc *gd; @@ -1149,6 +1214,56 @@ static int update_feature_set(ext2_filsys fs, char *features) } } + if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_ORPHAN_FILE)) { + ext2_ino_t ino; + + if (mount_flags & EXT2_MF_MOUNTED) { + fputs(_("The orphan_file feature may only be cleared " + "when the filesystem is unmounted.\n"), stderr); + return 1; + } + if (ext2fs_has_feature_orphan_present(sb) && f_flag < 2) { + fputs(_("The orphan_present feature is set. Please " + "run e2fsck before clearing orphan_file " + "feature.\n"), + stderr); + return 1; + } + err = ext2fs_read_bitmaps(fs); + if (err) { + com_err(program_name, err, "%s", + _("while loading bitmaps")); + return 1; + } + err = ext2fs_truncate_orphan_file(fs); + if (err) { + com_err(program_name, err, + _("\n\twhile trying to delete orphan file\n")); + return 1; + } + ino = sb->s_orphan_file_inum; + sb->s_orphan_file_inum = 0; + ext2fs_inode_alloc_stats2(fs, ino, -1, 0); + ext2fs_clear_feature_orphan_file(sb); + ext2fs_clear_feature_orphan_present(sb); + ext2fs_mark_super_dirty(fs); + } + + if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_ORPHAN_FILE)) { + if (!ext2fs_has_feature_journal(sb)) { + fputs(_("orphan_file feature can be set only for " + "filesystems with journal.\n"), stderr); + return 1; + } + /* + * If adding an orphan file, let the create orphan file + * code below handle setting the flag and creating it. + * We supply a default size if necessary. + */ + orphan_file_blocks = ext2fs_default_orphan_file_blocks(fs); + ext2fs_set_feature_orphan_file(sb); + } + if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { if (ext2fs_has_feature_meta_bg(sb)) { @@ -1493,6 +1608,22 @@ mmp_error: enabling_casefold = 1; } + if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_CASEFOLD)) { + if (mount_flags & EXT2_MF_MOUNTED) { + fputs(_("The casefold feature may only be disabled when " + "the filesystem is unmounted.\n"), stderr); + return 1; + } + if (has_casefold_inode(fs)) { + fputs(_("The casefold feature can't be cleared when " + "there are inodes with +F flag.\n"), stderr); + return 1; + } + fs->super->s_encoding = 0; + fs->super->s_encoding_flags = 0; + enabling_casefold = 0; + } + if (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_CSUM_SEED)) { if (!ext2fs_has_feature_metadata_csum(sb)) { @@ -2041,7 +2172,7 @@ static void parse_tune2fs_options(int argc, char **argv) open_flag = EXT2_FLAG_RW; break; case 'U': - new_UUID = optarg; + requested_uuid = optarg; U_flag = 1; open_flag = EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK; @@ -2273,6 +2404,21 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts) continue; } encoding_flags = arg; + } else if (!strcmp(token, "orphan_file_size")) { + if (!arg) { + r_usage++; + continue; + } + orphan_file_blocks = parse_num_blocks2(arg, + fs->super->s_log_block_size); + + if (orphan_file_blocks < 1) { + fprintf(stderr, + _("Invalid size of orphan file %s\n"), + arg); + r_usage++; + continue; + } } else r_usage++; } @@ -2933,6 +3079,80 @@ fs_update_journal_user(struct ext2_super_block *sb, __u8 old_uuid[UUID_SIZE]) return 0; } +/* + * Use FS_IOC_SETFSLABEL or FS_IOC_GETFSLABEL to set/get file system label + * Return: 0 on success + * 1 on error + * -1 when the old method should be used + */ +static int handle_fslabel(int setlabel) +{ +#ifdef __linux__ + errcode_t ret; + int mnt_flags, fd; + char label[FSLABEL_MAX]; + int maxlen = FSLABEL_MAX - 1; + char mntpt[PATH_MAX + 1]; + + ret = ext2fs_check_mount_point(device_name, &mnt_flags, + mntpt, sizeof(mntpt)); + if (ret) { + com_err(device_name, ret, _("while checking mount status")); + return 1; + } + if (!(mnt_flags & EXT2_MF_MOUNTED) || + (setlabel && (mnt_flags & EXT2_MF_READONLY))) + return -1; + + if (!mntpt[0]) { + fprintf(stderr,_("Unknown mount point for %s\n"), device_name); + return 1; + } + + fd = open(mntpt, O_RDONLY); + if (fd < 0) { + com_err(mntpt, errno, _("while opening mount point")); + return 1; + } + + /* Get fs label */ + if (!setlabel) { + if (ioctl(fd, FS_IOC_GETFSLABEL, &label)) { + close(fd); + if (errno == ENOTTY) + return -1; + com_err(mntpt, errno, _("while trying to get fs label")); + return 1; + } + close(fd); + printf("%.*s\n", EXT2_LEN_STR(label)); + return 0; + } + + /* If it's extN file system, truncate the label to appropriate size */ + if (mnt_flags & EXT2_MF_EXTFS) + maxlen = EXT2_LABEL_LEN; + if (strlen(new_label) > maxlen) { + fputs(_("Warning: label too long, truncating.\n"), + stderr); + new_label[maxlen] = '\0'; + } + + /* Set fs label */ + if (ioctl(fd, FS_IOC_SETFSLABEL, new_label)) { + close(fd); + if (errno == ENOTTY) + return -1; + com_err(mntpt, errno, _("while trying to set fs label")); + return 1; + } + close(fd); + return 0; +#else + return -1; +#endif +} + #ifndef BUILD_AS_LIB int main(int argc, char **argv) #else @@ -2945,6 +3165,9 @@ int tune2fs_main(int argc, char **argv) io_manager io_ptr, io_ptr_orig = NULL; int rc = 0; char default_undo_file[1] = { 0 }; + char mntpt[PATH_MAX + 1] = { 0 }; + int fd = -1; + struct fsuuid *fsuuid = NULL; #ifdef ENABLE_NLS setlocale(LC_MESSAGES, ""); @@ -2976,6 +3199,21 @@ int tune2fs_main(int argc, char **argv) #endif io_ptr = unix_io_manager; + /* + * Try the get/set fs label using ioctls before we even attempt + * to open the file system. + */ + if (L_flag || print_label) { + rc = handle_fslabel(L_flag); + if (rc != -1) { +#ifndef BUILD_AS_LIB + exit(rc); +#endif + return rc; + } + rc = 0; + } + retry_open: if ((open_flag & EXT2_FLAG_RW) == 0 || f_flag) open_flag |= EXT2_FLAG_SKIP_MMP; @@ -3079,9 +3317,10 @@ retry_open: goto closefs; } - retval = ext2fs_check_if_mounted(device_name, &mount_flags); + retval = ext2fs_check_mount_point(device_name, &mount_flags, + mntpt, sizeof(mntpt)); if (retval) { - com_err("ext2fs_check_if_mount", retval, + com_err("ext2fs_check_mount_point", retval, _("while determining whether %s is mounted."), device_name); rc = 1; @@ -3261,6 +3500,24 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n" if (rc) goto closefs; } + if (orphan_file_blocks) { + errcode_t err; + + err = ext2fs_read_bitmaps(fs); + if (err) { + com_err(program_name, err, "%s", + _("while loading bitmaps")); + rc = 1; + goto closefs; + } + err = ext2fs_create_orphan_file(fs, orphan_file_blocks); + if (err) { + com_err(program_name, err, "%s", + _("while creating orphan file")); + rc = 1; + goto closefs; + } + } if (Q_flag) { if (mount_flags & EXT2_MF_MOUNTED) { @@ -3279,6 +3536,8 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n" dgrp_t i; char buf[SUPERBLOCK_SIZE] __attribute__ ((aligned(8))); __u8 old_uuid[UUID_SIZE]; + uuid_t new_uuid; + errcode_t ret = -1; if (ext2fs_has_feature_stable_inodes(fs->super)) { fputs(_("Cannot change the UUID of this filesystem " @@ -3332,25 +3591,62 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n" set_csum = 1; } - memcpy(old_uuid, sb->s_uuid, UUID_SIZE); - if ((strcasecmp(new_UUID, "null") == 0) || - (strcasecmp(new_UUID, "clear") == 0)) { - uuid_clear(sb->s_uuid); - } else if (strcasecmp(new_UUID, "time") == 0) { - uuid_generate_time(sb->s_uuid); - } else if (strcasecmp(new_UUID, "random") == 0) { - uuid_generate(sb->s_uuid); - } else if (uuid_parse(new_UUID, sb->s_uuid)) { +#ifdef __linux__ + if ((mount_flags & EXT2_MF_MOUNTED) && + !(mount_flags & EXT2_MF_READONLY) && mntpt[0]) { + fd = open(mntpt, O_RDONLY); + if (fd >= 0) + fsuuid = malloc(sizeof(*fsuuid) + UUID_SIZE); + if (fsuuid) { + fsuuid->fsu_len = UUID_SIZE; + fsuuid->fsu_flags = 0; + ret = ioctl(fd, EXT4_IOC_GETFSUUID, fsuuid); + if (ret || fsuuid->fsu_len != UUID_SIZE) { + free(fsuuid); + fsuuid = NULL; + } + } + } +#endif + + memcpy(old_uuid, fsuuid ? fsuuid->fsu_uuid : sb->s_uuid, + UUID_SIZE); + if ((strcasecmp(requested_uuid, "null") == 0) || + (strcasecmp(requested_uuid, "clear") == 0)) { + uuid_clear(new_uuid); + } else if (strcasecmp(requested_uuid, "time") == 0) { + uuid_generate_time(new_uuid); + } else if (strcasecmp(requested_uuid, "random") == 0) { + uuid_generate(new_uuid); + } else if (uuid_parse(requested_uuid, new_uuid)) { com_err(program_name, 0, "%s", _("Invalid UUID format\n")); rc = 1; goto closefs; } - ext2fs_init_csum_seed(fs); - if (set_csum) { - for (i = 0; i < fs->group_desc_count; i++) - ext2fs_group_desc_csum_set(fs, i); - fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + + ret = -1; +#ifdef __linux__ + if (fsuuid) { + fsuuid->fsu_len = UUID_SIZE; + fsuuid->fsu_flags = 0; + memcpy(&fsuuid->fsu_uuid, new_uuid, UUID_SIZE); + ret = ioctl(fd, EXT4_IOC_SETFSUUID, fsuuid); + } +#endif + /* + * If we can't set the UUID via the ioctl, fall + * back to directly modifying the superblock + .*/ + if (ret) { + memcpy(sb->s_uuid, new_uuid, UUID_SIZE); + ext2fs_init_csum_seed(fs); + if (set_csum) { + for (i = 0; i < fs->group_desc_count; i++) + ext2fs_group_desc_csum_set(fs, i); + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + } + ext2fs_mark_super_dirty(fs); } /* If this is a journal dev, we need to copy UUID into jsb */ @@ -3374,8 +3670,6 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n" if ((rc = fs_update_journal_user(sb, old_uuid))) goto closefs; } - - ext2fs_mark_super_dirty(fs); } if (I_flag) { @@ -3450,6 +3744,10 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n" remove_error_table(&et_ext2_error_table); closefs: + if (fd >= 0) + close(fd); + if (fsuuid) + free(fsuuid); if (rc) { ext2fs_mmp_stop(fs); #ifndef BUILD_AS_LIB diff --git a/tests/filter.sed b/tests/filter.sed index 5fd68f34..265f458c 100644 --- a/tests/filter.sed +++ b/tests/filter.sed @@ -28,6 +28,7 @@ s/\\015//g s/, csum 0x\([0-9a-f]*\)//g s/ csum 0x\([0-9a-f]*\)//g /^Checksum:/d +/^Checksum seed:/d s/while trying to open [^ ]*/while trying to open test.img/ s/he filesystem on [^ ]* /he filesystem on test.img / s/^[^ ]* contains a \([a-z]*\) file system /test.img contains a \1 file system / diff --git a/tests/m_assume_storage_prezeroed/expect b/tests/m_assume_storage_prezeroed/expect new file mode 100644 index 00000000..b735e242 --- /dev/null +++ b/tests/m_assume_storage_prezeroed/expect @@ -0,0 +1,2 @@ +> 10000 +224 diff --git a/tests/m_assume_storage_prezeroed/script b/tests/m_assume_storage_prezeroed/script new file mode 100644 index 00000000..1a8d8463 --- /dev/null +++ b/tests/m_assume_storage_prezeroed/script @@ -0,0 +1,63 @@ +test_description="test prezeroed storage metadata allocation" +FILE_SIZE=16M + +LOG=$test_name.log +OUT=$test_name.out +EXP=$test_dir/expect + +if test "$(id -u)" -ne 0 ; then + echo "$test_name: $test_description: skipped (not root)" +elif ! command -v losetup >/dev/null ; then + echo "$test_name: $test_description: skipped (no losetup)" +else + dd if=/dev/zero of=$TMPFILE.1 bs=1 count=0 seek=$FILE_SIZE >> $LOG 2>&1 + dd if=/dev/zero of=$TMPFILE.2 bs=1 count=0 seek=$FILE_SIZE >> $LOG 2>&1 + + LOOP1=$(losetup --show --sector-size 4096 -f $TMPFILE.1) + if [ ! -b "$LOOP1" ]; then + echo "$test_name: $DESCRIPTION: skipped (no loop devices)" + rm -f $TMPFILE.1 $TMPFILE.2 + exit 0 + fi + LOOP2=$(losetup --show --sector-size 4096 -f $TMPFILE.2) + if [ ! -b "$LOOP2" ]; then + echo "$test_name: $DESCRIPTION: skipped (no loop devices)" + rm -f $TMPFILE.1 $TMPFILE.2 + losetup -d $LOOP1 + exit 0 + fi + + echo $MKE2FS -o Linux -t ext4 $LOOP1 >> $LOG 2>&1 + $MKE2FS -o Linux -t ext4 $LOOP1 >> $LOG 2>&1 + sync + stat $TMPFILE.1 >> $LOG 2>&1 + SZ=$(stat -c "%b" $TMPFILE.1) + if test $SZ -gt 10000 ; then + echo "> 10000" > $OUT + else + echo "$SZ" > $OUT + fi + + echo $MKE2FS -o Linux -t ext4 -E assume_storage_prezeroed=1 $LOOP2 >> $LOG 2>&1 + $MKE2FS -o Linux -t ext4 -E assume_storage_prezeroed=1 $LOOP2 >> $LOG 2>&1 + sync + stat $TMPFILE.2 >> $LOG 2>&1 + stat -c "%b" $TMPFILE.2 >> $OUT + + losetup -d $LOOP1 + losetup -d $LOOP2 + rm -f $TMPFILE.1 $TMPFILE.2 + + cmp -s $OUT $EXP + status=$? + + if [ "$status" = 0 ] ; then + echo "$test_name: $test_description: ok" + touch $test_name.ok + else + echo "$test_name: $test_description: failed" + cat $LOG > $test_name.failed + diff $EXP $OUT >> $test_name.failed + fi +fi +unset LOG OUT EXP FILE_SIZE LOOP1 LOOP2 |