Adds Next3 snapshot awareness to e2fsprogs. There are no snapshot COW operations during offline processing, only restrictions on changing a file system with snapshots offline. Adds the compatible features 'exclude_inode' and 'big_journal'. Adds the ro-compatible features 'has_snapshot','is_snapshot','fix_exclude','fix_snapshot'. chattr is used to control Next3 snapshots and lsattr is used to display snapshots status. Signed-off-by: Amir Goldstein <amir73il@users.sf.net> -------------------------------------------------------------------------------- diff -Nuarp a/debugfs/set_fields.c b/debugfs/set_fields.c --- a/debugfs/set_fields.c 2009-08-23 05:44:35.000000000 +0300 +++ b/debugfs/set_fields.c 2010-07-26 09:47:16.800445322 +0300 @@ -134,6 +134,11 @@ static struct field_set_info super_field { "raid_stripe_width", &set_sb.s_raid_stripe_width, 4, parse_uint }, { "log_groups_per_flex", &set_sb.s_log_groups_per_flex, 1, parse_uint }, { "kbytes_written", &set_sb.s_kbytes_written, 8, parse_uint }, + { "snapshot_inum", &set_sb.s_snapshot_inum, 4, parse_uint }, + { "snapshot_id", &set_sb.s_snapshot_id, 4, parse_uint }, + { "snapshot_r_blocks_count", &set_sb.s_snapshot_r_blocks_count, + 8, parse_uint }, + { "snapshot_list", &set_sb.s_snapshot_list, 4, parse_uint }, { 0, 0, 0, 0 } }; diff -Nuarp a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h --- a/e2fsck/e2fsck.h 2009-08-23 05:39:05.000000000 +0300 +++ b/e2fsck/e2fsck.h 2010-07-26 09:47:16.740445926 +0300 @@ -155,6 +155,7 @@ struct resource_track { #define E2F_OPT_WRITECHECK 0x0200 #define E2F_OPT_COMPRESS_DIRS 0x0400 #define E2F_OPT_FRAGCHECK 0x0800 +#define E2F_OPT_FIX_SNAPSHOT 0x1000 /* * E2fsck flags @@ -176,6 +177,11 @@ struct resource_track { #define E2F_FLAG_RESIZE_INODE 0x0400 /* Request to recreate resize inode */ #define E2F_FLAG_GOT_DEVSIZE 0x0800 /* Device size has been fetched */ #define E2F_FLAG_EXITING 0x1000 /* E2fsck exiting due to errors */ +#define E2F_FLAG_TIME_INSANE 0x2000 /* Time is insane */ +#define E2F_FLAG_EXCLUDE_INODE 0x4000 /* Request to recreate exclude inode */ +#define E2F_FLAG_CLEAR_SNAPSHOTS 0x8000 /* Clear all snapshot inodes */ + +#define E2F_RESET_FLAGS (E2F_FLAG_TIME_INSANE) /* * Defines for indicating the e2fsck pass number @@ -230,6 +236,7 @@ struct e2fsck_struct { ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/ ext2fs_block_bitmap block_found_map; /* Blocks which are in use */ + ext2fs_block_bitmap block_excluded_map; /* Blocks which are excluded */ ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */ ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */ @@ -472,6 +479,8 @@ void e2fsck_rehash_directories(e2fsck_t void check_super_block(e2fsck_t ctx); int check_backup_super_block(e2fsck_t ctx); void check_resize_inode(e2fsck_t ctx); +void check_exclude_inode(e2fsck_t ctx); +void check_snapshots(e2fsck_t ctx); /* util.c */ extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size, diff -Nuarp a/e2fsck/journal.c b/e2fsck/journal.c --- a/e2fsck/journal.c 2009-08-23 05:44:35.000000000 +0300 +++ b/e2fsck/journal.c 2010-07-26 09:47:16.710487408 +0300 @@ -836,6 +836,20 @@ static errcode_t recover_ext3_journal(e2 if (journal->j_superblock->s_errno) { + /* journal message buffer at journal super block + 1K */ + char *buf = ((char *) journal->j_superblock) + + SUPERBLOCK_OFFSET; + int len = ctx->fs->blocksize - 2*SUPERBLOCK_OFFSET; + + if (len >= 2*SUPERBLOCK_OFFSET && *buf) { + /* write journal message buffer to super block + 2K */ + io_channel_set_blksize(ctx->fs->io, SUPERBLOCK_OFFSET); + retval = io_channel_write_blk(ctx->fs->io, 2, 2, buf); + io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize); + /* clear journal message buffer */ + memset(buf, 0, len); + } + ctx->fs->super->s_state |= EXT2_ERROR_FS; ext2fs_mark_super_dirty(ctx->fs); journal->j_superblock->s_errno = 0; diff -Nuarp a/e2fsck/pass1.c b/e2fsck/pass1.c --- a/e2fsck/pass1.c 2009-08-23 05:44:35.000000000 +0300 +++ b/e2fsck/pass1.c 2010-07-26 09:47:16.720444777 +0300 @@ -55,6 +55,13 @@ #define _INLINE_ inline #endif +#define ext2fs_inode_i_blocks(fs, inode) \ + (inode)->i_blocks +#define ext2fs_file_acl_block(inode) \ + (inode)->i_file_acl +#define ext2fs_fast_mark_block_bitmap2(map, blk) \ + ext2fs_fast_mark_block_bitmap(map, blk) + static int process_block(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_blk, int ref_offset, void *priv_data); @@ -79,12 +86,12 @@ static void adjust_extattr_refcount(e2fs struct process_block_struct { ext2_ino_t ino; unsigned is_dir:1, is_reg:1, clear:1, suppress:1, - fragmented:1, compressed:1, bbcheck:1; - blk_t num_blocks; - blk_t max_blocks; + fragmented:1, compressed:1, bbcheck:1, snapfile:1; + blk64_t num_blocks; + blk64_t max_blocks; e2_blkcnt_t last_block; int num_illegal_blocks; - blk_t previous_block; + blk64_t previous_block; struct ext2_inode *inode; struct problem_context *pctx; ext2fs_block_bitmap fs_meta_blocks; @@ -581,6 +588,16 @@ void e2fsck_pass1(e2fsck_t ctx) ctx->flags |= E2F_FLAG_ABORT; return; } + if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_EXCLUDE_INODE) + pctx.errcode = ext2fs_allocate_block_bitmap(fs, + _("excluded block map"), + &ctx->block_excluded_map); + if (pctx.errcode) { + pctx.num = 1; + fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } e2fsck_setup_tdb_icount(ctx, 0, &ctx->inode_link_info); if (!ctx->inode_link_info) pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0, @@ -845,7 +862,7 @@ void e2fsck_pass1(e2fsck_t ctx) if (ino == EXT2_BOOT_LOADER_INO) { if (LINUX_S_ISDIR(inode->i_mode)) problem = PR_1_RESERVED_BAD_MODE; - } else if (ino == EXT2_RESIZE_INO) { + } else if (ino == EXT2_RESIZE_INO || ino == EXT2_EXCLUDE_INO) { if (inode->i_mode && !LINUX_S_ISREG(inode->i_mode)) problem = PR_1_RESERVED_BAD_MODE; @@ -1026,7 +1043,8 @@ void e2fsck_pass1(e2fsck_t ctx) (inode->i_block[EXT2_IND_BLOCK] || inode->i_block[EXT2_DIND_BLOCK] || inode->i_block[EXT2_TIND_BLOCK] || - inode->i_file_acl)) { + (inode->i_flags & EXT4_SNAPFILE_FL) || + ext2fs_file_acl_block(inode))) { inodes_to_process[process_inode_count].ino = ino; inodes_to_process[process_inode_count].inode = *inode; process_inode_count++; @@ -1094,6 +1112,25 @@ void e2fsck_pass1(e2fsck_t ctx) ctx->flags &= ~E2F_FLAG_RESIZE_INODE; } + if (ctx->flags & E2F_FLAG_EXCLUDE_INODE) { + ext2fs_block_bitmap save_bmap; + + save_bmap = fs->block_map; + fs->block_map = ctx->block_found_map; + clear_problem_context(&pctx); + pctx.errcode = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE); + if (pctx.errcode && + fix_problem(ctx, PR_1_EXCLUDE_INODE_CREATE, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, inode, + "clear_exclude"); + fs->super->s_feature_compat &= ~EXT2_FEATURE_COMPAT_EXCLUDE_INODE; + ctx->flags |= E2F_FLAG_RESTART; + } + fs->block_map = save_bmap; + ctx->flags &= ~E2F_FLAG_EXCLUDE_INODE; + } + if (ctx->flags & E2F_FLAG_RESTART) { /* * Only the master copy of the superblock and block @@ -1598,6 +1635,17 @@ void e2fsck_clear_inode(e2fsck_t ctx, ex struct ext2_inode *inode, int restart_flag, const char *source) { + /* don't clear inode with blocks when preening volume with active snapshot */ + if ((ctx->fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) && + ctx->fs->super->s_snapshot_inum) { + int i; + for (i = 0; i < EXT2_N_BLOCKS; i++) + if (inode->i_block[i]) + /* if we don't halt, inode blocks will be freed */ + preenhalt(ctx); + } + inode->i_flags = 0; inode->i_links_count = 0; ext2fs_icount_store(ctx->inode_link_info, ino, 0); @@ -1818,6 +1866,7 @@ static void check_blocks(e2fsck_t ctx, s pb.previous_block = 0; pb.is_dir = LINUX_S_ISDIR(inode->i_mode); pb.is_reg = LINUX_S_ISREG(inode->i_mode); + pb.snapfile = (pb.is_reg && (inode->i_flags & EXT4_SNAPFILE_FL)); pb.max_blocks = 1 << (31 - fs->super->s_log_block_size); pb.inode = inode; pb.pctx = pctx; @@ -1825,6 +1874,13 @@ static void check_blocks(e2fsck_t ctx, s pctx->ino = ino; pctx->errcode = 0; + if (pb.snapfile && (ctx->flags & E2F_FLAG_CLEAR_SNAPSHOTS)) { + /* discarding all snapshot files */ + e2fsck_clear_inode(ctx, ino, inode, E2F_FLAG_RESTART, + "check_blocks"); + return; + } + extent_fs = (ctx->fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS); @@ -1917,8 +1973,10 @@ static void check_blocks(e2fsck_t ctx, s } } - if (!(fs->super->s_feature_ro_compat & - EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + if ((!(fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && + /* snapshot file always supports the 'huge_file' flag */ + !(inode->i_flags & EXT4_SNAPFILE_FL)) || !(inode->i_flags & EXT4_HUGE_FILE_FL)) pb.num_blocks *= (fs->blocksize / 512); #if 0 @@ -1948,6 +2006,7 @@ static void check_blocks(e2fsck_t ctx, s size < (__u64)(pb.last_block & ~(blkpg-1)) *fs->blocksize)) bad_size = 3; else if (!(extent_fs && (inode->i_flags & EXT4_EXTENTS_FL)) && + !(pb.is_reg && (inode->i_flags & EXT4_SNAPFILE_FL)) && size > ext2_max_sizes[fs->super->s_log_block_size]) /* too big for a direct/indirect-mapped file */ bad_size = 4; @@ -1972,9 +2031,11 @@ static void check_blocks(e2fsck_t ctx, s if (LINUX_S_ISREG(inode->i_mode) && (inode->i_size_high || inode->i_size & 0x80000000UL)) ctx->large_files++; - if ((pb.num_blocks != inode->i_blocks) || - ((fs->super->s_feature_ro_compat & - EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && + if ((pb.num_blocks != ext2fs_inode_i_blocks(fs, inode)) || + (((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + /* snapshot file always supports the 'huge_file' flag */ + (inode->i_flags & EXT4_SNAPFILE_FL)) && (inode->i_flags & EXT4_HUGE_FILE_FL) && (inode->osd2.linux2.l_i_blocks_hi != 0))) { pctx->num = pb.num_blocks; @@ -2189,6 +2250,9 @@ static int process_block(ext2_filsys fs, mark_block_used(ctx, blk); } else mark_block_used(ctx, blk); + /* mark snapshot file blocks excluded */ + if (p->snapfile && ctx->block_excluded_map) + ext2fs_fast_mark_block_bitmap2(ctx->block_excluded_map, blk); p->num_blocks++; if (blockcnt >= 0) p->last_block = blockcnt; diff -Nuarp a/e2fsck/pass5.c b/e2fsck/pass5.c --- a/e2fsck/pass5.c 2009-08-23 05:44:35.000000000 +0300 +++ b/e2fsck/pass5.c 2010-07-26 09:47:16.720444777 +0300 @@ -14,6 +14,7 @@ #include "problem.h" static void check_block_bitmaps(e2fsck_t ctx); +static void check_exclude_bitmaps(e2fsck_t ctx); static void check_inode_bitmaps(e2fsck_t ctx); static void check_inode_end(e2fsck_t ctx); static void check_block_end(e2fsck_t ctx); @@ -44,6 +45,9 @@ void e2fsck_pass5(e2fsck_t ctx) check_block_bitmaps(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; + check_exclude_bitmaps(ctx); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; check_inode_bitmaps(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; @@ -60,6 +64,9 @@ void e2fsck_pass5(e2fsck_t ctx) ctx->inode_dir_map = 0; ext2fs_free_block_bitmap(ctx->block_found_map); ctx->block_found_map = 0; + if (ctx->block_excluded_map) + ext2fs_free_block_bitmap(ctx->block_excluded_map); + ctx->block_excluded_map = 0; print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io); } @@ -82,6 +89,18 @@ static void print_bitmap_problem(e2fsck_ else problem = PR_5_BLOCK_RANGE_USED; break; + case PR_5_BLOCK_NOTEXCLUDED: + if (pctx->blk == pctx->blk2) + pctx->blk2 = 0; + else + problem = PR_5_BLOCK_RANGE_NOTEXCLUDED; + break; + case PR_5_BLOCK_EXCLUDED: + if (pctx->blk == pctx->blk2) + pctx->blk2 = 0; + else + problem = PR_5_BLOCK_RANGE_EXCLUDED; + break; case PR_5_INODE_UNUSED: if (pctx->ino == pctx->ino2) pctx->ino2 = 0; @@ -293,6 +312,10 @@ redo_counts: } else if (fixit == 0) ext2fs_unmark_valid(fs); + if (fs->super->s_flags & EXT2_FLAGS_IS_SNAPSHOT) + /* ignore free block counts in next3 snapshot image */ + goto errout; + for (i = 0; i < fs->group_desc_count; i++) { if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) { pctx.group = i; @@ -323,6 +346,140 @@ errout: ext2fs_free_mem(&free_array); } +#define blk64_t blk_t +#define ext2fs_bg_flags_test(fs, group, flag) \ + (fs->group_desc[group].bg_flags & flag) +#define ext2fs_bg_flags_clear(fs, group, flag) \ + fs->group_desc[group].bg_flags &= ~flag +#define ext2fs_blocks_count(sb) \ + (sb)->s_blocks_count +#define ext2fs_fast_test_block_bitmap2 ext2fs_fast_test_block_bitmap + +static void check_exclude_bitmaps(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + blk64_t i; + int group = 0; + int blocks = 0; + int actual, bitmap; + struct problem_context pctx; + int problem, save_problem, fixit, had_problem; + errcode_t retval; + int csum_flag; + int skip_group = 0; + + clear_problem_context(&pctx); + + if (!(fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) + return; + + csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM); +redo_counts: + had_problem = 0; + save_problem = 0; + pctx.blk = pctx.blk2 = NO_BLK; + if (csum_flag && + (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) + skip_group++; + for (i = fs->super->s_first_data_block; + i < ext2fs_blocks_count(fs->super); + i++) { + actual = ext2fs_fast_test_block_bitmap2(ctx->block_excluded_map, i); + + if (skip_group) { + bitmap = 0; + actual = (actual != 0); + } else + bitmap = ext2fs_fast_test_block_bitmap2(fs->exclude_map, i); + + if (actual == bitmap) + goto do_counts; + + if (!actual && bitmap) { + /* + * Block not excluded, but marked in exclude bitmap. + */ + problem = PR_5_BLOCK_NOTEXCLUDED; + } else { + /* + * Block excluded, but not marked in exclude bitmap. + */ + problem = PR_5_BLOCK_EXCLUDED; + + if (skip_group) { + struct problem_context pctx2; + pctx2.blk = i; + pctx2.group = group; + if (fix_problem(ctx, PR_5_BLOCK_UNINIT,&pctx2)){ + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); + skip_group = 0; + } + } + } + if (pctx.blk == NO_BLK) { + pctx.blk = pctx.blk2 = i; + save_problem = problem; + } else { + if ((problem == save_problem) && + (pctx.blk2 == i-1)) + pctx.blk2++; + else { + print_bitmap_problem(ctx, save_problem, &pctx); + pctx.blk = pctx.blk2 = i; + save_problem = problem; + } + } + ctx->flags |= E2F_FLAG_PROG_SUPPRESS; + had_problem++; + + do_counts: + blocks ++; + if ((blocks == fs->super->s_blocks_per_group) || + (i == fs->super->s_blocks_count-1)) { + group ++; + blocks = 0; + skip_group = 0; + if (ctx->progress) + if ((ctx->progress)(ctx, 5, group, + fs->group_desc_count*2)) + return; + if (csum_flag && + (i != ext2fs_blocks_count(fs->super)-1) && + ext2fs_bg_flags_test(fs, group, + EXT2_BG_BLOCK_UNINIT)) + skip_group++; + } + } + if (pctx.blk != NO_BLK) + print_bitmap_problem(ctx, save_problem, &pctx); + if (had_problem) + fixit = end_problem_latch(ctx, PR_LATCH_XBITMAP); + else + fixit = -1; + ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS; + + if (fixit == 1) { + ext2fs_free_block_bitmap(fs->exclude_map); + retval = ext2fs_copy_bitmap(ctx->block_excluded_map, + &fs->exclude_map); + if (retval) { + clear_problem_context(&pctx); + fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + ext2fs_mark_exclude_dirty(fs); + /* clear fix_exclude flag */ + if (fs->super->s_flags & EXT2_FLAGS_FIX_EXCLUDE) { + fs->super->s_flags &= ~EXT2_FLAGS_FIX_EXCLUDE; + ext2fs_mark_super_dirty(fs); + } + } else if (fixit == 0) + ext2fs_unmark_valid(fs); +} + static void check_inode_bitmaps(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; diff -Nuarp a/e2fsck/problem.c b/e2fsck/problem.c --- a/e2fsck/problem.c 2009-08-22 20:19:04.000000000 +0300 +++ b/e2fsck/problem.c 2010-07-26 09:47:16.690443946 +0300 @@ -40,7 +40,9 @@ #define PROMPT_UNLINK 17 #define PROMPT_CLEAR_HTREE 18 #define PROMPT_RECREATE 19 -#define PROMPT_NULL 20 +#define PROMPT_TERMINATE_LIST 20 +#define PROMPT_DISCARD_SNAPSHOTS 21 +#define PROMPT_NULL 22 /* * These are the prompts which are used to ask the user if they want @@ -67,7 +69,9 @@ static const char *prompt[] = { N_("Unlink"), /* 17 */ N_("Clear HTree index"),/* 18 */ N_("Recreate"), /* 19 */ - "", /* 20 */ + N_("Terminate list"), /* 20 */ + N_("Discard snapshots"),/* 21 */ + "", /* 22 */ }; /* @@ -332,6 +336,27 @@ static struct e2fsck_problem problem_tab N_("Resize @i not valid. "), PROMPT_RECREATE, 0 }, + /* Exclude not enabled, but exclude inode is non-zero */ + { PR_0_CLEAR_EXCLUDE_INODE, + N_("Exclude_@i not enabled, but the exclude @i is non-zero. "), + PROMPT_CLEAR, 0 }, + + /* Exclude inode invalid */ + { PR_0_EXCLUDE_INODE_INVALID, + N_("Exclude @i not valid. "), + PROMPT_RECREATE, 0 }, + + /* Bad snapshot on list */ + { PR_0_BAD_SNAPSHOT, + N_("Bad @i found on snapshot list. "), + PROMPT_TERMINATE_LIST, PR_PREEN_OK }, + + /* Corrupted snapshot */ + { PR_0_FIX_SNAPSHOT, + N_("@f may contain corrupted snapshots.\n" + "This version of e2fsck does not support fixing snapshots.\n"), + PROMPT_DISCARD_SNAPSHOTS, 0 }, + /* Last mount time is in the future */ { PR_0_FUTURE_SB_LAST_MOUNT, N_("@S last mount time (%t,\n\tnow = %T) is in the future.\n"), @@ -800,6 +825,11 @@ static struct e2fsck_problem problem_tab N_("Resize @i (re)creation failed: %m."), PROMPT_ABORT, 0 }, + /* Exclude inode failed */ + { PR_1_EXCLUDE_INODE_CREATE, + N_("Exclude @i (re)creation failed: %m."), + PROMPT_CLEAR, 0 }, + /* invalid inode->i_extra_isize */ { PR_1_EXTRA_ISIZE, N_("@i %i has a extra size (%IS) which is @n\n"), @@ -1528,6 +1558,26 @@ static struct e2fsck_problem problem_tab "\n", PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, + /* Exclude bitmap differences header */ + { PR_5_EXCLUDE_BITMAP_HEADER, + N_("Exclude @B differences: "), + PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG}, + + /* Block not excluded, but marked in exclude bitmap */ + { PR_5_BLOCK_NOTEXCLUDED, + " -%b", + PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Block excluded, but not marked in exclude bitmap */ + { PR_5_BLOCK_EXCLUDED, + " +%b", + PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Exclude bitmap differences end */ + { PR_5_EXCLUDE_BITMAP_END, + "\n", + PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, + /* Inode bitmap differences header */ { PR_5_INODE_BITMAP_HEADER, N_("@i @B differences: "), @@ -1604,6 +1654,16 @@ static struct e2fsck_problem problem_tab " +(%b--%c)", PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + /* Block range not excluded, but marked in exclude bitmap */ + { PR_5_BLOCK_RANGE_NOTEXCLUDED, + " -(%b--%c)", + PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Block range excluded, but not marked in exclude bitmap */ + { PR_5_BLOCK_RANGE_EXCLUDED, + " +(%b--%c)", + PROMPT_NONE, PR_LATCH_XBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + /* Inode range not used, but marked in bitmap */ { PR_5_INODE_RANGE_UNUSED, " -(%i--%j)", @@ -1645,6 +1705,7 @@ static struct latch_descr pr_latch_info[ { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 }, { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END }, { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END }, + { PR_LATCH_XBITMAP, PR_5_EXCLUDE_BITMAP_HEADER, PR_5_EXCLUDE_BITMAP_END }, { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 }, { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END }, { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 }, diff -Nuarp a/e2fsck/problem.h b/e2fsck/problem.h --- a/e2fsck/problem.h 2009-08-22 20:19:04.000000000 +0300 +++ b/e2fsck/problem.h 2010-07-26 09:47:16.730448437 +0300 @@ -39,6 +39,7 @@ struct problem_context { #define PR_LATCH_TOOBIG 0x0080 /* Latch for file to big errors */ #define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */ #define PR_LATCH_BG_CHECKSUM 0x00A0 /* Latch for block group checksums */ +#define PR_LATCH_XBITMAP 0x00B0 /* Latch for pass 5 exclude bitmap proc. */ #define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1) @@ -227,6 +228,18 @@ struct problem_context { /* Block group checksum (latch question) */ #define PR_0_GDT_CSUM_LATCH 0x00003E +/* Exclude_inode not enabled, but exclude inode is non-zero */ +#define PR_0_CLEAR_EXCLUDE_INODE 0x000100 + +/* Exclude inode invalid */ +#define PR_0_EXCLUDE_INODE_INVALID 0x000101 + +/* Bas snapshot on list */ +#define PR_0_BAD_SNAPSHOT 0x000102 + +/* Corrupted snapshot */ +#define PR_0_FIX_SNAPSHOT 0x000103 + /* * Pass 1 errors @@ -517,6 +530,9 @@ struct problem_context { /* Extent node header invalid */ #define PR_1_EXTENT_HEADER_INVALID 0x01005F +/* Exclude inode failed */ +#define PR_1_EXCLUDE_INODE_CREATE 0x010100 + /* * Pass 1b errors */ @@ -980,6 +996,24 @@ struct problem_context { /* Inode in use but group is marked INODE_UNINIT */ #define PR_5_INODE_UNINIT 0x050019 +/* Exclude bitmap differences header */ +#define PR_5_EXCLUDE_BITMAP_HEADER 0x050100 + +/* Block not excluded, but marked in exclude bitmap */ +#define PR_5_BLOCK_NOTEXCLUDED 0x050101 + +/* Block excluded, but not marked in exclude bitmap */ +#define PR_5_BLOCK_EXCLUDED 0x050102 + +/* Block range not excluded, but marked in exclude bitmap */ +#define PR_5_BLOCK_RANGE_NOTEXCLUDED 0x050103 + +/* Block range excluded, but not marked in exclude bitmap */ +#define PR_5_BLOCK_RANGE_EXCLUDED 0x050104 + +/* Exclude bitmap differences end */ +#define PR_5_EXCLUDE_BITMAP_END 0x050105 + /* * Post-Pass 5 errors */ diff -Nuarp a/e2fsck/rehash.c b/e2fsck/rehash.c --- a/e2fsck/rehash.c 2009-08-23 05:44:35.000000000 +0300 +++ b/e2fsck/rehash.c 2010-07-26 09:47:16.710487408 +0300 @@ -812,6 +812,13 @@ void e2fsck_rehash_directories(e2fsck_t int cur, max, all_dirs, dir_index, first = 1; init_resource_track(&rtrack, ctx->fs->io); + + /* never rehash directories when scanning volume with active snapshot */ + if ((ctx->fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) && + ctx->fs->super->s_snapshot_inum) + return; + all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS; if (!ctx->dirs_to_hash && !all_dirs) diff -Nuarp a/e2fsck/super.c b/e2fsck/super.c --- a/e2fsck/super.c 2009-08-23 05:44:35.000000000 +0300 +++ b/e2fsck/super.c 2010-07-26 09:47:16.700442435 +0300 @@ -229,6 +229,12 @@ static int release_orphan_inodes(e2fsck_ struct problem_context pctx; char *block_buf; + /* never release orphans when scanning volume with active snapshot */ + if ((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) && + fs->super->s_snapshot_inum) + return 0; + if ((ino = fs->super->s_last_orphan) == 0) return 0; @@ -424,6 +430,240 @@ cleanup: } /* + * Check the exclude inode to make sure it is sane. We check both for + * the case where exclude bitmap is not enabled (in which case the + * exclude inode should be cleared) as well as the case where exclude + * bitmap is enabled. + */ +void check_exclude_inode(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + struct ext2_inode inode; + struct problem_context pctx; + int i, flags = 0; + blk_t blk; + errcode_t retval; + + clear_problem_context(&pctx); + + /* Migrate from old to new Next3 on-disk format */ + if (fs->super->s_feature_compat & + NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD) { + /* Move exclude inode from old to new position */ + retval = ext2fs_read_inode(fs, EXT2_EXCLUDE_INO_OLD, &inode); + if (!retval) { + e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, &inode, + "copy_old_exclude_ino"); + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO_OLD, &inode, + "clear_old_exclude_ino"); + /* Clear old exclude inode flag */ + fs->super->s_feature_compat &= + ~NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD; + ext2fs_mark_super_dirty(fs); + } + } + + /* Read the exclude inode */ + pctx.ino = EXT2_EXCLUDE_INO; + retval = ext2fs_read_inode(fs, EXT2_EXCLUDE_INO, &inode); + if (retval) { + if (fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE) + ctx->flags |= E2F_FLAG_EXCLUDE_INODE; + return; + } + + /* + * If the exclude inode feature isn't set, check to make sure + * the exclude inode is cleared; then we're done. + */ + if (!(fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) { + for (i=0; i < EXT2_N_BLOCKS; i++) { + if (inode.i_block[i]) + break; + } + if ((i < EXT2_N_BLOCKS) && + fix_problem(ctx, PR_0_CLEAR_EXCLUDE_INODE, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, &inode, + "clear_exclude"); + } + return; + } + + /* + * The exclude inode feature is enabled; check to make sure the + * only block in use is the double indirect block + */ + blk = inode.i_block[EXT2_DIND_BLOCK]; + for (i=0; i < EXT2_N_BLOCKS; i++) { + if (i != EXT2_DIND_BLOCK && inode.i_block[i]) + break; + } + if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count || + !(inode.i_mode & LINUX_S_IFREG) || + (blk < fs->super->s_first_data_block || + blk >= fs->super->s_blocks_count)) { + if (fix_problem(ctx, PR_0_EXCLUDE_INODE_INVALID, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, &inode, + "clear_exclude"); + ctx->flags |= E2F_FLAG_EXCLUDE_INODE; + } + return; + } + + if (!(ctx->options & E2F_OPT_READONLY)) + flags = EXCLUDE_ALLOC; + /* + * create exclude inode and/or allocate missing exclude bitmap blocks. + */ + clear_problem_context(&pctx); + pctx.errcode = ext2fs_create_exclude_inode(fs, flags); + if (pctx.errcode && + fix_problem(ctx, PR_1_EXCLUDE_INODE_CREATE, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_EXCLUDE_INO, &inode, + "clear_exclude"); + ctx->flags |= E2F_FLAG_EXCLUDE_INODE; + } +} + +/* + * Check that snapshot list contains valid snapshot files. + * Returns the number of valid snapshots on list. + * + * TODO: cleanup orphan snapshot files (not found on list) + */ +static int check_snapshot_list(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + struct ext2_super_block *sb = fs->super; + struct ext2_inode inode; + struct ext2_inode inode_prev; + ext2_ino_t ino = sb->s_snapshot_list; + ext2_ino_t ino_prev = 0; + errcode_t retval = 0; + struct problem_context pctx; + int i = 0; + + if (!ino && sb->s_snapshot_inum) { + /* reset snapshot list head to active snapshot */ + ino = sb->s_snapshot_list = sb->s_snapshot_inum; + ext2fs_mark_super_dirty(fs); + } + if (ino) + fputs(_("Checking snapshots: "), stderr); + + while (ino) { + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval || !(inode.i_flags & EXT4_SNAPFILE_FL) || + !LINUX_S_ISREG(inode.i_mode) || + inode.i_dtime) { + if (ino == sb->s_snapshot_list) { + /* reset list head */ + sb->s_snapshot_list = 0; + ext2fs_mark_super_dirty(fs); + ino = sb->s_snapshot_inum; + continue; + } + clear_problem_context(&pctx); + if (!fix_problem(ctx, PR_0_BAD_SNAPSHOT, &pctx)) + break; + + /* disconnect inode from snapshot list */ + if (ino == sb->s_snapshot_inum) { + /* reset active snapshot */ + sb->s_snapshot_inum = 0; + ext2fs_mark_super_dirty(fs); + } + if (ino_prev) { + /* terminate list at prev inode */ + inode_prev.i_next_snapshot = 0; + e2fsck_write_inode(ctx, ino_prev, &inode_prev, + "terminate snapshot list"); + } + break; + } + + fprintf(stderr, _("%u,"), inode.i_generation); + inode_prev = inode; + ino_prev = ino; + ino = inode.i_next_snapshot; + i++; + } + + if (ino_prev && !ino) + fputs(_("done\n"), stderr); + return i; +} + +/* + * This function checks if the file system has snapshots + */ +void check_snapshots(e2fsck_t ctx) +{ + struct ext2_super_block *sb = ctx->fs->super; + struct problem_context pctx; + int cont; + + if (!(sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT)) + /* no snapshots */ + return; + + if ((ctx->options & E2F_OPT_FIX_SNAPSHOT) || + (sb->s_flags & EXT2_FLAGS_FIX_SNAPSHOT)) { + /* corrupted snapshot need to be fixed */ + clear_problem_context(&pctx); + if (fix_problem(ctx, PR_0_FIX_SNAPSHOT, &pctx)) { + if ((sb->s_flags & EXT2_FLAGS_FIX_SNAPSHOT) || + (sb->s_snapshot_list || sb->s_snapshot_inum)) { + /* reset snapshot list head */ + sb->s_snapshot_list = sb->s_snapshot_inum = 0; + sb->s_flags &= ~EXT2_FLAGS_FIX_SNAPSHOT; + ext2fs_mark_super_dirty(ctx->fs); + } + /* don't ask again after pass1 */ + ctx->options &= ~E2F_OPT_FIX_SNAPSHOT; + /* clear all snapshot inodes (in pass1) */ + ctx->flags |= E2F_FLAG_CLEAR_SNAPSHOTS; + if (sb->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE) + /* and reset exclude bitmap */ + ctx->flags |= E2F_FLAG_EXCLUDE_INODE; + return; + } + } + + if (!check_snapshot_list(ctx)) + /* no valid snapshots on list */ + return; + + if (!sb->s_snapshot_inum) + /* no active snapshot */ + return; + + if ((ctx->options & E2F_OPT_PREEN) || + (ctx->options & E2F_OPT_NO)) + /* preen and readonly modes are snapshot friendly */ + return; + + printf(_("%s has snapshots. "), ctx->filesystem_name); + if (!ctx->interactive) + fatal_error(ctx, _("Cannot continue, aborting.\n\n")); + printf(_("\n\n\007\007\007\007WARNING!!! " + "Running e2fsck on filesystem with snapshots may\n" + "damage the snapshots.\007\007\007\n\n")); + cont = ask_yn(_("Do you really want to continue"), -1); + if (!cont) { + printf (_("check aborted.\n")); + exit (0); + } +} + +/* * This function checks the dirhash signed/unsigned hint if necessary. */ static void e2fsck_fix_dirhash_hint(e2fsck_t ctx) @@ -450,6 +690,43 @@ static void e2fsck_fix_dirhash_hint(e2fs } } +/* + * This function prints the message buffer at the end of super block. + */ +static void e2fsck_print_message_buffer(e2fsck_t ctx) +{ + char *buf; + int len = ctx->fs->blocksize - 2*SUPERBLOCK_OFFSET; + unsigned offset = 0; + int retval; +#define MSGLEN 256 + + if (len < 2*SUPERBLOCK_OFFSET) + return; + + buf = (char *) e2fsck_allocate_memory(ctx, len, "message buffer"); + + io_channel_set_blksize(ctx->fs->io, SUPERBLOCK_OFFSET); + /* read message buffer from super block + 2K */ + retval = io_channel_read_blk(ctx->fs->io, 2, 2, buf); + if (retval || !*buf) + goto out; + + /* print messages in buffer */ + puts("Error messages recorded in message buffer:"); + while (offset < len && buf[offset]) { + fputs(buf+offset, stdout); + offset += MSGLEN; + } + /* clear message buffer */ + memset(buf, 0, len); + retval = io_channel_write_blk(ctx->fs->io, 2, 2, buf); + puts("End of message buffer."); +out: + io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize); + ext2fs_free_mem(&buf); +} + void check_super_block(e2fsck_t ctx) { @@ -855,6 +1132,11 @@ void check_super_block(e2fsck_t ctx) */ e2fsck_fix_dirhash_hint(ctx); + /* + * Print message buffer if necessary + */ + e2fsck_print_message_buffer(ctx); + return; } diff -Nuarp a/e2fsck/unix.c b/e2fsck/unix.c --- a/e2fsck/unix.c 2009-08-23 05:44:35.000000000 +0300 +++ b/e2fsck/unix.c 2010-07-26 09:47:16.700442435 +0300 @@ -73,7 +73,7 @@ int journal_enable_debug = -1; static void usage(e2fsck_t ctx) { fprintf(stderr, - _("Usage: %s [-panyrcdfvtDFV] [-b superblock] [-B blocksize]\n" + _("Usage: %s [-panyrcdfvtxDFV] [-b superblock] [-B blocksize]\n" "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n" "\t\t[-l|-L bad_blocks_file] [-C fd] [-j external_journal]\n" "\t\t[-E extended-options] device\n"), @@ -92,6 +92,7 @@ static void usage(e2fsck_t ctx) " -j external_journal Set location of the external journal\n" " -l bad_blocks_file Add to badblocks list\n" " -L bad_blocks_file Set badblocks list\n" + " -x Fix or discard snapshots\n" )); exit(FSCK_USAGE); @@ -659,7 +660,7 @@ static errcode_t PRS(int argc, char *arg ctx->program_name = *argv; else ctx->program_name = "e2fsck"; - while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF) + while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkx")) != EOF) switch (c) { case 'C': ctx->progress = e2fsck_update_progress; @@ -782,6 +783,9 @@ static errcode_t PRS(int argc, char *arg case 'k': keep_bad_blocks++; break; + case 'x': + ctx->options |= E2F_OPT_FIX_SNAPSHOT; + break; default: usage(ctx); } @@ -1284,6 +1288,8 @@ print_unsupp_features: fatal_error(ctx, 0); check_if_skip(ctx); check_resize_inode(ctx); + check_snapshots(ctx); + check_exclude_inode(ctx); if (bad_blocks_file) read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks); else if (cflag) diff -Nuarp a/lib/e2p/e2p.h b/lib/e2p/e2p.h --- a/lib/e2p/e2p.h 2009-08-23 05:44:35.000000000 +0300 +++ b/lib/e2p/e2p.h 2010-07-26 09:47:16.630443266 +0300 @@ -17,6 +17,24 @@ /* `options' for print_flags() */ #define PFOPT_LONG 1 /* Must be 1 for compatibility with `int long_format'. */ +#define PFOPT_SNAPSHOT 2 /* list/control snapshot flags */ +#define PFOPT_SNAPSHOT_X 4 /* -X option for 'Snapshot' flags */ + +/* + * snapshot status/control flags for lsattr/chattr -X. + * reusing compression flags on the GET/SETFLAGS ioctl for snapshot control API. + * all the flags below are either read-only on-disk inode flags (deleted and + * shrunk) or in-memory inode status flags (the rest). + * TODO: implement new ioctls for snapshot status/control. + */ +#define NEXT3_SNAPFILE_LIST_FL 0x00000100 /* snapshot is on list */ +#define NEXT3_SNAPFILE_ENABLED_FL 0x00000200 /* snapshot is enabled */ +#define NEXT3_SNAPFILE_ACTIVE_FL 0x00000400 /* snapshot is active */ +#define NEXT3_SNAPFILE_INUSE_FL 0x00000800 /* snapshot is in-use */ +#define NEXT3_SNAPFILE_DELETED_FL 0x04000000 /* snapshot is deleted */ +#define NEXT3_SNAPFILE_SHRUNK_FL 0x08000000 /* snapshot is shrunk */ +#define NEXT3_SNAPFILE_OPEN_FL 0x10000000 /* snapshot is mounted */ +#define NEXT3_SNAPFILE_TAGGED_FL 0x20000000 /* snapshot is tagged */ int fgetflags (const char * name, unsigned long * flags); diff -Nuarp a/lib/e2p/feature.c b/lib/e2p/feature.c --- a/lib/e2p/feature.c 2009-08-13 04:39:57.000000000 +0300 +++ b/lib/e2p/feature.c 2010-07-26 09:47:16.620442748 +0300 @@ -39,6 +39,8 @@ static struct feature feature_list[] = { "resize_inode" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_LAZY_BG, "lazy_bg" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_INODE, + "exclude_inode" }, { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, "sparse_super" }, @@ -54,6 +56,8 @@ static struct feature feature_list[] = { "dir_nlink" }, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE, "extra_isize" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT, + "has_snapshot" }, { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION, "compression" }, diff -Nuarp a/lib/e2p/ls.c b/lib/e2p/ls.c --- a/lib/e2p/ls.c 2009-08-23 05:44:35.000000000 +0300 +++ b/lib/e2p/ls.c 2010-07-26 09:47:16.620442748 +0300 @@ -158,6 +158,18 @@ static void print_super_flags(struct ext fputs("test_filesystem ", f); flags_found++; } + if (s->s_flags & EXT2_FLAGS_IS_SNAPSHOT) { + fputs("is_snapshot ", f); + flags_found++; + } + if (s->s_flags & EXT2_FLAGS_FIX_SNAPSHOT) { + fputs("fix_snapshot ", f); + flags_found++; + } + if (s->s_flags & EXT2_FLAGS_FIX_EXCLUDE) { + fputs("fix_exclude ", f); + flags_found++; + } if (flags_found) fputs("\n", f); else @@ -311,6 +323,17 @@ void list_super2(struct ext2_super_block if (sb->s_last_orphan) fprintf(f, "First orphan inode: %u\n", sb->s_last_orphan); + if (sb->s_snapshot_inum) { + fprintf(f, "Snapshot inode: %u\n", + sb->s_snapshot_inum); + fprintf(f, "Snapshot ID: %u\n", + sb->s_snapshot_id); + fprintf(f, "Snapshot reserved blocks: %llu\n", + sb->s_snapshot_r_blocks_count); + } + if (sb->s_snapshot_list) + fprintf(f, "Snapshot list first inode: %u\n", + sb->s_snapshot_list); if ((sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) || sb->s_def_hash_version) fprintf(f, "Default directory hash: %s\n", diff -Nuarp a/lib/e2p/pf.c b/lib/e2p/pf.c --- a/lib/e2p/pf.c 2009-08-13 04:39:57.000000000 +0300 +++ b/lib/e2p/pf.c 2010-07-26 09:47:16.610443636 +0300 @@ -46,16 +46,47 @@ static struct flags_name flags_array[] = { EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" }, { EXT4_EXTENTS_FL, "e", "Extents" }, { EXT4_HUGE_FILE_FL, "h", "Huge_file" }, + { EXT4_SNAPFILE_FL, "x", "Snapshot_File" }, + { 0, NULL, NULL } +}; + +/* Traditional snapshot flags */ +static struct flags_name snapshot_flags_array[] = { + { NEXT3_SNAPFILE_LIST_FL, "l", "on_List" }, + { NEXT3_SNAPFILE_ENABLED_FL, "e", "Enabled" }, + { NEXT3_SNAPFILE_ACTIVE_FL, "a", "Active" }, + { NEXT3_SNAPFILE_INUSE_FL, "i", "Inuse_by_previous" }, + { NEXT3_SNAPFILE_DELETED_FL, "d", "Deleted" }, + { NEXT3_SNAPFILE_SHRUNK_FL, "s", "Shrunk" }, + { NEXT3_SNAPFILE_OPEN_FL, "m", "Mounted" }, + { NEXT3_SNAPFILE_TAGGED_FL, "t", "Tagged" }, + { 0, NULL, NULL } +}; + +/* Cool 'Snapshot' flags */ +static struct flags_name snapshot_X_flags_array[] = { + { NEXT3_SNAPFILE_LIST_FL, "S", "on_liSt" }, + { NEXT3_SNAPFILE_ENABLED_FL, "n", "eNabled" }, + { NEXT3_SNAPFILE_ACTIVE_FL, "a", "Active" }, + { NEXT3_SNAPFILE_INUSE_FL, "p", "inuse_by_Previous" }, + { NEXT3_SNAPFILE_DELETED_FL, "s", "Deleted" }, + { NEXT3_SNAPFILE_SHRUNK_FL, "h", "sHrunk" }, + { NEXT3_SNAPFILE_OPEN_FL, "o", "mOunted" }, + { NEXT3_SNAPFILE_TAGGED_FL, "t", "Tagged" }, { 0, NULL, NULL } }; void print_flags (FILE * f, unsigned long flags, unsigned options) { + struct flags_name *array = ((options & PFOPT_SNAPSHOT_X) ? + snapshot_X_flags_array : + ((options & PFOPT_SNAPSHOT) ? + snapshot_flags_array : flags_array)); int long_opt = (options & PFOPT_LONG); struct flags_name *fp; int first = 1; - for (fp = flags_array; fp->flag != 0; fp++) { + for (fp = array; fp->flag != 0; fp++) { if (flags & fp->flag) { if (long_opt) { if (first) diff -Nuarp a/lib/ext2fs/block.c b/lib/ext2fs/block.c --- a/lib/ext2fs/block.c 2009-08-23 05:44:35.000000000 +0300 +++ b/lib/ext2fs/block.c 2010-07-26 09:47:16.650443902 +0300 @@ -309,6 +309,7 @@ errcode_t ext2fs_block_iterate2(ext2_fil errcode_t retval; struct block_context ctx; int limit; + blk_t blk64; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); @@ -456,9 +457,17 @@ errcode_t ext2fs_block_iterate2(ext2_fil * Iterate over normal data blocks */ for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) { + if ((inode.i_flags & EXT4_SNAPFILE_FL) && + LINUX_S_ISREG(inode.i_mode) && + i < NEXT3_EXTRA_TIND_BLOCKS) + /* snapshot file extra triple indirect blocks */ + continue; + if (inode.i_block[i] || (flags & BLOCK_FLAG_APPEND)) { - ret |= (*ctx.func)(fs, &inode.i_block[i], - ctx.bcount, 0, i, priv_data); + blk64 = inode.i_block[i]; + ret |= (*ctx.func)(fs, &blk64, ctx.bcount, 0, i, + priv_data); + inode.i_block[i] = (blk_t) blk64; if (ret & BLOCK_ABORT) goto abort_exit; } @@ -484,6 +493,15 @@ errcode_t ext2fs_block_iterate2(ext2_fil if (ret & BLOCK_ABORT) goto abort_exit; } + if ((inode.i_flags & EXT4_SNAPFILE_FL) && LINUX_S_ISREG(inode.i_mode)) { + /* iterate snapshot file extra triple indirect blocks */ + for (i = 0; i < NEXT3_EXTRA_TIND_BLOCKS; i++) { + ret |= block_iterate_tind(&inode.i_block[i], + 0, EXT2_N_BLOCKS+i, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } + } abort_exit: if (ret & BLOCK_CHANGED) { diff -Nuarp a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c --- a/lib/ext2fs/bmap.c 2009-08-13 04:39:57.000000000 +0300 +++ b/lib/ext2fs/bmap.c 2010-07-26 09:47:16.660444109 +0300 @@ -136,6 +136,8 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, e struct ext2_inode inode_buf; ext2_extent_handle_t handle = 0; blk_t addr_per_block; + blk64_t addr_per_tind_block; + int tind; blk_t b, blk32; char *buf = 0; errcode_t retval = 0; @@ -289,7 +291,20 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, e /* Triply indirect block */ block -= addr_per_block * addr_per_block; - b = inode_bmap(inode, EXT2_TIND_BLOCK); + tind = EXT2_TIND_BLOCK; + addr_per_tind_block = addr_per_block * addr_per_block * addr_per_block; + if (block > addr_per_tind_block) { + /* use direct blocks as extra triple indirect blocks? */ + tind = block / addr_per_tind_block; + block -= tind * addr_per_tind_block; + if (!(inode->i_flags & EXT4_SNAPFILE_FL) || + !LINUX_S_ISREG(inode->i_mode) || + tind >= NEXT3_EXTRA_TIND_BLOCKS) { + retval = EXT2_ET_BAD_BLOCK_NUM; + goto done; + } + } + b = inode_bmap(inode, tind); if (!b) { if (!(bmap_flags & BMAP_ALLOC)) { if (bmap_flags & BMAP_SET) @@ -301,7 +316,7 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, e retval = ext2fs_alloc_block(fs, b, block_buf, &b); if (retval) goto done; - inode_bmap(inode, EXT2_TIND_BLOCK) = b; + inode_bmap(inode, tind) = b; blocks_alloc++; } retval = block_tind_bmap(fs, bmap_flags, b, block_buf, diff -Nuarp a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h --- a/lib/ext2fs/ext2_fs.h 2009-08-23 05:44:35.000000000 +0300 +++ b/lib/ext2fs/ext2_fs.h 2010-07-26 09:47:16.680445479 +0300 @@ -50,6 +50,8 @@ #define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ #define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ #define EXT2_JOURNAL_INO 8 /* Journal inode */ +#define EXT2_EXCLUDE_INO 9 /* The "exclude" inode, for snapshots */ +#define EXT2_EXCLUDE_INO_OLD 10 /* Old exclude inode */ /* First non-reserved inode for old ext2 filesystems */ #define EXT2_GOOD_OLD_FIRST_INO 11 @@ -149,6 +151,12 @@ struct ext2_group_desc __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/ }; +#define bg_exclude_bitmap_old bg_reserved[0] /* Old exclude bitmap cache */ +#define bg_cow_bitmap_old bg_reserved[1] /* Old COW bitmap cache */ + +/* + * Structure of a blocks group descriptor + */ struct ext4_group_desc { __u32 bg_block_bitmap; /* Blocks bitmap block */ @@ -246,6 +254,17 @@ struct ext2_dx_countlimit { #define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) #define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) #define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) +/* + * Snapshot files have different indirection mapping that can map up to 2^32 + * logical blocks, so they can cover the mapped filesystem block address space. + * Next3 must use either 4K or 8K blocks (depending on PAGE_SIZE). + * With 8K blocks, 1 triple indirect block maps 2^33 logical blocks. + * With 4K blocks (the system default), each triple indirect block maps 2^30 + * logical blocks, so 4 triple indirect blocks map 2^32 logical blocks. + * Snapshot files in small filesystems (<= 4G), use only 1 double indirect + * block to map the entire filesystem. + */ +#define NEXT3_EXTRA_TIND_BLOCKS 3 /* * Inode flags @@ -273,10 +292,14 @@ struct ext2_dx_countlimit { #define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ #define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ +#define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ +#define EXT4_SNAPFILE_FL 0x01000000 /* Inode is a snapshot */ +#define EXT4_SNAPFILE_DELETED_FL 0x04000000 /* Snapshot is being deleted */ +#define EXT4_SNAPFILE_SHRUNK_FL 0x08000000 /* Snapshot shrink has completed */ #define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ -#define EXT2_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */ -#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */ +#define EXT2_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x004B80FF /* User modifiable flags */ /* * ioctl commands @@ -422,7 +445,7 @@ struct ext2_inode_large { #define i_size_high i_dir_acl #if defined(__KERNEL__) || defined(__linux__) -#define i_reserved1 osd1.linux1.l_i_reserved1 +#define i_next_snapshot osd1.linux1.l_i_version #define i_frag osd2.linux2.l_i_frag #define i_fsize osd2.linux2.l_i_fsize #define i_uid_low i_uid @@ -461,6 +484,9 @@ struct ext2_inode_large { #define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */ #define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */ #define EXT2_FLAGS_TEST_FILESYS 0x0004 /* OK for use on development code */ +#define EXT2_FLAGS_IS_SNAPSHOT 0x0010 /* This is a snapshot image */ +#define EXT2_FLAGS_FIX_SNAPSHOT 0x0020 /* Snapshot inodes corrupted */ +#define EXT2_FLAGS_FIX_EXCLUDE 0x0040 /* Exclude bitmaps corrupted */ /* * Mount flags @@ -580,9 +606,21 @@ struct ext2_super_block { __u8 s_reserved_char_pad; __u16 s_reserved_pad; /* Padding to next 32bits */ __u64 s_kbytes_written; /* nr of lifetime kilobytes written */ - __u32 s_reserved[160]; /* Padding to the end of the block */ + __u32 s_snapshot_inum; /* Inode number of active snapshot */ + __u32 s_snapshot_id; /* sequential ID of active snapshot */ + __u64 s_snapshot_r_blocks_count; /* reserved blocks for active + snapshot's future use */ + __u32 s_snapshot_list; /* inode number of the head of the + on-disk snapshot list */ + __u32 s_reserved[155]; /* Padding to the end of the block */ }; +/* old snapshot field positions */ +#define s_snapshot_list_old s_reserved[151] /* Old snapshot list head */ +#define s_snapshot_r_blocks_old s_reserved[152] /* Old reserved for snapshot */ +#define s_snapshot_id_old s_reserved[153] /* Old active snapshot ID */ +#define s_snapshot_inum_old s_reserved[154] /* Old active snapshot inode */ + /* * Codes for operating systems */ @@ -609,6 +647,17 @@ struct ext2_super_block { #define EXT3_JNL_BACKUP_BLOCKS 1 /* + * A Next3 'big' journal needs to accomodate extra snapshot COW credits. + * Default journal size accomodates maximum possible COW credits. + * Minimum required journal size accomodates the avarage COW credits. + */ +#define EXT3_DEF_JOURNAL_BLOCKS 32768 +#define NEXT3_AVG_COW_CREDITS 16 +#define NEXT3_MAX_COW_CREDITS 24 +#define NEXT3_MIN_JOURNAL_BLOCKS (EXT3_DEF_JOURNAL_BLOCKS*NEXT3_AVG_COW_CREDITS) +#define NEXT3_DEF_JOURNAL_BLOCKS (EXT3_DEF_JOURNAL_BLOCKS*NEXT3_MAX_COW_CREDITS) + +/* * Feature set definitions */ @@ -626,6 +675,9 @@ struct ext2_super_block { #define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010 #define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 #define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040 +#define EXT2_FEATURE_COMPAT_EXCLUDE_INODE 0x0080 +#define NEXT3_FEATURE_COMPAT_BIG_JOURNAL_OLD 0x1000 /* Old big journal */ +#define NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD 0x2000 /* Old exclude inode */ #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 @@ -634,6 +686,11 @@ struct ext2_super_block { #define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 +#define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT 0x0080 +#define NEXT3_FEATURE_RO_COMPAT_HAS_SNAPSHOT_OLD 0x1000 /* Old has snapshots */ +#define NEXT3_FEATURE_RO_COMPAT_IS_SNAPSHOT_OLD 0x2000 /* Old is snapshot */ +#define NEXT3_FEATURE_RO_COMPAT_FIX_SNAPSHOT_OLD 0x4000 /* Old fix snapshot */ +#define NEXT3_FEATURE_RO_COMPAT_FIX_EXCLUDE_OLD 0x8000 /* Old fix exclude */ #define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 @@ -650,6 +707,7 @@ struct ext2_super_block { #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE) #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\ EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ EXT2_FEATURE_RO_COMPAT_BTREE_DIR) diff -Nuarp a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h --- a/lib/ext2fs/ext2fs.h 2009-08-23 05:44:35.000000000 +0300 +++ b/lib/ext2fs/ext2fs.h 2010-07-26 09:47:16.680445479 +0300 @@ -172,6 +172,9 @@ typedef struct ext2_file *ext2_file_t; #define EXT2_FLAG_EXCLUSIVE 0x4000 #define EXT2_FLAG_SOFTSUPP_FEATURES 0x8000 #define EXT2_FLAG_NOFREE_ON_ERROR 0x10000 +#define EXT2_FLAG_64BITS 0x20000 +#define EXT2_FLAG_PRINT_PROGRESS 0x40000 +#define EXT2_FLAG_EXCLUDE_DIRTY 0x100000 /* * Special flag in the ext2 inode i_flag field that means that this is @@ -186,6 +189,8 @@ typedef struct ext2_file *ext2_file_t; */ #define EXT2_MKJOURNAL_V1_SUPER 0x0000001 +#define opaque_ext2_group_desc ext2_group_desc + struct struct_ext2_filsys { errcode_t magic; io_channel io; @@ -196,10 +201,13 @@ struct struct_ext2_filsys { int fragsize; dgrp_t group_desc_count; unsigned long desc_blocks; - struct ext2_group_desc * group_desc; + struct opaque_ext2_group_desc * group_desc; + __u32 * exclude_blks; int inode_blocks_per_group; ext2fs_inode_bitmap inode_map; ext2fs_block_bitmap block_map; + ext2fs_block_bitmap exclude_map; + /* XXX FIXME-64: not 64-bit safe, but not used? */ errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino); errcode_t (*write_bitmaps)(ext2_filsys fs); @@ -504,6 +512,8 @@ typedef struct ext2_icount *ext2_icount_ #define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\ EXT2_FEATURE_COMPAT_IMAGIC_INODES|\ EXT3_FEATURE_COMPAT_HAS_JOURNAL|\ + EXT2_FEATURE_COMPAT_EXCLUDE_INODE|\ + NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD|\ EXT2_FEATURE_COMPAT_RESIZE_INODE|\ EXT2_FEATURE_COMPAT_DIR_INDEX|\ EXT2_FEATURE_COMPAT_EXT_ATTR) @@ -534,6 +544,7 @@ typedef struct ext2_icount *ext2_icount_ #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\ EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\ + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\ EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\ EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\ EXT4_FEATURE_RO_COMPAT_GDT_CSUM) @@ -644,8 +655,10 @@ extern errcode_t ext2fs_copy_bitmap(ext2 ext2fs_generic_bitmap *dest); extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs); extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs); +extern errcode_t ext2fs_write_exclude_bitmap (ext2_filsys fs); extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs); extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs); +extern errcode_t ext2fs_read_exclude_bitmap (ext2_filsys fs); extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, const char *descr, ext2fs_block_bitmap *ret); @@ -1070,6 +1083,8 @@ extern errcode_t ext2fs_add_journal_devi extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags); extern int ext2fs_default_journal_size(__u64 blocks); +extern int ext2fs_big_journal_size(int factor, __u64 blocks); +extern int ext2fs_check_journal_size(ext2_filsys fs); /* openfs.c */ extern errcode_t ext2fs_open(const char *name, int flags, int superblock, @@ -1114,6 +1129,13 @@ extern errcode_t ext2fs_read_bb_FILE(ext /* res_gdt.c */ extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs); +extern errcode_t ext2fs_create_exclude_inode(ext2_filsys fs, int flags); + +/* exclude inode creation flags */ +#define EXCLUDE_READONLY 0 /* only read exclude bitmap blocks */ +#define EXCLUDE_ALLOC 1 /* allocate missing exclude bitmap blocks */ +#define EXCLUDE_RESET 2 /* reset exclude bitmap blocks to zero */ +#define EXCLUDE_CREATE 3 /* alloc and/or reset exclude bitmap blocks */ /* swapfs.c */ extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, @@ -1306,6 +1328,14 @@ _INLINE_ void ext2fs_mark_bb_dirty(ext2_ } /* + * Mark the exclude bitmap as dirty + */ +_INLINE_ void ext2fs_mark_exclude_dirty(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_EXCLUDE_DIRTY | EXT2_FLAG_CHANGED; +} + +/* * Check to see if a filesystem's inode bitmap is dirty */ _INLINE_ int ext2fs_test_ib_dirty(ext2_filsys fs) @@ -1322,6 +1352,14 @@ _INLINE_ int ext2fs_test_bb_dirty(ext2_f } /* + * Check to see if a filesystem's exclude bitmap is dirty + */ +_INLINE_ int ext2fs_test_exclude_dirty(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_EXCLUDE_DIRTY); +} + +/* * Return the group # of a block */ _INLINE_ int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk) diff -Nuarp a/lib/ext2fs/i_block.c b/lib/ext2fs/i_block.c --- a/lib/ext2fs/i_block.c 2009-08-13 04:39:57.000000000 +0300 +++ b/lib/ext2fs/i_block.c 2010-07-26 09:47:16.650443902 +0300 @@ -21,6 +21,7 @@ #if HAVE_SYS_TYPES_H #include <sys/types.h> #endif +#include <errno.h> #include "ext2_fs.h" #include "ext2fs.h" @@ -28,18 +29,24 @@ errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode, blk64_t num_blocks) { - unsigned long long b; + unsigned long long b = inode->i_blocks; - if ((fs->super->s_feature_ro_compat & - EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && - (inode->i_flags & EXT4_HUGE_FILE_FL)) { - b = inode->i_blocks + - (((long long) inode->osd2.linux2.l_i_blocks_hi) << 32); - b += num_blocks; - inode->i_blocks = b & 0xFFFFFFFF; + if (!((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + /* snapshot file always supports the 'huge_file' flag */ + (inode->i_flags & EXT4_SNAPFILE_FL)) || + !(inode->i_flags & EXT4_HUGE_FILE_FL)) + num_blocks *= fs->blocksize / 512; + + b += num_blocks; + + if (fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) { + b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32; inode->osd2.linux2.l_i_blocks_hi = b >> 32; - } else - inode->i_blocks += (fs->blocksize / 512) * num_blocks; + } else if (b > 0xFFFFFFFF) + return EOVERFLOW; + inode->i_blocks = b & 0xFFFFFFFF; return 0; } @@ -47,29 +54,42 @@ errcode_t ext2fs_iblk_add_blocks(ext2_fi errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode, blk64_t num_blocks) { - unsigned long long b; + unsigned long long b = inode->i_blocks; - if ((fs->super->s_feature_ro_compat & - EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && - (inode->i_flags & EXT4_HUGE_FILE_FL)) { - b = inode->i_blocks + - (((long long) inode->osd2.linux2.l_i_blocks_hi) << 32); - b -= num_blocks; - inode->i_blocks = b & 0xFFFFFFFF; + if (!((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + /* snapshot file always supports the 'huge_file' flag */ + (inode->i_flags & EXT4_SNAPFILE_FL)) || + !(inode->i_flags & EXT4_HUGE_FILE_FL)) + num_blocks *= fs->blocksize / 512; + + if (num_blocks > b) + return EOVERFLOW; + + b -= num_blocks; + + if (fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) { + b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32; inode->osd2.linux2.l_i_blocks_hi = b >> 32; - } else - inode->i_blocks -= (fs->blocksize / 512) * num_blocks; + } + inode->i_blocks = b & 0xFFFFFFFF; return 0; } errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b) { - if ((fs->super->s_feature_ro_compat & - EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && - (inode->i_flags & EXT4_HUGE_FILE_FL)) { - inode->i_blocks = b & 0xFFFFFFFF; + if (!((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + /* snapshot file always supports the 'huge_file' flag */ + (inode->i_flags & EXT4_SNAPFILE_FL)) || + !(inode->i_flags & EXT4_HUGE_FILE_FL)) + b *= fs->blocksize / 512; + + inode->i_blocks = b & 0xFFFFFFFF; + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) inode->osd2.linux2.l_i_blocks_hi = b >> 32; - } else - inode->i_blocks = (fs->blocksize / 512) * b; + else if (b >> 32) + return EOVERFLOW; return 0; } diff -Nuarp a/lib/ext2fs/mkjournal.c b/lib/ext2fs/mkjournal.c --- a/lib/ext2fs/mkjournal.c 2009-08-23 05:44:35.000000000 +0300 +++ b/lib/ext2fs/mkjournal.c 2010-07-26 09:47:16.640447884 +0300 @@ -388,6 +388,47 @@ int ext2fs_default_journal_size(__u64 bl return 32768; } +/* + * Big journal is up to X times bigger than the default journal + * to accomodate snapshot COW credits in transactions. + * journal size is restricted to 1/32 of the filesystem size + */ +int ext2fs_big_journal_size(int factor, __u64 blocks) +{ + int mega_blocks = blocks >> 20; + if (!mega_blocks) + return ext2fs_default_journal_size(blocks); + + if (mega_blocks < factor) + /* 32K/1M = 1/32 of filesystem size */ + return EXT3_DEF_JOURNAL_BLOCKS*mega_blocks; + + /* X times bigger than the default journal */ + return EXT3_DEF_JOURNAL_BLOCKS*factor; +} + +/* + * Return the number of blocks in the journal inode + */ +int ext2fs_check_journal_size(ext2_filsys fs) +{ + struct ext2_inode j_inode; + int j_blocks; + + if (!(fs->super->s_feature_compat & + EXT3_FEATURE_COMPAT_HAS_JOURNAL) || + !fs->super->s_journal_inum) + return 0; + + if (ext2fs_read_inode(fs, fs->super->s_journal_inum, &j_inode)) + return -1; + + /* read journal inode size */ + j_blocks = j_inode.i_size >> EXT2_BLOCK_SIZE_BITS(fs->super); + + return j_blocks; +} + /* * This function adds a journal device to a filesystem */ diff -Nuarp a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c --- a/lib/ext2fs/openfs.c 2009-08-23 05:44:35.000000000 +0300 +++ b/lib/ext2fs/openfs.c 2010-07-26 09:47:16.670444963 +0300 @@ -199,6 +199,51 @@ errcode_t ext2fs_open2(const char *name, goto cleanup; } + /* Migrate super from old to new Next3 on-disk format */ + if ((fs->super->s_feature_ro_compat & + NEXT3_FEATURE_RO_COMPAT_HAS_SNAPSHOT_OLD) && + !(fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT)) { + struct ext2_super_block *sb = fs->super; + + /* Copy snapshot fields to new positions */ + sb->s_snapshot_inum = sb->s_snapshot_inum_old; + sb->s_snapshot_id = sb->s_snapshot_id_old; + sb->s_snapshot_r_blocks_count = sb->s_snapshot_r_blocks_old; + sb->s_snapshot_list = sb->s_snapshot_list_old; + /* Clear old snapshot fields */ + sb->s_snapshot_inum_old = 0; + sb->s_snapshot_id_old = 0; + sb->s_snapshot_r_blocks_old = 0; + sb->s_snapshot_list_old = 0; + /* Copy snapshot flags to new positions */ + fs->super->s_feature_ro_compat |= + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT; + if (fs->super->s_feature_compat & + NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD) + fs->super->s_feature_compat |= + EXT2_FEATURE_COMPAT_EXCLUDE_INODE; + if (fs->super->s_feature_ro_compat & + NEXT3_FEATURE_RO_COMPAT_FIX_SNAPSHOT_OLD) + fs->super->s_flags |= EXT2_FLAGS_FIX_SNAPSHOT; + if (fs->super->s_feature_ro_compat & + NEXT3_FEATURE_RO_COMPAT_FIX_EXCLUDE_OLD) + fs->super->s_flags |= EXT2_FLAGS_FIX_EXCLUDE; + /* Clear old snapshot flags */ + fs->super->s_feature_ro_compat &= + ~(NEXT3_FEATURE_RO_COMPAT_HAS_SNAPSHOT_OLD| + NEXT3_FEATURE_RO_COMPAT_IS_SNAPSHOT_OLD| + NEXT3_FEATURE_RO_COMPAT_FIX_SNAPSHOT_OLD| + NEXT3_FEATURE_RO_COMPAT_FIX_EXCLUDE_OLD); + /* Clear deprecated big journal flag */ + fs->super->s_feature_compat &= + ~NEXT3_FEATURE_COMPAT_BIG_JOURNAL_OLD; + /* Keep old exclude inode flag b/c inode was not moved yet */ + if (flags & EXT2_FLAG_RW) + /* in read-only mode just convert the in-memory copy */ + ext2fs_mark_super_dirty(fs); + } + /* * Check for feature set incompatibility */ diff -Nuarp a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c --- a/lib/ext2fs/res_gdt.c 2009-08-23 05:39:05.000000000 +0300 +++ b/lib/ext2fs/res_gdt.c 2010-07-26 09:47:16.630443266 +0300 @@ -217,3 +217,243 @@ out_free: return retval; } +#define ext2fs_group_desc(fs, gdp, grp) (gdp)+(grp) + +/* + * ext2fs_create_exclude_inode(): + * the exclude inode owns all the exclude bitmap blocks (one per block group) + * the exclude bitmap blocks are double indirectly linked to the exclude inode + * the exclude bitmap allocation goal is the first block of the block group + * exclude inode creation @flags: + * EXCLUDE_ALLOC (1) - allocate missing exclude bitmap blocks + * EXCLUDE_RESET (2) - reset exclude bitmap to zero + */ +errcode_t ext2fs_create_exclude_inode(ext2_filsys fs, int flags) +{ + errcode_t retval, retval2; + struct ext2_super_block *sb; + struct ext2_inode inode; + __u32 *dindir_buf, *indir_buf, *data_buf; + unsigned long long apb, inode_size; + blk_t dindir_blk, indir_blk, data_blk; + int gdt_dirty = 0, dindir_dirty = 0, inode_dirty = 0; + int indir_dirty = 0, data_dirty = 0; + int dindir_off, indir_off, grp, i, max_groups; + int create = flags & EXCLUDE_ALLOC; + int reset = flags & EXCLUDE_RESET; + + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + sb = fs->super; + + retval = ext2fs_get_array(3, fs->blocksize, &dindir_buf); + if (retval) + goto out_free; + indir_buf = (__u32 *)((char *)dindir_buf + 1*fs->blocksize); + data_buf = (__u32 *)((char *)dindir_buf + 2*fs->blocksize); + + /* Migrate from old to new Next3 on-disk format */ + if (fs->super->s_feature_compat & + NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD) { + /* Move exclude inode from old to new position */ + retval = ext2fs_read_inode(fs, EXT2_EXCLUDE_INO_OLD, &inode); + if (!retval) { + retval = ext2fs_write_inode(fs, EXT2_EXCLUDE_INO, + &inode); + if (retval) + goto out_free; + memset(&inode, 0, sizeof(inode)); + retval = ext2fs_write_inode(fs, EXT2_EXCLUDE_INO_OLD, + &inode); + if (retval) + goto out_free; + /* Clear old exclude inode flag */ + fs->super->s_feature_compat &= + ~NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD; + ext2fs_mark_super_dirty(fs); + } + } + + retval = ext2fs_read_inode(fs, EXT2_EXCLUDE_INO, &inode); + if (retval) + goto out_free; + + if (fs->exclude_blks) + ext2fs_free_mem(&fs->exclude_blks); + retval = ext2fs_get_array(fs->group_desc_count, fs->blocksize, + &fs->exclude_blks); + if (retval) + goto out_free; + memset(fs->exclude_blks, 0, fs->group_desc_count*fs->blocksize); + +#ifdef EXCLUDE_INO_PROGRESS + printf("Reserving exclude bitmap blocks: "); +#endif + + apb = EXT2_ADDR_PER_BLOCK(sb); + if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) { +#ifdef EXCLUDE_INO_DEBUG + printf("reading exclude inode dindir %u\n", dindir_blk); +#endif + retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf); + if (retval) + goto out_free; + } else if (create) { + blk_t goal = sb->s_first_data_block + fs->desc_blocks + + sb->s_reserved_gdt_blocks + 2 + + fs->inode_blocks_per_group; + + retval = ext2fs_alloc_block(fs, goal, (char *)dindir_buf, &dindir_blk); + if (retval) + goto out_free; + inode.i_mode = LINUX_S_IFREG | 0600; + inode.i_links_count = 1; + inode.i_block[EXT2_DIND_BLOCK] = dindir_blk; + ext2fs_iblk_set(fs, &inode, 1); +#ifdef EXCLUDE_INO_DEBUG + printf("allocated exclude inode dindir %u\n", dindir_blk); +#endif + dindir_dirty = inode_dirty = 1; + inode.i_ctime = fs->now ? fs->now : time(0); + } + + /* + * init exclude_blks array for all existing block groups + * and allocate indirect blocks for all reserved block groups + */ + max_groups = fs->desc_blocks + sb->s_reserved_gdt_blocks; + max_groups *= EXT2_DESC_PER_BLOCK(sb); + for (grp = 0; grp < max_groups; grp++) { + struct ext2_group_desc *gd = + ext2fs_group_desc(fs, fs->group_desc, grp); + + dindir_off = grp/apb; + indir_off = grp%apb; + if (indir_off == 0) { + /* flush current indirect block */ + if (indir_dirty) { + retval = ext2fs_write_ind_block(fs, indir_blk, indir_buf); + if (retval) + goto out_dindir; + indir_dirty = 0; + } + /* read/alloc next indirect block */ + if ((indir_blk = dindir_buf[dindir_off])) { +#ifdef EXCLUDE_INO_DEBUG + printf("reading exclude inode indir %u\n", indir_blk); +#endif + retval = ext2fs_read_ind_block(fs, indir_blk, indir_buf); + if (retval) + goto out_dindir; + } else if (create) { + retval = ext2fs_alloc_block(fs, dindir_blk, (char *)indir_buf, &indir_blk); + if (retval) + goto out_dindir; + dindir_buf[dindir_off] = indir_blk; + ext2fs_iblk_add_blocks(fs, &inode, 1); +#ifdef EXCLUDE_INO_DEBUG + printf("allocated exclude inode indir %u\n", indir_blk); +#endif + dindir_dirty = inode_dirty = 1; + } + } + + if (grp >= fs->group_desc_count) + continue; + /* read/alloc exclude bitmap block */ + data_blk = indir_buf[indir_off]; + if (!data_blk && create) { + /* allocate exclude bitmap block */ + retval = ext2fs_alloc_block(fs, gd->bg_block_bitmap, + (char *)data_buf, &data_blk); + if (retval) + goto out_dindir; + indir_buf[indir_off] = data_blk; + ext2fs_iblk_add_blocks(fs, &inode, 1); +#ifdef EXCLUDE_INO_DEBUG + printf("allocated exclude bitmap block %u\n", data_blk); +#endif + indir_dirty = inode_dirty = 1; + } else if (data_blk && reset) { + /* reset exclude bitmap block */ +#ifdef EXCLUDE_INO_DEBUG + printf("reading exclude bitmap block %u\n", data_blk); +#endif + retval = io_channel_read_blk(fs->io, data_blk, 1, + data_buf); + if (retval) + goto out_dindir; + /* zero data block */ + for (i = 0; i < apb; i++) { + if (!data_buf[i]) + continue; + data_buf[i] = 0; + data_dirty = 1; + } + if (data_dirty) { + retval = io_channel_write_blk(fs->io, data_blk, + 1, data_buf); + if (retval) + goto out_dindir; + data_dirty = 0; + } + } + fs->exclude_blks[grp] = data_blk; + /* reset old exclude/cow bitmap cache to zero */ + if (gd->bg_exclude_bitmap_old || gd->bg_cow_bitmap_old) { + gd->bg_exclude_bitmap_old = 0; + gd->bg_cow_bitmap_old = 0; + gdt_dirty = 1; + } +#ifdef EXCLUDE_INO_PROGRESS + printf("\b\b\b\b\b\b\b\b\b\b\b%5d/%5d", grp, + fs->group_desc_count); +#endif + } +#ifdef EXCLUDE_INO_PROGRESS + printf("\b\b\b\b\b\b\b\b\b\b\bdone \n"); +#endif + + /* exclude bitmap was reset to zero - clear fix_exclude flag */ + if (sb->s_flags & EXT2_FLAGS_FIX_EXCLUDE) { + sb->s_flags &= ~EXT2_FLAGS_FIX_EXCLUDE; + ext2fs_mark_super_dirty(fs); + } + +out_dindir: + if (indir_dirty) { + retval2 = ext2fs_write_ind_block(fs, indir_blk, indir_buf); + if (!retval) + retval = retval2; + } + if (dindir_dirty) { + retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf); + if (!retval) + retval = retval2; + } +out_inode: + if (inode_dirty) { + inode_size = fs->group_desc_count + apb + EXT2_NDIR_BLOCKS; + inode_size *= fs->blocksize; + inode.i_size = inode_size & 0xFFFFFFFF; + inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0); + retval2 = ext2fs_write_new_inode(fs, EXT2_EXCLUDE_INO, &inode); + if (!retval) + retval = retval2; + /* need to write out block bitmaps and group descriptors */ + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + } + if (gdt_dirty) { + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + ext2fs_mark_super_dirty(fs); + } +#ifdef EXCLUDE_INO_DEBUG + printf("inode.i_blocks = %u, i_size = %u\n", + inode.i_blocks, inode.i_size); +#endif +out_free: + ext2fs_free_mem(&dindir_buf); + return retval; +} + diff -Nuarp a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c --- a/lib/ext2fs/rw_bitmaps.c 2009-08-23 05:44:35.000000000 +0300 +++ b/lib/ext2fs/rw_bitmaps.c 2010-07-26 09:47:16.660444109 +0300 @@ -25,19 +25,37 @@ #include "ext2_fs.h" #include "ext2fs.h" + +#define blk64_t blk_t +#define __u64 __u32 +#define io_channel_read_blk64 io_channel_read_blk +#define io_channel_write_blk64 io_channel_write_blk +#define ext2fs_get_block_bitmap_range2 ext2fs_get_block_bitmap_range +#define ext2fs_set_block_bitmap_range2 ext2fs_set_block_bitmap_range +#define ext2fs_block_bitmap_loc(fs, group) \ + fs->group_desc[group].bg_block_bitmap +#define ext2fs_inode_bitmap_loc(fs, group) \ + fs->group_desc[group].bg_inode_bitmap +#define ext2fs_bg_flags_test(fs, group, bg_flag) \ + fs->group_desc[group].bg_flags & bg_flag +#define ext2fs_blocks_count(sb) sb->s_blocks_count + +#include "ext2_fs.h" +#include "ext2fs.h" #include "e2image.h" -static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) +static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block, + int do_exclude) { dgrp_t i; unsigned int j; int block_nbytes, inode_nbytes; unsigned int nbits; errcode_t retval; - char *block_buf, *inode_buf; + char *block_buf, *inode_buf, *exclude_buf; int csum_flag = 0; - blk_t blk; - blk_t blk_itr = fs->super->s_first_data_block; + blk64_t blk; + blk64_t blk_itr = fs->super->s_first_data_block; ext2_ino_t ino_itr = 1; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); @@ -45,6 +63,17 @@ static errcode_t write_bitmaps(ext2_fils if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; + if (!EXT2_HAS_COMPAT_FEATURE(fs->super, + EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) + do_exclude = 0; + + if (do_exclude && !fs->exclude_blks) { + /* read exclude_blks from exclude inode */ + retval = ext2fs_create_exclude_inode(fs, 0); + if (retval) + return retval; + } + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) csum_flag = 1; @@ -57,6 +86,13 @@ static errcode_t write_bitmaps(ext2_fils return retval; memset(block_buf, 0xff, fs->blocksize); } + if (do_exclude) { + block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + retval = ext2fs_get_mem(fs->blocksize, &exclude_buf); + if (retval) + return retval; + memset(exclude_buf, 0xff, fs->blocksize); + } if (do_inode) { inode_nbytes = (size_t) ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8); @@ -67,31 +103,44 @@ static errcode_t write_bitmaps(ext2_fils } for (i = 0; i < fs->group_desc_count; i++) { - if (!do_block) + if (!do_block && !do_exclude) goto skip_block_bitmap; - if (csum_flag && fs->group_desc[i].bg_flags & - EXT2_BG_BLOCK_UNINIT) + if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) + ) goto skip_this_block_bitmap; - retval = ext2fs_get_block_bitmap_range(fs->block_map, - blk_itr, block_nbytes << 3, block_buf); + if (do_block) + retval = ext2fs_get_block_bitmap_range2(fs->block_map, + blk_itr, block_nbytes << 3, block_buf); if (retval) return retval; - if (i == fs->group_desc_count - 1) { + if (do_exclude) + retval = ext2fs_get_block_bitmap_range2(fs->exclude_map, + blk_itr, block_nbytes << 3, exclude_buf); + if (retval) + return retval; + + if (do_block && i == fs->group_desc_count - 1) { /* Force bitmap padding for the last group */ - nbits = ((fs->super->s_blocks_count - - fs->super->s_first_data_block) - % EXT2_BLOCKS_PER_GROUP(fs->super)); + nbits = ((ext2fs_blocks_count(fs->super) + - (__u64) fs->super->s_first_data_block) + % (__u64) EXT2_BLOCKS_PER_GROUP(fs->super)); if (nbits) for (j = nbits; j < fs->blocksize * 8; j++) ext2fs_set_bit(j, block_buf); } - blk = fs->group_desc[i].bg_block_bitmap; - if (blk) { - retval = io_channel_write_blk(fs->io, blk, 1, - block_buf); + blk = ext2fs_block_bitmap_loc(fs, i); + if (do_block && blk) { + retval = io_channel_write_blk64(fs->io, blk, 1, + block_buf); + if (retval) + return EXT2_ET_BLOCK_BITMAP_WRITE; + } + if (do_exclude && (blk = fs->exclude_blks[i])) { + retval = io_channel_write_blk64(fs->io, blk, 1, + exclude_buf); if (retval) return EXT2_ET_BLOCK_BITMAP_WRITE; } @@ -102,8 +151,8 @@ static errcode_t write_bitmaps(ext2_fils if (!do_inode) continue; - if (csum_flag && fs->group_desc[i].bg_flags & - EXT2_BG_INODE_UNINIT) + if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) + ) goto skip_this_inode_bitmap; retval = ext2fs_get_inode_bitmap_range(fs->inode_map, @@ -111,9 +160,9 @@ static errcode_t write_bitmaps(ext2_fils if (retval) return retval; - blk = fs->group_desc[i].bg_inode_bitmap; + blk = ext2fs_inode_bitmap_loc(fs, i); if (blk) { - retval = io_channel_write_blk(fs->io, blk, 1, + retval = io_channel_write_blk64(fs->io, blk, 1, inode_buf); if (retval) return EXT2_ET_INODE_BITMAP_WRITE; @@ -133,10 +182,11 @@ static errcode_t write_bitmaps(ext2_fils return 0; } -static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) +static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block, + int do_exclude) { dgrp_t i; - char *block_bitmap = 0, *inode_bitmap = 0; + char *block_bitmap = 0, *inode_bitmap = 0, *exclude_bitmap = 0; char *buf; errcode_t retval; int block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; @@ -144,9 +194,9 @@ static errcode_t read_bitmaps(ext2_filsy int csum_flag = 0; int do_image = fs->flags & EXT2_FLAG_IMAGE_FILE; unsigned int cnt; - blk_t blk; - blk_t blk_itr = fs->super->s_first_data_block; - blk_t blk_cnt; + blk64_t blk; + blk64_t blk_itr = fs->super->s_first_data_block; + blk64_t blk_cnt; ext2_ino_t ino_itr = 1; ext2_ino_t ino_cnt; @@ -154,6 +204,17 @@ static errcode_t read_bitmaps(ext2_filsy fs->write_bitmaps = ext2fs_write_bitmaps; + if (!EXT2_HAS_COMPAT_FEATURE(fs->super, + EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) + do_exclude = 0; + + if (do_exclude && !fs->exclude_blks) { + /* read exclude_blks from exclude inode */ + retval = ext2fs_create_exclude_inode(fs, 0); + if (retval) + return retval; + } + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) csum_flag = 1; @@ -173,7 +234,21 @@ static errcode_t read_bitmaps(ext2_filsy (unsigned) block_nbytes, &block_bitmap); if (retval) goto cleanup; - } else + } + if (do_exclude) { + if (fs->exclude_map) + ext2fs_free_block_bitmap(fs->exclude_map); + strcpy(buf, "exclude bitmap for "); + strcat(buf, fs->device_name); + retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->exclude_map); + if (retval) + goto cleanup; + retval = ext2fs_get_mem(do_image ? fs->blocksize : + (unsigned) block_nbytes, &exclude_bitmap); + if (retval) + goto cleanup; + } + if (!do_block && !do_exclude) block_nbytes = 0; if (do_inode) { if (fs->inode_map) @@ -195,7 +270,7 @@ static errcode_t read_bitmaps(ext2_filsy blk = (fs->image_header->offset_inodemap / fs->blocksize); ino_cnt = fs->super->s_inodes_count; while (inode_nbytes > 0) { - retval = io_channel_read_blk(fs->image_io, blk++, + retval = io_channel_read_blk64(fs->image_io, blk++, 1, inode_bitmap); if (retval) goto cleanup; @@ -212,17 +287,22 @@ static errcode_t read_bitmaps(ext2_filsy } blk = (fs->image_header->offset_blockmap / fs->blocksize); - blk_cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * + blk_cnt = (blk64_t)EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count; while (block_nbytes > 0) { - retval = io_channel_read_blk(fs->image_io, blk++, + if (do_exclude) { + retval = EXT2_ET_BLOCK_BITMAP_READ; + goto cleanup; + } + + retval = io_channel_read_blk64(fs->image_io, blk++, 1, block_bitmap); if (retval) goto cleanup; cnt = fs->blocksize << 3; if (cnt > blk_cnt) cnt = blk_cnt; - retval = ext2fs_set_block_bitmap_range(fs->block_map, + retval = ext2fs_set_block_bitmap_range2(fs->block_map, blk_itr, cnt, block_bitmap); if (retval) goto cleanup; @@ -235,13 +315,13 @@ static errcode_t read_bitmaps(ext2_filsy for (i = 0; i < fs->group_desc_count; i++) { if (block_bitmap) { - blk = fs->group_desc[i].bg_block_bitmap; - if (csum_flag && fs->group_desc[i].bg_flags & - EXT2_BG_BLOCK_UNINIT && + blk = ext2fs_block_bitmap_loc(fs, i); + if (csum_flag && + ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) && ext2fs_group_desc_csum_verify(fs, i)) blk = 0; if (blk) { - retval = io_channel_read_blk(fs->io, blk, + retval = io_channel_read_blk64(fs->io, blk, -block_nbytes, block_bitmap); if (retval) { retval = EXT2_ET_BLOCK_BITMAP_READ; @@ -250,20 +330,42 @@ static errcode_t read_bitmaps(ext2_filsy } else memset(block_bitmap, 0, block_nbytes); cnt = block_nbytes << 3; - retval = ext2fs_set_block_bitmap_range(fs->block_map, + retval = ext2fs_set_block_bitmap_range2(fs->block_map, blk_itr, cnt, block_bitmap); if (retval) goto cleanup; - blk_itr += block_nbytes << 3; } + if (exclude_bitmap) { + blk = fs->exclude_blks[i]; + if (csum_flag && + ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) && + ext2fs_group_desc_csum_verify(fs, i)) + blk = 0; + if (blk) { + retval = io_channel_read_blk64(fs->io, blk, + -block_nbytes, exclude_bitmap); + if (retval) { + retval = EXT2_ET_BLOCK_BITMAP_READ; + goto cleanup; + } + } else + memset(exclude_bitmap, 0, block_nbytes); + cnt = block_nbytes << 3; + retval = ext2fs_set_block_bitmap_range2(fs->exclude_map, + blk_itr, cnt, exclude_bitmap); + if (retval) + goto cleanup; + } + if (block_nbytes) + blk_itr += block_nbytes << 3; if (inode_bitmap) { - blk = fs->group_desc[i].bg_inode_bitmap; - if (csum_flag && fs->group_desc[i].bg_flags & - EXT2_BG_INODE_UNINIT && + blk = ext2fs_inode_bitmap_loc(fs, i); + if (csum_flag && + ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) && ext2fs_group_desc_csum_verify(fs, i)) blk = 0; if (blk) { - retval = io_channel_read_blk(fs->io, blk, + retval = io_channel_read_blk64(fs->io, blk, -inode_nbytes, inode_bitmap); if (retval) { retval = EXT2_ET_INODE_BITMAP_READ; @@ -284,6 +386,8 @@ success_cleanup: ext2fs_free_mem(&inode_bitmap); if (block_bitmap) ext2fs_free_mem(&block_bitmap); + if (exclude_bitmap) + ext2fs_free_mem(&exclude_bitmap); return 0; cleanup: @@ -299,6 +403,8 @@ cleanup: ext2fs_free_mem(&inode_bitmap); if (block_bitmap) ext2fs_free_mem(&block_bitmap); + if (exclude_bitmap) + ext2fs_free_mem(&exclude_bitmap); if (buf) ext2fs_free_mem(&buf); return retval; @@ -306,39 +412,50 @@ cleanup: errcode_t ext2fs_read_inode_bitmap(ext2_filsys fs) { - return read_bitmaps(fs, 1, 0); + return read_bitmaps(fs, 1, 0, 0); } errcode_t ext2fs_read_block_bitmap(ext2_filsys fs) { - return read_bitmaps(fs, 0, 1); + return read_bitmaps(fs, 0, 1, 0); +} + +errcode_t ext2fs_read_exclude_bitmap (ext2_filsys fs) +{ + return read_bitmaps(fs, 0, 0, 1); } errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs) { - return write_bitmaps(fs, 1, 0); + return write_bitmaps(fs, 1, 0, 0); } errcode_t ext2fs_write_block_bitmap (ext2_filsys fs) { - return write_bitmaps(fs, 0, 1); + return write_bitmaps(fs, 0, 1, 0); +} + +errcode_t ext2fs_write_exclude_bitmap (ext2_filsys fs) +{ + return write_bitmaps(fs, 0, 0, 1); } errcode_t ext2fs_read_bitmaps(ext2_filsys fs) { - if (fs->inode_map && fs->block_map) + if (fs->inode_map && fs->block_map && fs->exclude_map) return 0; - return read_bitmaps(fs, !fs->inode_map, !fs->block_map); + return read_bitmaps(fs, !fs->inode_map, !fs->block_map, !fs->exclude_map); } errcode_t ext2fs_write_bitmaps(ext2_filsys fs) { int do_inode = fs->inode_map && ext2fs_test_ib_dirty(fs); int do_block = fs->block_map && ext2fs_test_bb_dirty(fs); + int do_exclude = fs->exclude_map && ext2fs_test_exclude_dirty(fs); - if (!do_inode && !do_block) + if (!do_inode && !do_block && !do_exclude) return 0; - return write_bitmaps(fs, do_inode, do_block); + return write_bitmaps(fs, do_inode, do_block, do_exclude); } diff -Nuarp a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c --- a/lib/ext2fs/swapfs.c 2009-08-23 05:39:05.000000000 +0300 +++ b/lib/ext2fs/swapfs.c 2010-07-26 09:47:16.640447884 +0300 @@ -71,6 +71,17 @@ void ext2fs_swap_super(struct ext2_super sb->s_want_extra_isize = ext2fs_swab16(sb->s_want_extra_isize); sb->s_flags = ext2fs_swab32(sb->s_flags); sb->s_kbytes_written = ext2fs_swab64(sb->s_kbytes_written); + sb->s_snapshot_inum = ext2fs_swab32(sb->s_snapshot_inum); + sb->s_snapshot_id = ext2fs_swab32(s_snapshot_id); + sb->s_snapshot_r_blocks_count = + ext2fs_swab64(sb->s_snapshot_r_blocks_count); + sb->s_snapshot_list = ext2fs_swab32(sb->s_snapshot_list); + sb->s_snapshot_inum_old = ext2fs_swab32(sb->s_snapshot_inum_old); + sb->s_snapshot_id_old = ext2fs_swab32(sb->s_snapshot_id_old); + sb->s_snapshot_r_blocks_old = + ext2fs_swab32(sb->s_snapshot_r_blocks_old); + sb->s_snapshot_list_old = ext2fs_swab32(sb->s_snapshot_list_old); + for (i=0; i < 4; i++) sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]); for (i=0; i < 17; i++) diff -Nuarp a/misc/chattr.c b/misc/chattr.c --- a/misc/chattr.c 2009-08-13 04:39:57.000000000 +0300 +++ b/misc/chattr.c 2010-07-26 09:47:16.770445154 +0300 @@ -56,6 +56,8 @@ static const char * program_name = "chattr"; +static int chsnap; + static int add; static int rem; static int set; @@ -81,8 +83,12 @@ static unsigned long sf; static void usage(void) { + fprintf(stderr, chsnap ? + _("Usage: %s [-+=let] snapshot files...\n") : + _("Usage: %s [-RVf] [-+=AacDdeijsSux] [-v version] files...\n"), + program_name); fprintf(stderr, - _("Usage: %s [-RVf] [-+=AacDdeijsSu] [-v version] files...\n"), + _("Usage: %s -X [-+=Snt] snapshot files...\n"), program_name); exit(1); } @@ -92,7 +98,7 @@ struct flags_char { char optchar; }; -static const struct flags_char flags_array[] = { +static const struct flags_char ext2_flags_array[] = { { EXT2_NOATIME_FL, 'A' }, { EXT2_SYNC_FL, 'S' }, { EXT2_DIRSYNC_FL, 'D' }, @@ -106,6 +112,25 @@ static const struct flags_char flags_arr { EXT2_UNRM_FL, 'u' }, { EXT2_NOTAIL_FL, 't' }, { EXT2_TOPDIR_FL, 'T' }, + { EXT4_SNAPFILE_FL, 'x' }, + { 0, 0 } +}; + +static const struct flags_char *flags_array = ext2_flags_array; + +/* Traditional snapshot flags */ +static struct flags_char snapshot_flags_array[] = { + { NEXT3_SNAPFILE_LIST_FL, 'l' }, + { NEXT3_SNAPFILE_ENABLED_FL, 'e' }, + { NEXT3_SNAPFILE_TAGGED_FL, 't' }, + { 0, 0 } +}; + +/* Cool 'Snapshot' flags */ +static struct flags_char snapshot_X_flags_array[] = { + { NEXT3_SNAPFILE_LIST_FL, 'S' }, + { NEXT3_SNAPFILE_ENABLED_FL, 'n' }, + { NEXT3_SNAPFILE_TAGGED_FL, 't' }, { 0, 0 } }; @@ -131,6 +156,10 @@ static int decode_arg (int * i, int argc { case '-': for (p = &argv[*i][1]; *p; p++) { + if (*p == 'X') { + flags_array = snapshot_X_flags_array; + continue; + } if (*p == 'R') { recursive = 1; continue; @@ -303,6 +332,11 @@ int main (int argc, char ** argv) #endif if (argc && *argv) program_name = *argv; + i = strlen(program_name); + if (i >= 6 && !strcmp(program_name + i - 6, "chsnap")) { + flags_array = snapshot_flags_array; + chsnap = 1; + } i = 1; while (i < argc && !end_arg) { /* '--' arg should end option processing */ diff -Nuarp a/misc/dumpe2fs.c b/misc/dumpe2fs.c --- a/misc/dumpe2fs.c 2009-08-23 05:44:35.000000000 +0300 +++ b/misc/dumpe2fs.c 2010-07-26 09:47:16.740445926 +0300 @@ -207,6 +207,13 @@ static void list_desc (ext2_filsys fs) diff = fs->group_desc[i].bg_inode_bitmap - first_block; if (diff >= 0) printf(" (+%ld)", diff); + if (fs->exclude_blks && fs->exclude_blks[i]) { + fputs(_(", Exclude bitmap at "), stdout); + print_number(fs->exclude_blks[i]); + diff = fs->exclude_blks[i] - first_block; + if (diff >= 0 && diff <= fs->super->s_blocks_per_group) + printf(" (+%ld)", diff); + } fputs(_("\n Inode table at "), stdout); print_range(fs->group_desc[i].bg_inode_table, fs->group_desc[i].bg_inode_table + diff -Nuarp a/misc/e2image.c b/misc/e2image.c --- a/misc/e2image.c 2009-08-23 05:44:35.000000000 +0300 +++ b/misc/e2image.c 2010-07-26 09:47:16.780449547 +0300 @@ -536,6 +536,7 @@ static void write_raw_image_file(ext2_fi } } else { if ((inode.i_flags & EXT4_EXTENTS_FL) || + (inode.i_flags & EXT4_SNAPFILE_FL) || inode.i_block[EXT2_IND_BLOCK] || inode.i_block[EXT2_DIND_BLOCK] || inode.i_block[EXT2_TIND_BLOCK]) { diff -Nuarp a/misc/lsattr.c b/misc/lsattr.c --- a/misc/lsattr.c 2009-08-13 04:39:57.000000000 +0300 +++ b/misc/lsattr.c 2010-07-26 09:47:16.770445154 +0300 @@ -70,7 +70,7 @@ static int generation_opt; static void usage(void) { - fprintf(stderr, _("Usage: %s [-RVadlv] [files...]\n"), program_name); + fprintf(stderr, _("Usage: %s [-XRVadlv] [files...]\n"), program_name); exit(1); } @@ -169,9 +169,16 @@ int main (int argc, char ** argv) #endif if (argc && *argv) program_name = *argv; - while ((c = getopt (argc, argv, "RVadlv")) != EOF) + i = strlen(program_name); + if (i >= 6 && !strcmp(program_name + i - 6, "lssnap")) + pf_options |= PFOPT_SNAPSHOT; + + while ((c = getopt (argc, argv, "XRVadlv")) != EOF) switch (c) { + case 'X': + pf_options |= PFOPT_SNAPSHOT_X; + break; case 'R': recursive = 1; break; @@ -185,7 +192,7 @@ int main (int argc, char ** argv) dirs_opt = 1; break; case 'l': - pf_options = PFOPT_LONG; + pf_options |= PFOPT_LONG; break; case 'v': generation_opt = 1; diff -Nuarp a/misc/mke2fs.c b/misc/mke2fs.c --- a/misc/mke2fs.c 2009-08-23 05:44:35.000000000 +0300 +++ b/misc/mke2fs.c 2010-07-26 09:47:16.750515354 +0300 @@ -378,6 +378,10 @@ static void write_inode_tables(ext2_fils /* The kernel doesn't need to zero the itable blocks */ fs->group_desc[i].bg_flags |= EXT2_BG_INODE_ZEROED; ext2fs_group_desc_csum_set(fs, i); + if (fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE) + /* zero the designated exclude bitmap block */ + num++; } retval = ext2fs_zero_blocks(fs, blk, num, &blk, &num); if (retval) { @@ -832,6 +836,7 @@ static void parse_extended_opts(struct e static __u32 ok_features[3] = { /* Compat */ EXT3_FEATURE_COMPAT_HAS_JOURNAL | + EXT2_FEATURE_COMPAT_EXCLUDE_INODE | EXT2_FEATURE_COMPAT_RESIZE_INODE | EXT2_FEATURE_COMPAT_DIR_INDEX | EXT2_FEATURE_COMPAT_EXT_ATTR, @@ -843,6 +848,7 @@ static __u32 ok_features[3] = { EXT4_FEATURE_INCOMPAT_FLEX_BG, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\ EXT4_FEATURE_RO_COMPAT_HUGE_FILE| EXT4_FEATURE_RO_COMPAT_DIR_NLINK| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| @@ -949,6 +955,9 @@ static char **parse_fs_type(const char * ext_type = "ext2"; else if (!strcmp(program_name, "mke3fs")) ext_type = "ext3"; + else if (!strcmp(program_name, "mkfs.next3") || + !strcmp(program_name, "mkn3fs")) + ext_type = "ext3"; else if (progname) { ext_type = strrchr(progname, '/'); if (ext_type) @@ -1163,6 +1172,17 @@ static void PRS(int argc, char *argv[]) if (!strcmp(program_name, "mkfs.ext3") || !strcmp(program_name, "mke3fs")) journal_size = -1; + + /* If called as mkfs.next3: */ + if (!strcmp(program_name, "mkfs.next3") || + !strcmp(program_name, "mkn3fs")) { + /* 1. create a big journal */ + journal_size = -NEXT3_MAX_COW_CREDITS; + /* 2. use system page size as block size */ + blocksize = sys_page_size; + /* 3. create exclude inode */ + edit_feature("exclude_inode", &fs_param.s_feature_compat); + } } while ((c = getopt (argc, argv, @@ -2052,6 +2072,15 @@ int main (int argc, char *argv[]) exit(1); } } + if (fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE) { + retval = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE); + if (retval) { + com_err("ext2fs_create_exclude_inode", retval, + _("while reserving blocks for exclude bitmap")); + exit(1); + } + } } if (journal_device) { diff -Nuarp a/misc/tune2fs.c b/misc/tune2fs.c --- a/misc/tune2fs.c 2009-08-23 05:44:35.000000000 +0300 +++ b/misc/tune2fs.c 2010-07-26 09:47:16.760447608 +0300 @@ -118,6 +118,7 @@ static void usage(void) static __u32 ok_features[3] = { /* Compat */ EXT3_FEATURE_COMPAT_HAS_JOURNAL | + EXT2_FEATURE_COMPAT_EXCLUDE_INODE | EXT2_FEATURE_COMPAT_DIR_INDEX, /* Incompat */ EXT2_FEATURE_INCOMPAT_FILETYPE | @@ -125,6 +126,7 @@ static __u32 ok_features[3] = { EXT4_FEATURE_INCOMPAT_FLEX_BG, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE | + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\ EXT4_FEATURE_RO_COMPAT_HUGE_FILE| EXT4_FEATURE_RO_COMPAT_DIR_NLINK| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| @@ -135,6 +137,7 @@ static __u32 ok_features[3] = { static __u32 clear_ok_features[3] = { /* Compat */ EXT3_FEATURE_COMPAT_HAS_JOURNAL | + EXT2_FEATURE_COMPAT_EXCLUDE_INODE | EXT2_FEATURE_COMPAT_RESIZE_INODE | EXT2_FEATURE_COMPAT_DIR_INDEX, /* Incompat */ @@ -142,6 +145,7 @@ static __u32 clear_ok_features[3] = { EXT4_FEATURE_INCOMPAT_FLEX_BG, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE | + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT|\ EXT4_FEATURE_RO_COMPAT_HUGE_FILE| EXT4_FEATURE_RO_COMPAT_DIR_NLINK| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| @@ -249,57 +253,199 @@ no_valid_journal: free(journal_path); } +#define blk64_t blk_t +#define ext2fs_block_iterate3 \ + ext2fs_block_iterate2 +#define ext2fs_unmark_block_bitmap2 \ + ext2fs_unmark_block_bitmap +#define ext2fs_group_of_blk2 \ + ext2fs_group_of_blk +#define ext2fs_bg_free_blocks_count_set(fs, group, count) \ + fs->group_desc[group].bg_free_blocks_count = (count) +#define ext2fs_bg_free_blocks_count(fs, group) \ + fs->group_desc[group].bg_free_blocks_count +#define ext2fs_free_blocks_count_add(sb, count) \ + sb->s_free_blocks_count += (count) +#define ext2fs_group_desc(fs, gdp, grp) \ + (gdp)+(grp) + /* Helper function for remove_journal_inode */ -static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr, - int blockcnt EXT2FS_ATTR((unused)), +static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr, + e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), void *private EXT2FS_ATTR((unused))) { - blk_t block; + blk64_t block; int group; block = *blocknr; - ext2fs_unmark_block_bitmap(fs->block_map, block); - group = ext2fs_group_of_blk(fs, block); - fs->group_desc[group].bg_free_blocks_count++; + ext2fs_unmark_block_bitmap2(fs->block_map, block); + group = ext2fs_group_of_blk2(fs, block); + ext2fs_bg_free_blocks_count_set(fs, group, ext2fs_bg_free_blocks_count(fs, group) + 1); ext2fs_group_desc_csum_set(fs, group); - fs->super->s_free_blocks_count++; + ext2fs_free_blocks_count_add(fs->super, 1); return 0; } /* - * Remove the journal inode from the filesystem + * Remove a special inode from the filesystem: + * - resize inode, @nlink = 0 + * - exclude inode, @nlink = 0 + * - snapshot inodes, @nlink = 1 (snapshots directory) */ -static void remove_journal_inode(ext2_filsys fs) +static void remove_special_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, int nlink) { - struct ext2_inode inode; - errcode_t retval; - ino_t ino = fs->super->s_journal_inum; - - retval = ext2fs_read_inode(fs, ino, &inode); + int retval = ext2fs_read_bitmaps(fs); if (retval) { com_err(program_name, retval, - _("while reading journal inode")); + _("while reading bitmaps")); + exit(1); + } + retval = ext2fs_block_iterate3(fs, ino, + BLOCK_FLAG_READ_ONLY, NULL, + release_blocks_proc, NULL); + if (retval) { + com_err(program_name, retval, + _("while clearing inode")); exit(1); } - if (ino == EXT2_JOURNAL_INO) { - retval = ext2fs_read_bitmaps(fs); + if (nlink) { + /* reset truncated inode */ + inode->i_size = 0; + inode->i_size_high = 0; + inode->i_blocks = 0; + memset(inode->i_block, 0, sizeof(inode->i_block)); + } else { + /* clear unlinked inode */ + memset(inode, 0, sizeof(*inode)); + } + ext2fs_mark_bb_dirty(fs); + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; +} + +/* + * Discard snapshots list (free all snapshot blocks) + */ +static void discard_snapshot_list(ext2_filsys fs) +{ + struct ext2_super_block *sb = fs->super; + struct ext2_inode inode; + ext2_ino_t ino = sb->s_snapshot_list; + errcode_t retval; + int i = 0; + + if (!ino) + /* no snapshot list, but maybe active snapshot exists? */ + ino = sb->s_snapshot_inum; + if (ino) + fputs(_("Discarding snapshots: "), stderr); + + while (ino) { + retval = ext2fs_read_inode(fs, ino, &inode); if (retval) { com_err(program_name, retval, - _("while reading bitmaps")); + _("while reading snapshot inode %u"), + ino); exit(1); } - retval = ext2fs_block_iterate(fs, ino, - BLOCK_FLAG_READ_ONLY, NULL, - release_blocks_proc, NULL); + + remove_special_inode(fs, ino, &inode, 1); + + retval = ext2fs_write_inode(fs, ino, &inode); if (retval) { com_err(program_name, retval, - _("while clearing journal inode")); + _("while writing snapshot inode %u"), + ino); exit(1); } - memset(&inode, 0, sizeof(inode)); - ext2fs_mark_bb_dirty(fs); - fs->flags &= ~EXT2_FLAG_SUPER_ONLY; - } else + + fprintf(stderr, _("%u,"), inode.i_generation); + ino = inode.i_next_snapshot; + i++; + } + + if (i > 0) { + sb->s_snapshot_inum = 0; + sb->s_snapshot_id = 0; + sb->s_snapshot_r_blocks_count = 0; + sb->s_snapshot_list = 0; + fputs(_("done\n"), stderr); + } + + /* no snapshots, so no snapshot problems to fix */ + sb->s_flags &= ~EXT2_FLAGS_FIX_SNAPSHOT; + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + ext2fs_mark_super_dirty(fs); +} + +/* + * Remove the exclude inode from the filesystem + */ +static void remove_exclude_inode(ext2_filsys fs) +{ + struct ext2_inode inode; + ino_t ino = EXT2_EXCLUDE_INO; + errcode_t retval; + struct ext2_group_desc *gd; + int i; + + if (fs->super->s_feature_compat & + NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD) { + /* Remove old exclude inode */ + ino = EXT2_EXCLUDE_INO_OLD; + /* Clear old exclude inode flag */ + fs->super->s_feature_compat &= + ~NEXT3_FEATURE_COMPAT_EXCLUDE_INODE_OLD; + /* Reset old exclude/cow bitmap cache to zero */ + for (i = 0; i < fs->group_desc_count; i++) { + gd = ext2fs_group_desc(fs, fs->group_desc, i); + gd->bg_exclude_bitmap_old = 0; + gd->bg_cow_bitmap_old = 0; + } + } + + /* clear fix_exclude flag */ + fs->super->s_flags &= ~EXT2_FLAGS_FIX_EXCLUDE; + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + ext2fs_mark_super_dirty(fs); + + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) { + com_err(program_name, retval, + _("while reading exclude inode")); + exit(1); + } + + remove_special_inode(fs, ino, &inode, 0); + + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) { + com_err(program_name, retval, + _("while writing exclude inode")); + exit(1); + } +} + +/* + * Remove the journal inode from the filesystem + */ +static void remove_journal_inode(ext2_filsys fs) +{ + struct ext2_inode inode; + errcode_t retval; + ino_t ino = fs->super->s_journal_inum; + + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) { + com_err(program_name, retval, + _("while reading journal inode")); + exit(1); + } + if (ino == EXT2_JOURNAL_INO) + remove_special_inode(fs, ino, &inode, 0); + else inode.i_flags &= ~EXT2_IMMUTABLE_FL; retval = ext2fs_write_inode(fs, ino, &inode); if (retval) { @@ -326,6 +472,32 @@ static void update_mntopts(ext2_filsys f ext2fs_mark_super_dirty(fs); } +static int verify_clean_fs(ext2_filsys fs, int compat, unsigned int mask, + int on) +{ + struct ext2_super_block *sb= fs->super; + + if ((mount_flags & EXT2_MF_MOUNTED) && + !(mount_flags & EXT2_MF_READONLY)) { + fprintf(stderr, _("The '%s' feature may only be " + "%s when the filesystem is\n" + "unmounted or mounted read-only.\n"), + e2p_feature2string(compat, mask), + on ? "set" : "cleared"); + exit(1); + } + if (sb->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_RECOVER) { + fprintf(stderr, _("The needs_recovery flag is set. " + "Please run e2fsck before %s\n" + "the '%s' flag.\n"), + on ? "setting" : "clearing", + e2p_feature2string(compat, mask)); + exit(1); + } + return 1; +} + /* * Update the feature set as provided by the user. */ @@ -333,6 +505,7 @@ static void update_feature_set(ext2_fils { struct ext2_super_block *sb = fs->super; __u32 old_features[3]; + errcode_t retval; int type_err; unsigned int mask_err; @@ -342,11 +515,32 @@ static void update_feature_set(ext2_fils !((&sb->s_feature_compat)[(type)] & (mask))) #define FEATURE_CHANGED(type, mask) ((mask) & \ (old_features[(type)] ^ (&sb->s_feature_compat)[(type)])) +#define FEATURE_ON_SAFE(compat, mask) \ + (FEATURE_ON(compat, mask) && verify_clean_fs(fs, compat, mask, 1)) +#define FEATURE_OFF_SAFE(compat, mask) \ + (FEATURE_OFF(compat, mask) && verify_clean_fs(fs, compat, mask, 0)) old_features[E2P_FEATURE_COMPAT] = sb->s_feature_compat; old_features[E2P_FEATURE_INCOMPAT] = sb->s_feature_incompat; old_features[E2P_FEATURE_RO_INCOMPAT] = sb->s_feature_ro_compat; + /* disallow changing features when filesystem has snapshots */ + if (sb->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) { + fputs(_("The filesystem has snapshots. " + "Please clear the has_snapshot flag\n" + "before clearing/setting other filesystem flags.\n"), + stderr); + ok_features[E2P_FEATURE_COMPAT] = 0; + ok_features[E2P_FEATURE_INCOMPAT] = 0; + ok_features[E2P_FEATURE_RO_INCOMPAT] = + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT; + clear_ok_features[E2P_FEATURE_COMPAT] = 0; + clear_ok_features[E2P_FEATURE_INCOMPAT] = 0; + clear_ok_features[E2P_FEATURE_RO_INCOMPAT] = + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT; + } + if (e2p_edit_feature2(features, &sb->s_feature_compat, ok_features, clear_ok_features, &type_err, &mask_err)) { @@ -402,6 +596,67 @@ static void update_feature_set(ext2_fils sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; } + if (FEATURE_OFF_SAFE(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) { + remove_exclude_inode(fs); + } + + if (FEATURE_ON_SAFE(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) { + retval = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE); + if (retval) { + com_err(program_name, retval, + _("while creating exclude inode")); + exit(1); + } + } + + if (FEATURE_OFF_SAFE(E2P_FEATURE_RO_INCOMPAT, + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT)) { + discard_snapshot_list(fs); + if (sb->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE) { + /* reset exclude bitmap blocks */ + retval = ext2fs_create_exclude_inode(fs, EXCLUDE_RESET); + if (retval) + sb->s_feature_compat &= + ~EXT2_FEATURE_COMPAT_EXCLUDE_INODE; + } + } + + if (FEATURE_ON_SAFE(E2P_FEATURE_RO_INCOMPAT, + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT)) { + int big_journal = 0; + + if ((sb->s_feature_compat & + EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { + /* update 'big_journal' flag */ + big_journal = (ext2fs_check_journal_size(fs) >= + NEXT3_MIN_JOURNAL_BLOCKS); + } else if (!journal_size || journal_size == -1) { + /* Create a big journal for Next3 */ + journal_size = -NEXT3_MAX_COW_CREDITS; + big_journal = 1; + } + + if (!big_journal) + fprintf(stderr, + _("Warning: journal size is not big enough.\n" + "For best operation of Next3, try re-creating " + "the journal with '-J big' before setting the " + "'has_snapshot' flag.\n")); + + /* allocate/reset exclude bitmap blocks */ + retval = ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE); + if (!retval) + sb->s_feature_compat |= + EXT2_FEATURE_COMPAT_EXCLUDE_INODE; + else + fprintf(stderr, + _("Warning: failed to create exclude inode.\n" + "For best operation of Next3, try re-creating " + "the exclude inode before setting the " + "'has_snapshot' flag.\n")); + } + if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX)) { if (!sb->s_def_hash_version) sb->s_def_hash_version = EXT2_HASH_HALF_MD4; diff -Nuarp a/misc/util.c b/misc/util.c --- a/misc/util.c 2009-08-23 05:39:05.000000000 +0300 +++ b/misc/util.c 2010-07-26 09:47:16.760447608 +0300 @@ -218,6 +218,20 @@ void parse_journal_opts(const char *opts journal_size = strtoul(arg, &p, 0); if (*p) journal_usage++; + } else if (strcmp(token, "big") == 0) { + /* Create a big journal for Next3 */ + journal_size = -NEXT3_MAX_COW_CREDITS; + continue; + } else if (strcmp(token, "bigger") == 0) { + /* Create a journal bigger than default */ + if (!arg) { + journal_usage++; + continue; + } + journal_size = -strtoul(arg, &p, 0); + if (*p) + journal_usage++; + continue; } else if (strcmp(token, "v1_superblock") == 0) { journal_flags |= EXT2_MKJOURNAL_V1_SUPER; continue; @@ -231,6 +245,8 @@ void parse_journal_opts(const char *opts "\tis set off by an equals ('=') sign.\n\n" "Valid journal options are:\n" "\tsize=<journal size in megabytes>\n" + "\tbig (Next3 big journal size)\n" + "\tbigger=<X times bigger than default size>\n" "\tdevice=<journal device>\n\n" "The journal size must be between " "1024 and 10240000 filesystem blocks.\n\n"), stderr); @@ -259,6 +275,20 @@ unsigned int figure_journal_size(int siz return 0; } + if (size < -1) { + /* bigger journal requested */ + j_blocks = ext2fs_big_journal_size(-size, fs->super->s_blocks_count); + if (j_blocks < EXT3_DEF_JOURNAL_BLOCKS*(-size)) { + fputs(_("\nFilesystem too small for requested " + "journal size. "), stderr); + if (j_blocks < 0) { + fputs(_("Aborting.\n"), stderr); + exit(1); + } + fputs(_("Creating a smaller journal.\n"), stderr); + } + } + if (size > 0) { j_blocks = size * 1024 / (fs->blocksize / 1024); if (j_blocks < 1024 || j_blocks > 10240000) { diff -Nuarp a/resize/main.c b/resize/main.c --- a/resize/main.c 2009-08-23 05:39:05.000000000 +0300 +++ b/resize/main.c 2010-07-26 09:47:16.790517166 +0300 @@ -437,6 +437,17 @@ int main (int argc, char ** argv) if (mount_flags & EXT2_MF_MOUNTED) { retval = online_resize_fs(fs, mtpt, &new_size, flags); } else { + /* do not offline resize a volume with active snapshot */ + if (!force && (fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT) && + fs->super->s_snapshot_inum) { + fprintf(stderr, + _("offline resize will damage next3 snapshots " + "on %s - Please mount the filesystem " + "for online resize.\n\n"), + device_name); + exit(1); + } if (!force && ((fs->super->s_lastcheck < fs->super->s_mtime) || (fs->super->s_state & EXT2_ERROR_FS) || ((fs->super->s_state & EXT2_VALID_FS) == 0))) { diff -Nuarp a/resize/resize2fs.c b/resize/resize2fs.c --- a/resize/resize2fs.c 2009-08-23 05:44:35.000000000 +0300 +++ b/resize/resize2fs.c 2010-07-26 09:47:16.790517166 +0300 @@ -48,6 +48,7 @@ static errcode_t inode_scan_and_fix(ext2 static errcode_t inode_ref_fix(ext2_resize_t rfs); static errcode_t move_itables(ext2_resize_t rfs); static errcode_t fix_resize_inode(ext2_filsys fs); +static errcode_t fix_exclude_inode(ext2_filsys fs); static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs); static errcode_t fix_sb_journal_backup(ext2_filsys fs); @@ -153,6 +154,10 @@ errcode_t resize_fs(ext2_filsys fs, blk_ if (retval) goto errout; + retval = fix_exclude_inode(rfs->new_fs); + if (retval) + goto errout; + retval = fix_sb_journal_backup(rfs->new_fs); if (retval) goto errout; @@ -1770,6 +1775,25 @@ errout: } /* + * Fix the exclude inode + */ +static errcode_t fix_exclude_inode(ext2_filsys fs) +{ + if (!(fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_EXCLUDE_INODE)) + return 0; + /* + * create_exclude_inode(): + * - updates exclude_blks for existing block groups + * - allocates exclude bitmap blocks for new block groups + * - doesn't free exclude bitmap blocks of deleted block group, + * so when resizing from large to small filesystem, + * it would be wise to remove the exclude inode beforehand. + */ + return ext2fs_create_exclude_inode(fs, EXCLUDE_CREATE); +} + +/* * Finally, recalculate the summary information */ static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs) diff -Nuarp a/version.h b/version.h --- a/version.h 2009-08-23 06:04:33.000000000 +0300 +++ b/version.h 2010-07-26 09:47:16.800445322 +0300 @@ -7,5 +7,5 @@ * redistributed under the GNU Public License. */ -#define E2FSPROGS_VERSION "1.41.9" -#define E2FSPROGS_DATE "22-Aug-2009" +#define E2FSPROGS_VERSION "1.41.9-next3-1.0.12" +#define E2FSPROGS_DATE "25-Jul-2010"