@@ -198,6 +198,21 @@ separated, and may take an argument using the equals ('=') sign. The
following options are supported:
.RS 1.2i
.TP
+.BI clone= dup|zero
+Resolve files with shared blocks in pass 1D by giving each file a private
+copy of the blocks (dup);
+or replacing the shared blocks with private, zero-filled blocks (zero).
+The default is dup. zero option is incompatible with -E unshare_blocks
+option.
+.TP
+.BI shared= preserve|lost+found|delete
+Files with shared blocks discovered in pass 1D are cloned and then left
+in place (preserve);
+cloned and then disconnected from their parent directory,
+then reconnected to /lost+found in pass 3 (lost+found);
+or simply deleted (delete). The default is preserve.
+lost+found and delete are incompatible with -E unshare_blocks option.
+.TP
.BI ea_ver= extended_attribute_version
Set the version of the extended attribute blocks which
.B e2fsck
@@ -147,6 +147,19 @@ will offer to clear
the test_fs flag if the ext4 filesystem is available on the system. It
defaults to true.
.TP
+.I clone
+This string relation controls the default handling of shared blocks in pass 1D.
+It can be set to dup or zero. See the
+.I "-E clone"
+option description in e2fsck(8).
+.TP
+.I shared
+This string relation controls the default disposition of files discovered to
+have shared blocks in pass 1D. It can be set to preserve, lost+found,
+or delete. See the
+.I "-E shared"
+option description in e2fsck(8).
+.TP
.I defer_check_on_battery
This boolean relation controls whether or not the interval between
filesystem checks (either based on time or number of mounts) should
@@ -211,6 +211,17 @@ struct resource_track {
#define E2F_PASS_5 5
#define E2F_PASS_1B 6
+enum shared_opt {
+ E2F_SHARED_PRESERVE = 0,
+ E2F_SHARED_DELETE,
+ E2F_SHARED_LPF
+};
+
+enum clone_opt {
+ E2F_CLONE_DUP = 0,
+ E2F_CLONE_ZERO
+};
+
/*
* Define the extended attribute refcount structure
*/
@@ -387,6 +398,8 @@ struct e2fsck_struct {
time_t now;
time_t time_fudge; /* For working around buggy init scripts */
int ext_attr_ver;
+ enum shared_opt shared;
+ enum clone_opt clone;
profile_t profile;
int blocks_per_page;
ext2_u32_list encrypted_dirs;
@@ -541,6 +541,9 @@ static void pass1d(e2fsck_t ctx, char *block_buf)
q = (struct dup_cluster *) dnode_get(m);
if (q->num_bad > 1)
file_ok = 0;
+ if (q->num_bad == 1 && (ctx->clone == E2F_CLONE_ZERO ||
+ ctx->shared != E2F_SHARED_PRESERVE))
+ file_ok = 0;
if (check_if_fs_cluster(ctx, s->cluster)) {
file_ok = 0;
meta_data = 1;
@@ -601,13 +604,26 @@ static void pass1d(e2fsck_t ctx, char *block_buf)
continue;
}
if ((ctx->options & E2F_OPT_UNSHARE_BLOCKS) ||
- fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
+ (ctx->shared != E2F_SHARED_DELETE &&
+ fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx))) {
pctx.errcode = clone_file(ctx, ino, p, block_buf);
- if (pctx.errcode)
+ if (pctx.errcode) {
fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
- else
- continue;
+ goto delete;
+ }
+ if (ctx->shared == E2F_SHARED_LPF &&
+ fix_problem(ctx, PR_1D_DISCONNECT_QUESTION, &pctx)){
+ pctx.errcode = ext2fs_unlink(fs, p->dir,
+ NULL, ino, 0);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1D_DISCONNECT_ERROR,
+ &pctx);
+ goto delete;
+ }
+ }
+ continue;
}
+delete:
/*
* Note: When unsharing blocks, we don't prompt to delete
* files. If the clone operation fails than the unshare
@@ -631,7 +647,8 @@ static void decrement_badcount(e2fsck_t ctx, blk64_t block,
{
p->num_bad--;
if (p->num_bad <= 0 ||
- (p->num_bad == 1 && !check_if_fs_block(ctx, block))) {
+ (p->num_bad == 1 && !check_if_fs_block(ctx, block) &&
+ ctx->clone == E2F_CLONE_DUP)) {
if (check_if_fs_cluster(ctx, EXT2FS_B2C(ctx->fs, block)))
return;
ext2fs_unmark_block_bitmap2(ctx->block_dup_map, block);
@@ -830,6 +847,14 @@ static int clone_file_block(ext2_filsys fs,
p = (struct dup_cluster *) dnode_get(n);
+ if (!is_meta) {
+ if (ctx->clone == E2F_CLONE_ZERO && p->num_bad == 0) {
+ ext2fs_unmark_block_bitmap2(ctx->block_found_map,
+ *block_nr);
+ ext2fs_block_alloc_stats(fs, *block_nr, -1);
+ }
+ }
+
cs->dup_cluster = c;
/*
* Let's try an implied cluster allocation. If we get the same
@@ -876,10 +901,15 @@ cluster_alloc_ok:
printf("Cloning block #%lld from %llu to %llu\n",
blockcnt, *block_nr, new_block);
#endif
- retval = io_channel_read_blk64(fs->io, *block_nr, 1, cs->buf);
- if (retval) {
- cs->errcode = retval;
- return BLOCK_ABORT;
+ if (ctx->clone == E2F_CLONE_ZERO) {
+ memset(cs->buf, 0, fs->blocksize);
+ } else {
+ retval = io_channel_read_blk64(fs->io, *block_nr, 1,
+ cs->buf);
+ if (retval) {
+ cs->errcode = retval;
+ return BLOCK_ABORT;
+ }
}
if (should_write) {
retval = io_channel_write_blk64(fs->io, new_block, 1, cs->buf);
@@ -1281,6 +1281,14 @@ static struct e2fsck_problem problem_table[] = {
{ PR_1D_CLONE_ERROR,
N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0, 0, 0, 0 },
+ /* File with shared blocks found */
+ { PR_1D_DISCONNECT_QUESTION,
+ N_("File with shared blocks found\n"), PROMPT_CONNECT, 0 },
+
+ /* Couldn't unlink file (error) */
+ { PR_1D_DISCONNECT_ERROR,
+ N_("Couldn't unlink file: %m\n"), PROMPT_NONE, 0 },
+
/* Pass 1E Extent tree optimization */
/* Pass 1E: Optimizing extent trees */
@@ -756,6 +756,13 @@ struct problem_context {
/* Couldn't clone file (error) */
#define PR_1D_CLONE_ERROR 0x013008
+/* File with shared blocks found */
+#define PR_1D_DISCONNECT_QUESTION 0x013009
+
+/* Couldn't unlink file (error) */
+#define PR_1D_DISCONNECT_ERROR 0x01300A
+
+
/*
* Pass 1e --- rebuilding extent trees
*/
@@ -657,6 +657,49 @@ static void signal_cancel(int sig EXT2FS_ATTR((unused)))
}
#endif
+static void initialize_profile_options(e2fsck_t ctx)
+{
+ char *tmp;
+
+ /* [options] shared=preserve|lost+found|delete */
+ tmp = NULL;
+ ctx->shared = E2F_SHARED_PRESERVE;
+ profile_get_string(ctx->profile, "options", "shared", 0,
+ "preserve", &tmp);
+ if (tmp) {
+ if (strcmp(tmp, "preserve") == 0)
+ ctx->shared = E2F_SHARED_PRESERVE;
+ else if (strcmp(tmp, "delete") == 0)
+ ctx->shared = E2F_SHARED_DELETE;
+ else if (strcmp(tmp, "lost+found") == 0)
+ ctx->shared = E2F_SHARED_LPF;
+ else {
+ com_err(ctx->program_name, 0,
+ _("configuration error: 'shared=%s'"), tmp);
+ fatal_error(ctx, 0);
+ }
+ free(tmp);
+ }
+
+ /* [options] clone=dup|zero */
+ tmp = NULL;
+ ctx->clone = E2F_CLONE_DUP;
+ profile_get_string(ctx->profile, "options", "clone", 0,
+ "dup", &tmp);
+ if (tmp) {
+ if (strcmp(tmp, "dup") == 0)
+ ctx->clone = E2F_CLONE_DUP;
+ else if (strcmp(tmp, "zero") == 0)
+ ctx->clone = E2F_CLONE_ZERO;
+ else {
+ com_err(ctx->program_name, 0,
+ _("configuration error: 'clone=%s'"), tmp);
+ fatal_error(ctx, 0);
+ }
+ free(tmp);
+ }
+}
+
static void parse_extended_opts(e2fsck_t ctx, const char *opts)
{
char *buf, *token, *next, *p, *arg;
@@ -707,6 +750,36 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
} else if (strcmp(token, "fragcheck") == 0) {
ctx->options |= E2F_OPT_FRAGCHECK;
continue;
+ /* -E shared=preserve|lost+found|delete */
+ } else if (strcmp(token, "shared") == 0) {
+ if (!arg) {
+ extended_usage++;
+ continue;
+ }
+ if (strcmp(arg, "preserve") == 0) {
+ ctx->shared = E2F_SHARED_PRESERVE;
+ } else if (strcmp(arg, "lost+found") == 0) {
+ ctx->shared = E2F_SHARED_LPF;
+ } else if (strcmp(arg, "delete") == 0) {
+ ctx->shared = E2F_SHARED_DELETE;
+ } else {
+ extended_usage++;
+ continue;
+ }
+ /* -E clone=dup|zero */
+ } else if (strcmp(token, "clone") == 0) {
+ if (!arg) {
+ extended_usage++;
+ continue;
+ }
+ if (strcmp(arg, "dup") == 0) {
+ ctx->clone = E2F_CLONE_DUP;
+ } else if (strcmp(arg, "zero") == 0) {
+ ctx->clone = E2F_CLONE_ZERO;
+ } else {
+ extended_usage++;
+ continue;
+ }
} else if (strcmp(token, "journal_only") == 0) {
if (arg) {
extended_usage++;
@@ -771,6 +844,8 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
fputs("\tjournal_only\n", stderr);
fputs("\tdiscard\n", stderr);
fputs("\tnodiscard\n", stderr);
+ fputs(_("\tshared=<preserve|lost+found|delete>\n"), stderr);
+ fputs(_("\tclone=<dup|zero>\n"), stderr);
fputs("\toptimize_extents\n", stderr);
fputs("\tno_optimize_extents\n", stderr);
fputs("\tinode_count_fullmap\n", stderr);
@@ -840,6 +915,8 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
else
ctx->program_name = "e2fsck";
+ initialize_profile_options(ctx);
+
phys_mem_kb = get_memory_size() / 1024;
ctx->readahead_kb = ~0ULL;
while ((c = getopt(argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkz:")) != EOF)
@@ -1032,6 +1109,15 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
fatal_error(ctx, 0);
}
+ if ((ctx->options & E2F_OPT_UNSHARE_BLOCKS) &&
+ (ctx->clone != E2F_CLONE_DUP ||
+ ctx->shared != E2F_SHARED_PRESERVE)) {
+ com_err(ctx->program_name, 0, "%s",
+ _("The -E unshare_blocks and specified clone/shared "
+ "options are incompatible"));
+ fatal_error(ctx, 0);
+ }
+
if ((cp = getenv("E2FSCK_CONFIG")) != NULL)
config_fn[0] = cp;
profile_set_syntax_err_cb(syntax_err_report);