Merge "e2fsck: Add an extended option for unsharing blocks."
diff --git a/e2fsck/e2fsck.8.in b/e2fsck/e2fsck.8.in
index 915273d..18d0f4b 100644
--- a/e2fsck/e2fsck.8.in
+++ b/e2fsck/e2fsck.8.in
@@ -240,6 +240,15 @@
Only fix damaged metadata; do not optimize htree directories or compress
extent trees. This option is incompatible with the -D and -E bmap2extent
options.
+.TP
+.BI unshare_blocks
+If the filesystem has shared blocks, with the shared blocks read-only feature
+enabled, then this will unshare all shared blocks and unset the read-only
+feature bit. If there is not enough free space then the operation will fail.
+If the filesystem does not have the read-only feature bit, but has shared
+blocks anyway, then this option will have no effect. Note when using this
+option, if there is no free space to clone blocks, there is no prompt to
+delete files and instead the operation will fail.
.RE
.TP
.B \-f
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index f356810..52833e6 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -153,22 +153,23 @@
/*
* E2fsck options
*/
-#define E2F_OPT_READONLY 0x0001
-#define E2F_OPT_PREEN 0x0002
-#define E2F_OPT_YES 0x0004
-#define E2F_OPT_NO 0x0008
-#define E2F_OPT_TIME 0x0010
-#define E2F_OPT_TIME2 0x0020
-#define E2F_OPT_CHECKBLOCKS 0x0040
-#define E2F_OPT_DEBUG 0x0080
-#define E2F_OPT_FORCE 0x0100
-#define E2F_OPT_WRITECHECK 0x0200
-#define E2F_OPT_COMPRESS_DIRS 0x0400
-#define E2F_OPT_FRAGCHECK 0x0800
-#define E2F_OPT_JOURNAL_ONLY 0x1000 /* only replay the journal */
-#define E2F_OPT_DISCARD 0x2000
-#define E2F_OPT_CONVERT_BMAP 0x4000 /* convert blockmap to extent */
-#define E2F_OPT_FIXES_ONLY 0x8000 /* skip all optimizations */
+#define E2F_OPT_READONLY 0x00001
+#define E2F_OPT_PREEN 0x00002
+#define E2F_OPT_YES 0x00004
+#define E2F_OPT_NO 0x00008
+#define E2F_OPT_TIME 0x00010
+#define E2F_OPT_TIME2 0x00020
+#define E2F_OPT_CHECKBLOCKS 0x00040
+#define E2F_OPT_DEBUG 0x00080
+#define E2F_OPT_FORCE 0x00100
+#define E2F_OPT_WRITECHECK 0x00200
+#define E2F_OPT_COMPRESS_DIRS 0x00400
+#define E2F_OPT_FRAGCHECK 0x00800
+#define E2F_OPT_JOURNAL_ONLY 0x01000 /* only replay the journal */
+#define E2F_OPT_DISCARD 0x02000
+#define E2F_OPT_CONVERT_BMAP 0x04000 /* convert blockmap to extent */
+#define E2F_OPT_FIXES_ONLY 0x08000 /* skip all optimizations */
+#define E2F_OPT_UNSHARE_BLOCKS 0x10000
/*
* E2fsck flags
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index c5fdbf7..1d34f9a 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -2131,8 +2131,10 @@
clear_problem_context(&pctx);
if (ext2fs_fast_test_block_bitmap2(ctx->block_found_map, block)) {
- if (ext2fs_has_feature_shared_blocks(ctx->fs->super))
+ if (ext2fs_has_feature_shared_blocks(ctx->fs->super) &&
+ !(ctx->options & E2F_OPT_UNSHARE_BLOCKS)) {
return;
+ }
if (!ctx->block_dup_map) {
pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
_("multiply claimed block map"),
diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c
index b40f026..eb46415 100644
--- a/e2fsck/pass1b.c
+++ b/e2fsck/pass1b.c
@@ -245,6 +245,24 @@
pass1d(ctx, block_buf);
print_resource_track(ctx, "Pass 1d", &rtrack, ctx->fs->io);
+ if (ext2fs_has_feature_shared_blocks(ctx->fs->super) &&
+ (ctx->options & E2F_OPT_UNSHARE_BLOCKS)) {
+ /*
+ * If we successfully managed to unshare all blocks, unset the
+ * shared block feature.
+ */
+ blk64_t next;
+ int result = ext2fs_find_first_set_block_bitmap2(
+ ctx->block_dup_map,
+ ctx->fs->super->s_first_data_block,
+ ext2fs_blocks_count(ctx->fs->super) - 1,
+ &next);
+ if (result == ENOENT) {
+ ext2fs_clear_feature_shared_blocks(ctx->fs->super);
+ ext2fs_mark_super_dirty(ctx->fs);
+ }
+ }
+
/*
* Time to free all of the accumulated data structures that we
* don't need anymore.
@@ -582,14 +600,21 @@
fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
continue;
}
- if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
+ if ((ctx->options & E2F_OPT_UNSHARE_BLOCKS) ||
+ fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
pctx.errcode = clone_file(ctx, ino, p, block_buf);
if (pctx.errcode)
fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
else
continue;
}
- if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
+ /*
+ * Note: When unsharing blocks, we don't prompt to delete
+ * files. If the clone operation fails than the unshare
+ * operation should fail too.
+ */
+ if (!(ctx->options & E2F_OPT_UNSHARE_BLOCKS) &&
+ fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
delete_file(ctx, ino, p, block_buf);
else
ext2fs_unmark_valid(fs);
@@ -818,6 +843,13 @@
cs->errcode = retval;
return BLOCK_ABORT;
}
+ if (ext2fs_has_feature_shared_blocks(fs->super)) {
+ /*
+ * Update the block stats so we don't get a prompt to fix block
+ * counts in the final pass.
+ */
+ ext2fs_block_alloc_stats2(fs, new_block, +1);
+ }
cluster_alloc_ok:
cs->alloc_block = new_block;
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 6029cc3..ee27481 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -721,6 +721,9 @@
} else if (strcmp(token, "fixes_only") == 0) {
ctx->options |= E2F_OPT_FIXES_ONLY;
continue;
+ } else if (strcmp(token, "unshare_blocks") == 0) {
+ ctx->options |= E2F_OPT_UNSHARE_BLOCKS;
+ continue;
} else {
fprintf(stderr, _("Unknown extended option: %s\n"),
token);
@@ -741,6 +744,7 @@
fputs(("\tnodiscard\n"), stderr);
fputs(("\treadahead_kb=<buffer size>\n"), stderr);
fputs(("\tbmap2extent\n"), stderr);
+ fputs(("\tunshare_blocks\n"), stderr);
fputc('\n', stderr);
exit(1);
}
diff --git a/tests/f_unshare_blocks_no_space/expect.1 b/tests/f_unshare_blocks_no_space/expect.1
new file mode 100644
index 0000000..b2f6ab1
--- /dev/null
+++ b/tests/f_unshare_blocks_no_space/expect.1
@@ -0,0 +1,136 @@
+Pass 1: Checking inodes, blocks, and sizes
+
+Running additional passes to resolve blocks claimed by more than one inode...
+Pass 1B: Rescanning for multiply-claimed blocks
+Multiply-claimed block(s) in inode 24: 10
+Multiply-claimed block(s) in inode 25: 9 9 9--10
+Multiply-claimed block(s) in inode 26: 9 9 9--10
+Multiply-claimed block(s) in inode 27: 9 9 9--10
+Multiply-claimed block(s) in inode 28: 9 9 9--10
+Multiply-claimed block(s) in inode 29: 9 9 9--10
+Multiply-claimed block(s) in inode 30: 9 9 9--10
+Multiply-claimed block(s) in inode 31: 9 9 9--10
+Multiply-claimed block(s) in inode 32: 9 9 9--10
+Pass 1C: Scanning directories for inodes with multiply-claimed blocks
+Pass 1D: Reconciling multiply-claimed blocks
+(There are 9 inodes containing multiply-claimed blocks.)
+
+File /file4.txt (inode #24, mod time Mon Mar 5 20:30:04 2018)
+ has 1 multiply-claimed block(s), shared with 8 file(s):
+ /file18.txt (inode #32, mod time Mon Mar 5 20:30:04 2018)
+ /file6.txt (inode #31, mod time Mon Mar 5 20:30:04 2018)
+ /file12.txt (inode #30, mod time Mon Mar 5 20:30:04 2018)
+ /file3.txt (inode #29, mod time Mon Mar 5 20:30:04 2018)
+ /file9.txt (inode #28, mod time Mon Mar 5 20:30:04 2018)
+ /file8.txt (inode #27, mod time Mon Mar 5 20:30:04 2018)
+ /file15.txt (inode #26, mod time Mon Mar 5 20:30:04 2018)
+ /file20.txt (inode #25, mod time Mon Mar 5 20:30:04 2018)
+clone_file: Could not allocate block in ext2 filesystem returned from clone_file_block
+Couldn't clone file: Could not allocate block in ext2 filesystem
+File /file20.txt (inode #25, mod time Mon Mar 5 20:30:04 2018)
+ has 4 multiply-claimed block(s), shared with 8 file(s):
+ /file18.txt (inode #32, mod time Mon Mar 5 20:30:04 2018)
+ /file6.txt (inode #31, mod time Mon Mar 5 20:30:04 2018)
+ /file12.txt (inode #30, mod time Mon Mar 5 20:30:04 2018)
+ /file3.txt (inode #29, mod time Mon Mar 5 20:30:04 2018)
+ /file9.txt (inode #28, mod time Mon Mar 5 20:30:04 2018)
+ /file8.txt (inode #27, mod time Mon Mar 5 20:30:04 2018)
+ /file15.txt (inode #26, mod time Mon Mar 5 20:30:04 2018)
+ /file4.txt (inode #24, mod time Mon Mar 5 20:30:04 2018)
+clone_file: Could not allocate block in ext2 filesystem returned from clone_file_block
+Couldn't clone file: Could not allocate block in ext2 filesystem
+File /file15.txt (inode #26, mod time Mon Mar 5 20:30:04 2018)
+ has 4 multiply-claimed block(s), shared with 8 file(s):
+ /file18.txt (inode #32, mod time Mon Mar 5 20:30:04 2018)
+ /file6.txt (inode #31, mod time Mon Mar 5 20:30:04 2018)
+ /file12.txt (inode #30, mod time Mon Mar 5 20:30:04 2018)
+ /file3.txt (inode #29, mod time Mon Mar 5 20:30:04 2018)
+ /file9.txt (inode #28, mod time Mon Mar 5 20:30:04 2018)
+ /file8.txt (inode #27, mod time Mon Mar 5 20:30:04 2018)
+ /file20.txt (inode #25, mod time Mon Mar 5 20:30:04 2018)
+ /file4.txt (inode #24, mod time Mon Mar 5 20:30:04 2018)
+clone_file: Could not allocate block in ext2 filesystem returned from clone_file_block
+Couldn't clone file: Could not allocate block in ext2 filesystem
+File /file8.txt (inode #27, mod time Mon Mar 5 20:30:04 2018)
+ has 4 multiply-claimed block(s), shared with 8 file(s):
+ /file18.txt (inode #32, mod time Mon Mar 5 20:30:04 2018)
+ /file6.txt (inode #31, mod time Mon Mar 5 20:30:04 2018)
+ /file12.txt (inode #30, mod time Mon Mar 5 20:30:04 2018)
+ /file3.txt (inode #29, mod time Mon Mar 5 20:30:04 2018)
+ /file9.txt (inode #28, mod time Mon Mar 5 20:30:04 2018)
+ /file15.txt (inode #26, mod time Mon Mar 5 20:30:04 2018)
+ /file20.txt (inode #25, mod time Mon Mar 5 20:30:04 2018)
+ /file4.txt (inode #24, mod time Mon Mar 5 20:30:04 2018)
+clone_file: Could not allocate block in ext2 filesystem returned from clone_file_block
+Couldn't clone file: Could not allocate block in ext2 filesystem
+File /file9.txt (inode #28, mod time Mon Mar 5 20:30:04 2018)
+ has 4 multiply-claimed block(s), shared with 8 file(s):
+ /file18.txt (inode #32, mod time Mon Mar 5 20:30:04 2018)
+ /file6.txt (inode #31, mod time Mon Mar 5 20:30:04 2018)
+ /file12.txt (inode #30, mod time Mon Mar 5 20:30:04 2018)
+ /file3.txt (inode #29, mod time Mon Mar 5 20:30:04 2018)
+ /file8.txt (inode #27, mod time Mon Mar 5 20:30:04 2018)
+ /file15.txt (inode #26, mod time Mon Mar 5 20:30:04 2018)
+ /file20.txt (inode #25, mod time Mon Mar 5 20:30:04 2018)
+ /file4.txt (inode #24, mod time Mon Mar 5 20:30:04 2018)
+clone_file: Could not allocate block in ext2 filesystem returned from clone_file_block
+Couldn't clone file: Could not allocate block in ext2 filesystem
+File /file3.txt (inode #29, mod time Mon Mar 5 20:30:04 2018)
+ has 4 multiply-claimed block(s), shared with 8 file(s):
+ /file18.txt (inode #32, mod time Mon Mar 5 20:30:04 2018)
+ /file6.txt (inode #31, mod time Mon Mar 5 20:30:04 2018)
+ /file12.txt (inode #30, mod time Mon Mar 5 20:30:04 2018)
+ /file9.txt (inode #28, mod time Mon Mar 5 20:30:04 2018)
+ /file8.txt (inode #27, mod time Mon Mar 5 20:30:04 2018)
+ /file15.txt (inode #26, mod time Mon Mar 5 20:30:04 2018)
+ /file20.txt (inode #25, mod time Mon Mar 5 20:30:04 2018)
+ /file4.txt (inode #24, mod time Mon Mar 5 20:30:04 2018)
+clone_file: Could not allocate block in ext2 filesystem returned from clone_file_block
+Couldn't clone file: Could not allocate block in ext2 filesystem
+File /file12.txt (inode #30, mod time Mon Mar 5 20:30:04 2018)
+ has 4 multiply-claimed block(s), shared with 8 file(s):
+ /file18.txt (inode #32, mod time Mon Mar 5 20:30:04 2018)
+ /file6.txt (inode #31, mod time Mon Mar 5 20:30:04 2018)
+ /file3.txt (inode #29, mod time Mon Mar 5 20:30:04 2018)
+ /file9.txt (inode #28, mod time Mon Mar 5 20:30:04 2018)
+ /file8.txt (inode #27, mod time Mon Mar 5 20:30:04 2018)
+ /file15.txt (inode #26, mod time Mon Mar 5 20:30:04 2018)
+ /file20.txt (inode #25, mod time Mon Mar 5 20:30:04 2018)
+ /file4.txt (inode #24, mod time Mon Mar 5 20:30:04 2018)
+clone_file: Could not allocate block in ext2 filesystem returned from clone_file_block
+Couldn't clone file: Could not allocate block in ext2 filesystem
+File /file6.txt (inode #31, mod time Mon Mar 5 20:30:04 2018)
+ has 4 multiply-claimed block(s), shared with 8 file(s):
+ /file18.txt (inode #32, mod time Mon Mar 5 20:30:04 2018)
+ /file12.txt (inode #30, mod time Mon Mar 5 20:30:04 2018)
+ /file3.txt (inode #29, mod time Mon Mar 5 20:30:04 2018)
+ /file9.txt (inode #28, mod time Mon Mar 5 20:30:04 2018)
+ /file8.txt (inode #27, mod time Mon Mar 5 20:30:04 2018)
+ /file15.txt (inode #26, mod time Mon Mar 5 20:30:04 2018)
+ /file20.txt (inode #25, mod time Mon Mar 5 20:30:04 2018)
+ /file4.txt (inode #24, mod time Mon Mar 5 20:30:04 2018)
+clone_file: Could not allocate block in ext2 filesystem returned from clone_file_block
+Couldn't clone file: Could not allocate block in ext2 filesystem
+File /file18.txt (inode #32, mod time Mon Mar 5 20:30:04 2018)
+ has 4 multiply-claimed block(s), shared with 8 file(s):
+ /file6.txt (inode #31, mod time Mon Mar 5 20:30:04 2018)
+ /file12.txt (inode #30, mod time Mon Mar 5 20:30:04 2018)
+ /file3.txt (inode #29, mod time Mon Mar 5 20:30:04 2018)
+ /file9.txt (inode #28, mod time Mon Mar 5 20:30:04 2018)
+ /file8.txt (inode #27, mod time Mon Mar 5 20:30:04 2018)
+ /file15.txt (inode #26, mod time Mon Mar 5 20:30:04 2018)
+ /file20.txt (inode #25, mod time Mon Mar 5 20:30:04 2018)
+ /file4.txt (inode #24, mod time Mon Mar 5 20:30:04 2018)
+clone_file: Could not allocate block in ext2 filesystem returned from clone_file_block
+Couldn't clone file: Could not allocate block in ext2 filesystem
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+
+test_filesys: ********** WARNING: Filesystem still has errors **********
+
+test_filesys: 32/32 files (34.4% non-contiguous), 64/64 blocks
+Exit status is 4
diff --git a/tests/f_unshare_blocks_no_space/expect.2 b/tests/f_unshare_blocks_no_space/expect.2
new file mode 100644
index 0000000..8137dc7
--- /dev/null
+++ b/tests/f_unshare_blocks_no_space/expect.2
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 32/32 files (34.4% non-contiguous), 64/64 blocks
+Exit status is 1
diff --git a/tests/f_unshare_blocks_no_space/image.gz b/tests/f_unshare_blocks_no_space/image.gz
new file mode 100644
index 0000000..8fff6d4
--- /dev/null
+++ b/tests/f_unshare_blocks_no_space/image.gz
Binary files differ
diff --git a/tests/f_unshare_blocks_no_space/name b/tests/f_unshare_blocks_no_space/name
new file mode 100644
index 0000000..ca323a6
--- /dev/null
+++ b/tests/f_unshare_blocks_no_space/name
@@ -0,0 +1 @@
+unshare blocks should fail with no free space
diff --git a/tests/f_unshare_blocks_no_space/script b/tests/f_unshare_blocks_no_space/script
new file mode 100644
index 0000000..bc44354
--- /dev/null
+++ b/tests/f_unshare_blocks_no_space/script
@@ -0,0 +1,2 @@
+FSCK_OPT="-yf -E unshare_blocks"
+. $cmd_dir/run_e2fsck
diff --git a/tests/f_unshare_blocks_ok/expect.1 b/tests/f_unshare_blocks_ok/expect.1
new file mode 100644
index 0000000..e0ea764
--- /dev/null
+++ b/tests/f_unshare_blocks_ok/expect.1
@@ -0,0 +1,26 @@
+Pass 1: Checking inodes, blocks, and sizes
+
+Running additional passes to resolve blocks claimed by more than one inode...
+Pass 1B: Rescanning for multiply-claimed blocks
+Multiply-claimed block(s) in inode 12: 9
+Multiply-claimed block(s) in inode 13: 9
+Pass 1C: Scanning directories for inodes with multiply-claimed blocks
+Pass 1D: Reconciling multiply-claimed blocks
+(There are 2 inodes containing multiply-claimed blocks.)
+
+File /file2.txt (inode #12, mod time Sat Mar 3 02:12:33 2018)
+ has 1 multiply-claimed block(s), shared with 1 file(s):
+ /file1.txt (inode #13, mod time Sat Mar 3 02:12:15 2018)
+File /file1.txt (inode #13, mod time Sat Mar 3 02:12:15 2018)
+ has 1 multiply-claimed block(s), shared with 1 file(s):
+ /file2.txt (inode #12, mod time Sat Mar 3 02:12:33 2018)
+Multiply-claimed blocks already reassigned or cloned.
+
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 13/32 files (0.0% non-contiguous), 13/64 blocks
+Exit status is 0
diff --git a/tests/f_unshare_blocks_ok/expect.2 b/tests/f_unshare_blocks_ok/expect.2
new file mode 100644
index 0000000..b215382
--- /dev/null
+++ b/tests/f_unshare_blocks_ok/expect.2
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 13/32 files (0.0% non-contiguous), 13/64 blocks
+Exit status is 0
diff --git a/tests/f_unshare_blocks_ok/image.gz b/tests/f_unshare_blocks_ok/image.gz
new file mode 100644
index 0000000..db747e2
--- /dev/null
+++ b/tests/f_unshare_blocks_ok/image.gz
Binary files differ
diff --git a/tests/f_unshare_blocks_ok/name b/tests/f_unshare_blocks_ok/name
new file mode 100644
index 0000000..e051a62
--- /dev/null
+++ b/tests/f_unshare_blocks_ok/name
@@ -0,0 +1 @@
+unshare blocks successfully
diff --git a/tests/f_unshare_blocks_ok/script b/tests/f_unshare_blocks_ok/script
new file mode 100644
index 0000000..bc44354
--- /dev/null
+++ b/tests/f_unshare_blocks_ok/script
@@ -0,0 +1,2 @@
+FSCK_OPT="-yf -E unshare_blocks"
+. $cmd_dir/run_e2fsck