2010-07-20 15:15:29

by Amir Goldstein

[permalink] [raw]
Subject: [PATCHES/RFC v1.0.12] e2fsprogs: Next3 patch series

Hi guys,

The following patches apply and tested on e2fsprogs master branch (on July 20).

Please help me review and push these patches upstream.

Thanks,
Amir.

[PATCH 01/12] e2fsprogs: Next3 on-disk format changes
[PATCH 02/12] e2fsprogs: Create a big journal for Next3
[PATCH 03/12] e2fsprogs: Create/check exclude inode for Next3
[PATCH 04/12] e2fsprogs: Next3 snapshot control with chattr/lsattr -X
[PATCH 05/12] e2fsprogs: Avoid offline modifications to a file system with snapshots
[PATCH 06/12] e2fsprogs: Cleanup Next3 snapshot list when removing has_snapshot feature
[PATCH 07/12] e2fsprogs: Check Next3 exclude bitmap on fsck
[PATCH 08/12] e2fsprogs: Check Next3 snapshot list on fsck
[PATCH 09/12] e2fsprogs: Delete all snapshots on fsck -x
[PATCH 10/12] e2fsprogs: Map maximum filesystem size with a single snapshot file
[PATCH 11/12] e2fsprogs: Dump Next3 message buffer on fsck
[PATCH 12/12] e2fsprogs: Migrate old to new on-disk format




2010-07-20 15:17:21

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 01/12] e2fsprogs: Next3 on-disk format changes

Next3 uses a few reserved fields and flags in the Ext2 super block,
and inode structs.
Next3 defines the compatible feature 'exclude_inode', meaning that
the special exclude inode was allocated.
Next3 defines the read-only compatible features 'has_snapshot', meaning
that the file system contains snapshots.

Signed-off-by: Amir Goldstein <[email protected]>
---
lib/e2p/feature.c | 4 ++++
lib/e2p/ls.c | 12 ++++++++++++
lib/ext2fs/ext2_fs.h | 3 ++-
lib/ext2fs/ext2fs.h | 2 ++
4 files changed, 20 insertions(+), 1 deletions(-)

diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c
index c7f8a35..b9dd7bd 100644
--- a/lib/e2p/feature.c
+++ b/lib/e2p/feature.c
@@ -40,6 +40,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" },
@@ -55,6 +57,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 --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index 6964519..fe12faa 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -160,6 +160,18 @@ static void print_super_flags(struct ext2_super_block * s, FILE *f)
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
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index fa256e5..3a65515 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -431,7 +431,7 @@ struct ext2_inode_large {
#define i_dir_acl i_size_high

#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
@@ -692,6 +692,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 --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index e2c3b09..f0645c3 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -517,6 +517,7 @@ typedef struct ext2_icount *ext2_icount_t;
#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|\
EXT2_FEATURE_COMPAT_RESIZE_INODE|\
EXT2_FEATURE_COMPAT_DIR_INDEX|\
EXT2_FEATURE_COMPAT_EXT_ATTR)
@@ -549,6 +550,7 @@ typedef struct ext2_icount *ext2_icount_t;
#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)
--
1.6.6


2010-07-20 15:17:23

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 02/12] e2fsprogs: Create a big journal for Next3

Next3 transactions reserve up to 24 times more credits than Ext3
transactions for the same operation. On mke2fs and tune2fs, if the
'-J big' option is used to create a journal, increase the default
journal size by a factor of 24.

Signed-off-by: Amir Goldstein <[email protected]>
---
lib/ext2fs/ext2_fs.h | 11 +++++++++++
lib/ext2fs/ext2fs.h | 2 ++
lib/ext2fs/mkjournal.c | 41 +++++++++++++++++++++++++++++++++++++++++
misc/mke2fs.c | 12 ++++++++++++
misc/util.c | 30 ++++++++++++++++++++++++++++++
5 files changed, 96 insertions(+), 0 deletions(-)

diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 3a65515..8e850eb 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -647,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
*/

diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index f0645c3..0c2587f 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1245,6 +1245,8 @@ extern errcode_t ext2fs_add_journal_device(ext2_filsys fs,
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,
diff --git a/lib/ext2fs/mkjournal.c b/lib/ext2fs/mkjournal.c
index 9466e78..a7fb526 100644
--- a/lib/ext2fs/mkjournal.c
+++ b/lib/ext2fs/mkjournal.c
@@ -400,6 +400,47 @@ int ext2fs_default_journal_size(__u64 blocks)
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 --git a/misc/mke2fs.c b/misc/mke2fs.c
index add7c0c..88f4230 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -903,6 +903,9 @@ static char **parse_fs_type(const char *fs_type,
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)
@@ -1164,6 +1167,15 @@ 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;
+ }
}

while ((c = getopt (argc, argv,
diff --git a/misc/util.c b/misc/util.c
index 51bdb60..435557a 100644
--- a/misc/util.c
+++ b/misc/util.c
@@ -222,6 +222,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;
@@ -235,6 +249,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);
@@ -263,6 +279,20 @@ unsigned int figure_journal_size(int size, ext2_filsys fs)
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) {
--
1.6.6


2010-07-20 15:17:25

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 03/12] e2fsprogs: Create/check exclude inode for Next3

The exclude inode owns all the exclude bitmap blocks.
It is pre-allocated by 'mke2fs/tune2fs -O exclude_inode'.
It is extended by resize2fs when block groups are added.
Fsck checks that all exclude inode blocks are allocated.

Signed-off-by: Amir Goldstein <[email protected]>
---
e2fsck/e2fsck.h | 2 +
e2fsck/pass1.c | 21 +++++-
e2fsck/problem.c | 15 ++++
e2fsck/problem.h | 9 ++
e2fsck/super.c | 84 ++++++++++++++++++++
e2fsck/unix.c | 1 +
lib/ext2fs/ext2fs.h | 8 ++
lib/ext2fs/res_gdt.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++
misc/dumpe2fs.c | 7 ++
misc/mke2fs.c | 16 ++++
misc/tune2fs.c | 135 +++++++++++++++++++++++++++-----
resize/resize2fs.c | 24 ++++++
12 files changed, 512 insertions(+), 20 deletions(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index d4df5f3..0f23751 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -177,6 +177,7 @@ struct resource_track {
#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_RESET_FLAGS (E2F_FLAG_TIME_INSANE)

@@ -475,6 +476,7 @@ void e2fsck_rehash_directories(e2fsck_t ctx);
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);

/* util.c */
extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 93763cd..5793467 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -890,7 +890,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;
@@ -1143,6 +1143,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
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 8032fda..199fe5e 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -332,6 +332,16 @@ static struct e2fsck_problem problem_table[] = {
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 },
+
/* 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 +810,11 @@ static struct e2fsck_problem problem_table[] = {
N_("Resize @i (re)creation failed: %m."),
PROMPT_CONTINUE, 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"),
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 7c4c156..dba3a23 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -227,6 +227,12 @@ 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
+

/*
* Pass 1 errors
@@ -520,6 +526,9 @@ struct problem_context {
/* EOFBLOCKS flag set when not necessary */
#define PR_1_EOFBLOCKS_FL_SET 0x010060

+/* Exclude inode failed */
+#define PR_1_EXCLUDE_INODE_CREATE 0x010100
+
/*
* Pass 1b errors
*/
diff --git a/e2fsck/super.c b/e2fsck/super.c
index b6923c6..c155949 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -426,6 +426,90 @@ 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);
+
+ /* 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;
+ }
+}
+
+/*
* This function checks the dirhash signed/unsigned hint if necessary.
*/
static void e2fsck_fix_dirhash_hint(e2fsck_t ctx)
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 6cb2214..71e563d 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1319,6 +1319,7 @@ print_unsupp_features:
fatal_error(ctx, 0);
check_if_skip(ctx);
check_resize_inode(ctx);
+ check_exclude_inode(ctx);
if (bad_blocks_file)
read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
else if (cflag)
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 0c2587f..c8a8dbc 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -209,6 +209,7 @@ struct struct_ext2_filsys {
dgrp_t group_desc_count;
unsigned long desc_blocks;
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;
@@ -1293,6 +1294,13 @@ extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,

/* 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,
diff --git a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c
index bf10995..bfc51fa 100644
--- a/lib/ext2fs/res_gdt.c
+++ b/lib/ext2fs/res_gdt.c
@@ -218,3 +218,213 @@ out_free:
return retval;
}

+/*
+ * 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);
+
+ 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;
+#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 --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 6ec0858..76d5065 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -207,6 +207,13 @@ static void list_desc (ext2_filsys fs)
diff = ext2fs_inode_bitmap_loc(fs, i) - 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(ext2fs_inode_table_loc(fs, i),
ext2fs_inode_table_loc(fs, i) +
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 88f4230..6894945 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -329,6 +329,10 @@ static void write_inode_tables(ext2_filsys fs, int lazy_flag)
/* The kernel doesn't need to zero the itable blocks */
ext2fs_bg_flags_set(fs, i, 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_blocks2(fs, blk, num, &blk, &num);
if (retval) {
@@ -784,6 +788,7 @@ static void parse_extended_opts(struct ext2_super_block *param,
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,
@@ -1175,6 +1180,8 @@ static void PRS(int argc, char *argv[])
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);
}
}

@@ -2173,6 +2180,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 --git a/misc/tune2fs.c b/misc/tune2fs.c
index 4734331..596b384 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -119,6 +119,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 |
@@ -136,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 */
@@ -270,6 +272,74 @@ static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr,
}

/*
+ * Remove a special inode from the filesystem:
+ * - resize inode, @nlink = 0
+ * - exclude inode, @nlink = 0
+ * - snapshot inodes, @nlink = 1 (snapshots directory)
+ */
+static void remove_special_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, int nlink)
+{
+ int retval = ext2fs_read_bitmaps(fs);
+ if (retval) {
+ com_err(program_name, retval,
+ _("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 (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;
+}
+
+/*
+ * 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;
+
+ /* 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)
@@ -284,25 +354,9 @@ static void remove_journal_inode(ext2_filsys fs)
_("while reading journal inode"));
exit(1);
}
- if (ino == EXT2_JOURNAL_INO) {
- retval = ext2fs_read_bitmaps(fs);
- if (retval) {
- com_err(program_name, retval,
- _("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 journal inode"));
- exit(1);
- }
- memset(&inode, 0, sizeof(inode));
- ext2fs_mark_bb_dirty(fs);
- fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
- } else
+ 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) {
@@ -341,6 +395,32 @@ static void request_fsck_afterwards(ext2_filsys fs)
printf(_("(and reboot afterwards!)\n"));
}

+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.
*/
@@ -359,6 +439,10 @@ static void update_feature_set(ext2_filsys fs, char *features)
!((&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;
@@ -419,6 +503,19 @@ static void update_feature_set(ext2_filsys fs, char *features)
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_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 --git a/resize/resize2fs.c b/resize/resize2fs.c
index 064c4c4..b0e9f34 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -48,6 +48,7 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs);
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);

@@ -149,6 +150,10 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
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;
@@ -1774,6 +1779,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)
--
1.6.6


2010-07-20 15:17:28

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 04/12] e2fsprogs: Next3 snapshot control with chattr/lsattr -X

Set/clear snapshots parent directory with chattr +/-x.
Take/delete snapshot with chattr -X +/-S.
Enable/disable snapshot with chattr -X +/-n.
View snapshot status with lsattr -X.

Signed-off-by: Amir Goldstein <[email protected]>
---
lib/e2p/e2p.h | 15 +++++++++++++++
lib/e2p/pf.c | 17 ++++++++++++++++-
misc/chattr.c | 26 ++++++++++++++++++++++++--
misc/lsattr.c | 9 ++++++---
4 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/lib/e2p/e2p.h b/lib/e2p/e2p.h
index 4a68dd9..de6ae54 100644
--- a/lib/e2p/e2p.h
+++ b/lib/e2p/e2p.h
@@ -26,6 +26,21 @@
/* `options' for print_flags() */

#define PFOPT_LONG 1 /* Must be 1 for compatibility with `int long_format'. */
+#define PFOPT_SNAPSHOT 2
+
+/*
+ * snapshot status/control flags for lsattr/chattr -X.
+ * using free flags on the GET/SETFLAGS ioctl for snapshot control API.
+ * TODO: define new ioctls for snapshot status/control.
+ */
+#define NEXT3_SNAPFILE_LIST_FL 0x00100000 /* snapshot is on list */
+#define NEXT3_SNAPFILE_ACTIVE_FL 0x00200000 /* snapshot is active */
+#define NEXT3_SNAPFILE_OPEN_FL 0x00400000 /* snapshot is mounted */
+#define NEXT3_SNAPFILE_INUSE_FL 0x00800000 /* snapshot is in-use */
+#define NEXT3_SNAPFILE_ENABLED_FL 0x02000000 /* snapshot is enabled */
+#define NEXT3_SNAPFILE_DELETED_FL 0x04000000 /* snapshot is deleted */
+#define NEXT3_SNAPFILE_SHRUNK_FL 0x08000000 /* snapshot is shrunk */
+#define NEXT3_SNAPFILE_TAGGED_FL 0x10000000 /* snapshot is tagged */


int fgetflags (const char * name, unsigned long * flags);
diff --git a/lib/e2p/pf.c b/lib/e2p/pf.c
index f34a5cc..67a3994 100644
--- a/lib/e2p/pf.c
+++ b/lib/e2p/pf.c
@@ -48,16 +48,31 @@ 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 }
+};
+
+static struct flags_name snapshot_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) ?
+ 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 --git a/misc/chattr.c b/misc/chattr.c
index de33b08..dfe9e64 100644
--- a/misc/chattr.c
+++ b/misc/chattr.c
@@ -82,7 +82,10 @@ static unsigned long sf;
static void usage(void)
{
fprintf(stderr,
- _("Usage: %s [-RVf] [-+=AacDdeijsSu] [-v version] files...\n"),
+ _("Usage: %s [-RVf] [-+=AacDdeijsSux] [-v version] files...\n"),
+ program_name);
+ fprintf(stderr,
+ _("Usage: %s -X [-+=Snapshot] files...\n"),
program_name);
exit(1);
}
@@ -92,7 +95,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 +109,21 @@ static const struct flags_char flags_array[] = {
{ 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;
+
+static struct flags_char snapshot_flags_array[] = {
+ { NEXT3_SNAPFILE_LIST_FL, 'S' },
+ { NEXT3_SNAPFILE_ENABLED_FL, 'n' },
+ { NEXT3_SNAPFILE_ACTIVE_FL, 'a' },
+ { NEXT3_SNAPFILE_INUSE_FL, 'p' },
+ { NEXT3_SNAPFILE_DELETED_FL, 's' },
+ { NEXT3_SNAPFILE_SHRUNK_FL, 'h' },
+ { NEXT3_SNAPFILE_OPEN_FL, 'o' },
+ { NEXT3_SNAPFILE_TAGGED_FL, 't' },
{ 0, 0 }
};

@@ -131,6 +149,10 @@ static int decode_arg (int * i, int argc, char ** argv)
{
case '-':
for (p = &argv[*i][1]; *p; p++) {
+ if (*p == 'X') {
+ flags_array = snapshot_flags_array;
+ continue;
+ }
if (*p == 'R') {
recursive = 1;
continue;
diff --git a/misc/lsattr.c b/misc/lsattr.c
index 15b17ad..2864075 100644
--- a/misc/lsattr.c
+++ b/misc/lsattr.c
@@ -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,12 @@ int main (int argc, char ** argv)
#endif
if (argc && *argv)
program_name = *argv;
- while ((c = getopt (argc, argv, "RVadlv")) != EOF)
+ while ((c = getopt (argc, argv, "XRVadlv")) != EOF)
switch (c)
{
+ case 'X':
+ pf_options |= PFOPT_SNAPSHOT;
+ break;
case 'R':
recursive = 1;
break;
@@ -185,7 +188,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;
--
1.6.6


2010-07-20 15:17:32

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 05/12] e2fsprogs: Avoid offline modifications to a file system with snapshots

Next3 sets the read-only compatible feature 'has_snapshot',
so the file system could be mounted with Ext3 only in read-only mode
to protect the snapshots.
Fsck displays a warning about possible corruption of the snapshots
in interactive mode and avoids freeing blocks in preen mode.

Signed-off-by: Amir Goldstein <[email protected]>
---
e2fsck/e2fsck.h | 1 +
e2fsck/pass1.c | 11 +++++++++++
e2fsck/pass5.c | 4 ++++
e2fsck/rehash.c | 7 +++++++
e2fsck/super.c | 41 +++++++++++++++++++++++++++++++++++++++++
e2fsck/unix.c | 1 +
misc/mke2fs.c | 1 +
misc/tune2fs.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
resize/main.c | 11 +++++++++++
9 files changed, 131 insertions(+), 0 deletions(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 0f23751..13d4161 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -477,6 +477,7 @@ 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 --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 5793467..43ca2e6 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1668,6 +1668,17 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino,
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);
diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index cbc12f3..fa14b19 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -334,6 +334,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] != ext2fs_bg_free_blocks_count(fs, i)) {
pctx.group = i;
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index 5543134..e045fd1 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -829,6 +829,13 @@ void e2fsck_rehash_directories(e2fsck_t ctx)
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 --git a/e2fsck/super.c b/e2fsck/super.c
index c155949..a9cf0d9 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -231,6 +231,12 @@ static int release_orphan_inodes(e2fsck_t ctx)
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;

@@ -510,6 +516,41 @@ void check_exclude_inode(e2fsck_t ctx)
}

/*
+ * 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 (!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)
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 71e563d..43b0197 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1319,6 +1319,7 @@ 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);
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 6894945..f383f82 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -801,6 +801,7 @@ static __u32 ok_features[3] = {
EXT4_FEATURE_INCOMPAT_64BIT,
/* 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|
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 596b384..47d2754 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -127,6 +127,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|
@@ -145,6 +146,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|
@@ -448,6 +450,23 @@ static void update_feature_set(ext2_filsys fs, char *features)
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)) {
@@ -516,6 +535,41 @@ static void update_feature_set(ext2_filsys fs, char *features)
}
}

+ 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 --git a/resize/main.c b/resize/main.c
index 7d8b287..ab5515f 100644
--- a/resize/main.c
+++ b/resize/main.c
@@ -444,6 +444,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))) {
--
1.6.6


2010-07-20 15:17:32

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 06/12] e2fsprogs: Cleanup Next3 snapshot list when removing has_snapshot feature

Discard all snapshots by 'tune2fs -O ^has_snapshot'.
Snapshot inodes are chained on a list starting at the super block.
Delete all snapshot inodes on the list and reset exclude bitmap.

Signed-off-by: Amir Goldstein <[email protected]>
---
misc/tune2fs.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 68 insertions(+), 0 deletions(-)

diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 47d2754..24b3aaa 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -311,6 +311,61 @@ static void remove_special_inode(ext2_filsys fs, ext2_ino_t ino,
}

/*
+ * 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 snapshot inode %u"),
+ ino);
+ exit(1);
+ }
+
+ remove_special_inode(fs, ino, &inode, 1);
+
+ retval = ext2fs_write_inode(fs, ino, &inode);
+ if (retval) {
+ com_err(program_name, retval,
+ _("while writing snapshot inode %u"),
+ ino);
+ exit(1);
+ }
+
+ 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)
@@ -535,6 +590,19 @@ static void update_feature_set(ext2_filsys fs, char *features)
}
}

+ 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;
--
1.6.6


2010-07-20 15:17:37

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 08/12] e2fsprogs: Check Next3 snapshot list on fsck

Check that all inodes on snapshot list are valid snapshots and
prompt to terminate list when bad inode is found.

Signed-off-by: Amir Goldstein <[email protected]>
---
e2fsck/problem.c | 11 ++++++-
e2fsck/problem.h | 3 ++
e2fsck/super.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 0a2d31d..0b6b892 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -40,7 +40,8 @@
#define PROMPT_UNLINK 17
#define PROMPT_CLEAR_HTREE 18
#define PROMPT_RECREATE 19
-#define PROMPT_NULL 20
+#define PROMPT_TERMINATE_LIST 20
+#define PROMPT_NULL 22

/*
* These are the prompts which are used to ask the user if they want
@@ -67,7 +68,8 @@ static const char *prompt[] = {
N_("Unlink"), /* 17 */
N_("Clear HTree index"),/* 18 */
N_("Recreate"), /* 19 */
- "", /* 20 */
+ N_("Terminate list"), /* 20 */
+ "", /* 22 */
};

/*
@@ -342,6 +344,11 @@ static struct e2fsck_problem problem_table[] = {
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 },
+
/* 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"),
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index da52597..1e63b52 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -234,6 +234,9 @@ struct problem_context {
/* Exclude inode invalid */
#define PR_0_EXCLUDE_INODE_INVALID 0x000101

+/* Bas snapshot on list */
+#define PR_0_BAD_SNAPSHOT 0x000102
+

/*
* Pass 1 errors
diff --git a/e2fsck/super.c b/e2fsck/super.c
index a9cf0d9..8e24c18 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -516,6 +516,75 @@ void check_exclude_inode(e2fsck_t ctx)
}

/*
+ * 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)
@@ -528,6 +597,10 @@ void check_snapshots(e2fsck_t ctx)
/* no snapshots */
return;

+ if (!check_snapshot_list(ctx))
+ /* no valid snapshots on list */
+ return;
+
if (!sb->s_snapshot_inum)
/* no active snapshot */
return;
--
1.6.6


2010-07-20 15:17:40

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 09/12] e2fsprogs: Delete all snapshots on fsck -x

On fsck -x or if the 'fix_snapshot' flag is set, prompt to delete
all snapshot inodes and reset exclude bitmap.

Signed-off-by: Amir Goldstein <[email protected]>
---
e2fsck/e2fsck.h | 2 ++
e2fsck/pass1.c | 7 +++++++
e2fsck/problem.c | 8 ++++++++
e2fsck/problem.h | 3 +++
e2fsck/super.c | 24 ++++++++++++++++++++++++
e2fsck/unix.c | 8 ++++++--
6 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index bc938e0..19a39fd 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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
@@ -178,6 +179,7 @@ struct resource_track {
#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)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index d7d51c1..daecce6 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1931,6 +1931,13 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
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);

diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 0b6b892..647075b 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -41,6 +41,7 @@
#define PROMPT_CLEAR_HTREE 18
#define PROMPT_RECREATE 19
#define PROMPT_TERMINATE_LIST 20
+#define PROMPT_DISCARD_SNAPSHOTS 21
#define PROMPT_NULL 22

/*
@@ -69,6 +70,7 @@ static const char *prompt[] = {
N_("Clear HTree index"),/* 18 */
N_("Recreate"), /* 19 */
N_("Terminate list"), /* 20 */
+ N_("Discard snapshots"),/* 21 */
"", /* 22 */
};

@@ -349,6 +351,12 @@ static struct e2fsck_problem problem_table[] = {
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"),
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 1e63b52..43c3376 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -237,6 +237,9 @@ struct problem_context {
/* Bas snapshot on list */
#define PR_0_BAD_SNAPSHOT 0x000102

+/* Corrupted snapshot */
+#define PR_0_FIX_SNAPSHOT 0x000103
+

/*
* Pass 1 errors
diff --git a/e2fsck/super.c b/e2fsck/super.c
index 8e24c18..8cc6584 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -597,6 +597,30 @@ void check_snapshots(e2fsck_t ctx)
/* 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;
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 43b0197..ec00800 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -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);
@@ -667,7 +668,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
ctx->program_name = *argv;
else
ctx->program_name = "e2fsck";
- while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
+ 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;
@@ -790,6 +791,9 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
case 'k':
keep_bad_blocks++;
break;
+ case 'x':
+ ctx->options |= E2F_OPT_FIX_SNAPSHOT;
+ break;
default:
usage(ctx);
}
--
1.6.6


2010-07-20 15:17:35

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 07/12] e2fsprogs: Check Next3 exclude bitmap on fsck

Excluding blocks is done by setting their bit in the exclude bitmap.
There is one exclude bitmap block per block group. The exclude
bitmap blocks are owned by the special exclude inode.
Fsck checks that all (and only) snapshot file blocks are excluded.

Signed-off-by: Amir Goldstein <[email protected]>
---
e2fsck/e2fsck.h | 1 +
e2fsck/pass1.c | 16 +++++-
e2fsck/pass5.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++
e2fsck/problem.c | 31 ++++++++++
e2fsck/problem.h | 19 ++++++
lib/ext2fs/ext2fs.h | 20 +++++++
lib/ext2fs/rw_bitmaps.c | 138 ++++++++++++++++++++++++++++++++++++++------
7 files changed, 349 insertions(+), 20 deletions(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 13d4161..bc938e0 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -234,6 +234,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 */

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 43ca2e6..d7d51c1 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -79,7 +79,7 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
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;
+ fragmented:1, compressed:1, bbcheck:1, snapfile:1;
blk64_t num_blocks;
blk64_t max_blocks;
e2_blkcnt_t last_block;
@@ -625,6 +625,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,
@@ -1913,6 +1923,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
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;
@@ -2258,6 +2269,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 --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index fa14b19..9167cfb 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -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_t ctx, int problem,
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;
@@ -367,6 +386,131 @@ errout:
ext2fs_free_mem(&free_array);
}

+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 --git a/e2fsck/problem.c b/e2fsck/problem.c
index 199fe5e..0a2d31d 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1548,6 +1548,26 @@ static struct e2fsck_problem problem_table[] = {
"\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: "),
@@ -1624,6 +1644,16 @@ static struct e2fsck_problem problem_table[] = {
" +(%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)",
@@ -1665,6 +1695,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 --git a/e2fsck/problem.h b/e2fsck/problem.h
index dba3a23..da52597 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -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)

@@ -992,6 +993,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 --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index c8a8dbc..53c7a55 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -182,6 +182,7 @@ typedef struct ext2_file *ext2_file_t;
#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
@@ -213,6 +214,7 @@ struct struct_ext2_filsys {
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);
@@ -671,8 +673,10 @@ extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
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);
@@ -1495,6 +1499,14 @@ _INLINE_ void ext2fs_mark_bb_dirty(ext2_filsys fs)
}

/*
+ * 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)
@@ -1511,6 +1523,14 @@ _INLINE_ int ext2fs_test_bb_dirty(ext2_filsys fs)
}

/*
+ * 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 --git a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c
index 89416ca..b70605a 100644
--- a/lib/ext2fs/rw_bitmaps.c
+++ b/lib/ext2fs/rw_bitmaps.c
@@ -27,14 +27,15 @@
#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;
blk64_t blk;
blk64_t blk_itr = fs->super->s_first_data_block;
@@ -45,6 +46,17 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
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 +69,13 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
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,19 +86,26 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
}

for (i = 0; i < fs->group_desc_count; i++) {
- if (!do_block)
+ if (!do_block && !do_exclude)
goto skip_block_bitmap;

if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT)
)
goto skip_this_block_bitmap;

- retval = ext2fs_get_block_bitmap_range2(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 = ((ext2fs_blocks_count(fs->super)
- (__u64) fs->super->s_first_data_block)
@@ -89,12 +115,18 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
ext2fs_set_bit(j, block_buf);
}
blk = ext2fs_block_bitmap_loc(fs, i);
- if (blk) {
+ 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;
+ }
skip_this_block_bitmap:
blk_itr += block_nbytes << 3;
skip_block_bitmap:
@@ -133,10 +165,11 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
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;
@@ -154,6 +187,17 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)

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 +217,21 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
(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)
@@ -215,6 +273,11 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
blk_cnt = (blk64_t)EXT2_BLOCKS_PER_GROUP(fs->super) *
fs->group_desc_count;
while (block_nbytes > 0) {
+ 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)
@@ -254,8 +317,30 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
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 = ext2fs_inode_bitmap_loc(fs, i);
if (csum_flag &&
@@ -284,6 +369,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 +386,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 +395,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);
}
--
1.6.6


2010-07-20 15:17:46

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 11/12] e2fsprogs: Dump Next3 message buffer on fsck

Next3 error messages are recorded in a 2K message buffer after the
journal super block. On journal recovery, the journal message buffer
is copied to the file system message buffer. On fsck, if the message
buffer is not empty, the recorded messages are printed to stdout and
the buffer is cleared.
Next3 supports only block size of 4K, so there is always 2K of free
space for the message buffer after the 1K super block.

Signed-off-by: Amir Goldstein <[email protected]>
---
e2fsck/journal.c | 14 ++++++++++++++
e2fsck/super.c | 42 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 56 insertions(+), 0 deletions(-)

diff --git a/e2fsck/journal.c b/e2fsck/journal.c
index 75dafa6..aa16751 100644
--- a/e2fsck/journal.c
+++ b/e2fsck/journal.c
@@ -836,6 +836,20 @@ static errcode_t recover_ext3_journal(e2fsck_t ctx)


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 --git a/e2fsck/super.c b/e2fsck/super.c
index 8cc6584..1dee84d 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -674,6 +674,43 @@ static void e2fsck_fix_dirhash_hint(e2fsck_t ctx)
}
}

+/*
+ * 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)
{
@@ -1096,6 +1133,11 @@ void check_super_block(e2fsck_t ctx)
*/
e2fsck_fix_dirhash_hint(ctx);

+ /*
+ * Print message buffer if necessary
+ */
+ e2fsck_print_message_buffer(ctx);
+
return;
}

--
1.6.6


2010-07-20 15:17:48

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 12/12] e2fsprogs: Migrate old to new on-disk format

Detect old 'has_snapshot' feature on ext2fs_open(), migrate snapshot
fields and flags and set new 'has_snapshot' feature.
Detect old 'exclude_inode' feature on create/check_exclude_inode(),
copy old exclude inode to new exclude inode location and set the new
'exclude_inode' feature.

Signed-off-by: Amir Goldstein <[email protected]>
---
e2fsck/super.c | 18 ++++++++++++++++++
lib/ext2fs/ext2_fs.h | 16 ++++++++++++++++
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/openfs.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/res_gdt.c | 28 ++++++++++++++++++++++++++++
lib/ext2fs/swapfs.c | 5 +++++
misc/tune2fs.c | 17 +++++++++++++++++
7 files changed, 130 insertions(+), 0 deletions(-)

diff --git a/e2fsck/super.c b/e2fsck/super.c
index 1dee84d..948c4bb 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -448,6 +448,24 @@ void check_exclude_inode(e2fsck_t ctx)

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);
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 3a48486..bcc0f78 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -51,6 +51,7 @@
#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
@@ -150,6 +151,9 @@ 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
*/
@@ -632,6 +636,12 @@ struct ext2_super_block {

#define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START)

+/* 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
*/
@@ -687,6 +697,8 @@ struct ext2_super_block {
#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
@@ -696,6 +708,10 @@ struct ext2_super_block {
#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
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 53c7a55..0e0b2fb 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -521,6 +521,7 @@ typedef struct ext2_icount *ext2_icount_t;
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)
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index 50aa207..e84a124 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -205,6 +205,51 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
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 --git a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c
index bfc51fa..5df27c9 100644
--- a/lib/ext2fs/res_gdt.c
+++ b/lib/ext2fs/res_gdt.c
@@ -252,6 +252,28 @@ errcode_t ext2fs_create_exclude_inode(ext2_filsys fs, int flags)
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;
@@ -377,6 +399,12 @@ errcode_t ext2fs_create_exclude_inode(ext2_filsys fs, int flags)
}
}
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);
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index 619bd2d..d672c97 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -76,6 +76,11 @@ void ext2fs_swap_super(struct ext2_super_block * sb)
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]);
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 24b3aaa..3e32b81 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -373,6 +373,23 @@ 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;
--
1.6.6


2010-07-20 15:17:54

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 10/12] e2fsprogs: Map maximum filesystem size with a single snapshot file

To map 2^32 logical blocks, 4 triple indirect blocks are used instead
of just one. The extra 3 triple indirect blocks are stored in-place
of direct blocks, which are not in use by snapshot files.
Snapshots cannot be enabled on filesytem with block size < 4K.

Signed-off-by: Amir Goldstein <[email protected]>
---
e2fsck/pass1.c | 14 ++++++++++----
lib/ext2fs/block.c | 15 +++++++++++++++
lib/ext2fs/bmap.c | 19 +++++++++++++++++--
lib/ext2fs/ext2_fs.h | 11 +++++++++++
lib/ext2fs/i_block.c | 12 +++++++++---
misc/e2image.c | 1 +
6 files changed, 63 insertions(+), 9 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index daecce6..cd4432b 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1081,6 +1081,7 @@ 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_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;
@@ -2007,8 +2008,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
}
}

- 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
@@ -2039,6 +2042,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
!(inode->i_flags & EXT4_EOFBLOCKS_FL))
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;
@@ -2077,8 +2081,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
(inode->i_size_high || inode->i_size & 0x80000000UL))
ctx->large_files++;
if ((pb.num_blocks != ext2fs_inode_i_blocks(fs, inode)) ||
- ((fs->super->s_feature_ro_compat &
- EXT4_FEATURE_RO_COMPAT_HUGE_FILE) &&
+ (((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;
diff --git a/lib/ext2fs/block.c b/lib/ext2fs/block.c
index ff0d8bd..dfc0791 100644
--- a/lib/ext2fs/block.c
+++ b/lib/ext2fs/block.c
@@ -484,6 +484,12 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
* 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)) {
blk64 = inode.i_block[i];
ret |= (*ctx.func)(fs, &blk64, ctx.bcount, 0, i,
@@ -514,6 +520,15 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
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 --git a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c
index fbcb375..d92e981 100644
--- a/lib/ext2fs/bmap.c
+++ b/lib/ext2fs/bmap.c
@@ -136,6 +136,8 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
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;
@@ -286,7 +288,20 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,

/* 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)
@@ -298,7 +313,7 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
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 --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 8e850eb..3a48486 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -250,6 +250,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
diff --git a/lib/ext2fs/i_block.c b/lib/ext2fs/i_block.c
index 822776d..8079d8e 100644
--- a/lib/ext2fs/i_block.c
+++ b/lib/ext2fs/i_block.c
@@ -31,8 +31,10 @@ errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode,
{
unsigned long long b = inode->i_blocks;

- if (!(fs->super->s_feature_ro_compat &
+ 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;

@@ -53,8 +55,10 @@ errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode,
{
unsigned long long b = inode->i_blocks;

- if (!(fs->super->s_feature_ro_compat &
+ 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;

@@ -74,8 +78,10 @@ errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode,

errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b)
{
- if (!(fs->super->s_feature_ro_compat &
+ 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;

diff --git a/misc/e2image.c b/misc/e2image.c
index 003ac5a..d9e0681 100644
--- a/misc/e2image.c
+++ b/misc/e2image.c
@@ -536,6 +536,7 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag)
}
} 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]) {
--
1.6.6


2010-07-20 15:57:29

by Amir G.

[permalink] [raw]
Subject: Re: [PATCHES/RFC v1.0.12] e2fsprogs: Next3 patch series

On Tue, Jul 20, 2010 at 6:16 PM, Amir Goldstein wrote:
> Hi guys,
>
> The following patches apply and tested on e2fsprogs master branch (on July 20).
>
> Please help me review and push these patches upstream.
>
> Thanks,
> Amir.
>
> [PATCH 01/12] e2fsprogs: Next3 on-disk format changes
> [PATCH 02/12] e2fsprogs: Create a big journal for Next3
> [PATCH 03/12] e2fsprogs: Create/check exclude inode for Next3
> [PATCH 04/12] e2fsprogs: Next3 snapshot control with chattr/lsattr -X
> [PATCH 05/12] e2fsprogs: Avoid offline modifications to a file system with snapshots
> [PATCH 06/12] e2fsprogs: Cleanup Next3 snapshot list when removing has_snapshot feature
> [PATCH 07/12] e2fsprogs: Check Next3 exclude bitmap on fsck
> [PATCH 08/12] e2fsprogs: Check Next3 snapshot list on fsck
> [PATCH 09/12] e2fsprogs: Delete all snapshots on fsck -x
> [PATCH 10/12] e2fsprogs: Map maximum filesystem size with a single snapshot file
> [PATCH 11/12] e2fsprogs: Dump Next3 message buffer on fsck
> [PATCH 12/12] e2fsprogs: Migrate old to new on-disk format
>
>
>

Hi Jan,

I saw you are now an official maintainer of ext3
and you were also the only one to pick up the ext3_forget() bug fix I posted.

So I thought it is worth a shot to ask you personally if you will have
time to review my Next3 patches.

In fact, the posted patches are only the small patches to e2fsprogs.
The more challenging job is the review of the Next3 snapshot patches,
which apply on top of ext3 (or rather a forked branch of ext3 called next3).
They are available for download at
http://sourceforge.net/projects/next3/files/Latest%20patch%20series
but I can also post them to the list if you like (about 40 medium size patches).

Ted and Andreas have looked at some of the previous e2fsprogs patches
I posted a while back
and Ted has already merged a part of the the first patch (on-disk
format) to the master branch.
I hope they will have time to look at these new patches.

Thanks,
Amir.

2010-07-20 16:38:51

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCHES/RFC v1.0.12] e2fsprogs: Next3 patch series

On Tue 20-07-10 18:57:26, Amir G. wrote:
> On Tue, Jul 20, 2010 at 6:16 PM, Amir Goldstein wrote:
> > Hi guys,
> >
> > The following patches apply and tested on e2fsprogs master branch (on July 20).
> >
> > Please help me review and push these patches upstream.
> >
> > Thanks,
> > Amir.
> >
> > [PATCH 01/12] e2fsprogs: Next3 on-disk format changes
> > [PATCH 02/12] e2fsprogs: Create a big journal for Next3
> > [PATCH 03/12] e2fsprogs: Create/check exclude inode for Next3
> > [PATCH 04/12] e2fsprogs: Next3 snapshot control with chattr/lsattr -X
> > [PATCH 05/12] e2fsprogs: Avoid offline modifications to a file system with snapshots
> > [PATCH 06/12] e2fsprogs: Cleanup Next3 snapshot list when removing has_snapshot feature
> > [PATCH 07/12] e2fsprogs: Check Next3 exclude bitmap on fsck
> > [PATCH 08/12] e2fsprogs: Check Next3 snapshot list on fsck
> > [PATCH 09/12] e2fsprogs: Delete all snapshots on fsck -x
> > [PATCH 10/12] e2fsprogs: Map maximum filesystem size with a single snapshot file
> > [PATCH 11/12] e2fsprogs: Dump Next3 message buffer on fsck
> > [PATCH 12/12] e2fsprogs: Migrate old to new on-disk format
> >
> So I thought it is worth a shot to ask you personally if you will have
> time to review my Next3 patches.
>
> In fact, the posted patches are only the small patches to e2fsprogs.
> The more challenging job is the review of the Next3 snapshot patches,
> which apply on top of ext3 (or rather a forked branch of ext3 called next3).
> They are available for download at
> http://sourceforge.net/projects/next3/files/Latest%20patch%20series
> but I can also post them to the list if you like (about 40 medium size patches).
Well, I can have a look at those patches. But I'd like to know what is
exactly your motivation - is it that you have some customers running with
this clone and want to upstream the fs, or is it that you'd like to
contribute the cool feature you've developped, or something else? Because
on this depends where we should go from the current situation...
To state my position: I'm not willing to merge the feature into ext3
because it's basically in a maintenance mode so we don't accept larger
features to it anymore (for stability reasons).
You could, of course, copy ext3 code base and create a separate next3
filesystem. But such code duplication would be generally frowned upon and I
personally wouldn't like to take the burden of maintaining such code so
you'd have to do it. Moreover you have to port all ext3 fixes to your code
and you have a problem that as time progresses, new features are added to
ext4, not ext3, so I think it would be less and less attractive to run
Next3 instead of ext4... So this doesn't seem like an ideal solution either.
For future, the most promising to me would be to change the
implementation to work with ext4 and merge it there. I understand there are
technical issues with this and I'm not sure how hard they would be to
solve. But for me as a filesystem developper this would seem like a
direction where it's worth to invest some time and energy and I can help
with that (and I believe other ext4 developpers might lend a hand as well).
Just my thoughts... Anyway, I've added to my todo an item to have a look
at your patches so that I have better idea what we are discussing about.

Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR

2010-07-20 20:12:24

by Amir G.

[permalink] [raw]
Subject: Re: [PATCHES/RFC v1.0.12] e2fsprogs: Next3 patch series

On Tue, Jul 20, 2010 at 6:38 PM, Jan Kara <[email protected]> wrote:
>> In fact, the posted patches are only the small patches to e2fsprogs.
>> The more challenging job is the review of the Next3 snapshot patches,
>> which apply on top of ext3 (or rather a forked branch of ext3 called next3).
>> They are available for download at
>> http://sourceforge.net/projects/next3/files/Latest%20patch%20series
>> but I can also post them to the list if you like (about 40 medium size patches).
> ?Well, I can have a look at those patches. But I'd like to know what is
> exactly your motivation - is it that you have some customers running with
> this clone and want to upstream the fs, or is it that you'd like to
> contribute the cool feature you've developped, or something else?

contribute cool feature is the best match.

> Because on this depends where we should go from the current situation...
> ?To state my position: I'm not willing to merge the feature into ext3
> because it's basically in a maintenance mode so we don't accept larger
> features to it anymore (for stability reasons).

of course.

> ?You could, of course, copy ext3 code base and create a separate next3
> filesystem. But such code duplication would be generally frowned upon and I
> personally wouldn't like to take the burden of maintaining such code so
> you'd have to do it. Moreover you have to port all ext3 fixes to your code
> and you have a problem that as time progresses, new features are added to
> ext4, not ext3, so I think it would be less and less attractive to run
> Next3 instead of ext4... So this doesn't seem like an ideal solution either.

you are not the first to tell me that the fork from ext3 is not a good idea.
I agree that in the long term, Next3 as a file system driver has no place,
but for practical reasons, I needed to create a separate file system driver,
so people will be able to use the new feature without patching ext3 during
the long time it will take me to merge the feature to ext4.


> ?For future, the most promising to me would be to change the
> implementation to work with ext4 and merge it there. I understand there are
> technical issues with this and I'm not sure how hard they would be to
> solve. But for me as a filesystem developper this would seem like a
> direction where it's worth to invest some time and energy and I can help
> with that (and I believe other ext4 developpers might lend a hand as well).
> ?Just my thoughts...

and the first step towards getting the snapshot feature into ext4 is
for some ext3/4
developers to review the patches, so I will have someone (rather some than one)
with whom I can discuss the merge issues.
I actually proposed the next3 merge as a topic for LSF, but that
didn't get much attention.

> Anyway, I've added to my todo an item to have a look
> at your patches so that I have better idea what we are discussing about.

That would be great.
If you like, we can schedule a call after you've gone through some of
the patches/docs.
I have done this with Ted and I think it's a good way to get started.
if you haven't looked at http://sourceforge.net/apps/mediawiki/next3/
that would be a good place to start (also links to the snapshots design paper)

Thanks for taking an interest,
Amir.

2010-07-20 21:11:21

by Greg Freemyer

[permalink] [raw]
Subject: Re: [PATCHES/RFC v1.0.12] e2fsprogs: Next3 patch series

On Tue, Jul 20, 2010 at 4:12 PM, Amir G. <[email protected]> wrote:
> On Tue, Jul 20, 2010 at 6:38 PM, Jan Kara <[email protected]> wrote:
>>> In fact, the posted patches are only the small patches to e2fsprogs.
>>> The more challenging job is the review of the Next3 snapshot patches,
>>> which apply on top of ext3 (or rather a forked branch of ext3 called next3).
>>> They are available for download at
>>> http://sourceforge.net/projects/next3/files/Latest%20patch%20series
>>> but I can also post them to the list if you like (about 40 medium size patches).
>> ?Well, I can have a look at those patches. But I'd like to know what is
>> exactly your motivation - is it that you have some customers running with
>> this clone and want to upstream the fs, or is it that you'd like to
>> contribute the cool feature you've developped, or something else?
>
> contribute cool feature is the best match.
>
>> Because on this depends where we should go from the current situation...
>> ?To state my position: I'm not willing to merge the feature into ext3
>> because it's basically in a maintenance mode so we don't accept larger
>> features to it anymore (for stability reasons).
>
> of course.
>
>> ?You could, of course, copy ext3 code base and create a separate next3
>> filesystem. But such code duplication would be generally frowned upon and I
>> personally wouldn't like to take the burden of maintaining such code so
>> you'd have to do it. Moreover you have to port all ext3 fixes to your code
>> and you have a problem that as time progresses, new features are added to
>> ext4, not ext3, so I think it would be less and less attractive to run
>> Next3 instead of ext4... So this doesn't seem like an ideal solution either.
>
> you are not the first to tell me that the fork from ext3 is not a good idea.
> I agree that in the long term, Next3 as a file system driver has no place,
> but for practical reasons, I needed to create a separate file system driver,
> so people will be able to use the new feature without patching ext3 during
> the long time it will take me to merge the feature to ext4.
>
>
>> ?For future, the most promising to me would be to change the
>> implementation to work with ext4 and merge it there. I understand there are
>> technical issues with this and I'm not sure how hard they would be to
>> solve. But for me as a filesystem developper this would seem like a
>> direction where it's worth to invest some time and energy and I can help
>> with that (and I believe other ext4 developpers might lend a hand as well).
>> ?Just my thoughts...
>
> and the first step towards getting the snapshot feature into ext4 is
> for some ext3/4
> developers to review the patches, so I will have someone (rather some than one)
> with whom I can discuss the merge issues.
> I actually proposed the next3 merge as a topic for LSF, but that
> didn't get much attention.
>
>> Anyway, I've added to my todo an item to have a look
>> at your patches so that I have better idea what we are discussing about.
>
> That would be great.
> If you like, we can schedule a call after you've gone through some of
> the patches/docs.
> I have done this with Ted and I think it's a good way to get started.
> if you haven't looked at http://sourceforge.net/apps/mediawiki/next3/
> that would be a good place to start (also links to the snapshots design paper)
>
> Thanks for taking an interest,
> Amir.

So are you just asking for comments right now, or are you asking that
the e2fsprogs patches get applied?

Maybe a separate e2fsprogs branch for next3 should be created like
there was for the 64-bit patches? (That would be Ted/Jan's call, not
mine.)

Greg

2010-07-21 09:21:18

by Amir G.

[permalink] [raw]
Subject: Re: [PATCHES/RFC v1.0.12] e2fsprogs: Next3 patch series

On Wed, Jul 21, 2010 at 12:11 AM, Greg Freemyer <[email protected]> wrote:
>
> So are you just asking for comments right now, or are you asking that
> the e2fsprogs patches get applied?
>

I am asking that the patches will get applied. comments are always welcome.

I am looking for 2 types of code review:

1. check that the patch looks good in general and doesn't change
behavior when the snapshot feature is disabled.
that kind of review would approve that the patch may be applied to the
master branch.

2. check the behavior of the code when snapshot feature is enabled.
this requires the reviewer to acquire some knowledge about the design
of the snapshot feature.

> Maybe a separate e2fsprogs branch for next3 should be created like
> there was for the 64-bit patches? ?(That would be Ted/Jan's call, not
> mine.)
>

That's not my call, but I do not think that would be necessary.
the next3 e2fsprogs patches are really small and are restricted to a
small number of files.
for that reason, it wasn't so hard for me to rebase them from the
maint branch to the master branch (64bit code).
there is some dependency among the patches, but most of them are small
and independent,
so they don't have to be applied all at once. I also verified that
every sub-series of the patches builds with no errors.

To put it in other words, any review, of any type, of any patch, would
be helpful.
Thanks,
Amir.

2010-07-26 07:38:38

by Amir G.

[permalink] [raw]
Subject: [RESEND PATCH 04/12] e2fsprogs: Next3 snapshot control with chattr/lsattr -X

From: Amir Goldstein <[email protected]>

Set/clear snapshots parent directory with chattr +/-x.
Take/delete snapshot with chattr -X +/-S.
Enable/disable snapshot with chattr -X +/-n.
View snapshot status with lsattr -X.

Signed-off-by: Amir Goldstein <[email protected]>
---
lib/e2p/e2p.h | 18 ++++++++++++++++++
lib/e2p/pf.c | 33 ++++++++++++++++++++++++++++++++-
misc/chattr.c | 38 ++++++++++++++++++++++++++++++++++++--
misc/lsattr.c | 13 ++++++++++---
4 files changed, 96 insertions(+), 6 deletions(-)

diff --git a/lib/e2p/e2p.h b/lib/e2p/e2p.h
index 4a68dd9..1fbbe1b 100644
--- a/lib/e2p/e2p.h
+++ b/lib/e2p/e2p.h
@@ -26,6 +26,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 --git a/lib/e2p/pf.c b/lib/e2p/pf.c
index f34a5cc..063bf2a 100644
--- a/lib/e2p/pf.c
+++ b/lib/e2p/pf.c
@@ -48,16 +48,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 --git a/misc/chattr.c b/misc/chattr.c
index de33b08..449101f 100644
--- a/misc/chattr.c
+++ b/misc/chattr.c
@@ -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_array[] = {
{ 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, char ** argv)
{
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 --git a/misc/lsattr.c b/misc/lsattr.c
index 15b17ad..3885628 100644
--- a/misc/lsattr.c
+++ b/misc/lsattr.c
@@ -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;
--
1.6.6