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.
+.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.
+.TP
.BI ea_ver= extended_attribute_version
Set the version of the extended attribute blocks which
.B e2fsck
the test_fs flag if the ext4 file system 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
file system checks (either based on time or number of mounts) should
#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
*/
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 casefolded_dirs;
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;
fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
continue;
}
- if ((ctx->options & E2F_OPT_UNSHARE_BLOCKS) ||
- fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
+ if (ctx->shared != E2F_SHARED_DELETE &&
+ ((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)
+ 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
{
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);
if (!cs->save_dup_cluster)
return;
decrement_badcount(cs->ctx, cs->save_blocknr, cs->save_dup_cluster);
+ if (cs->ctx->clone == E2F_CLONE_ZERO &&
+ cs->save_dup_cluster->num_bad == 0) {
+ ext2fs_unmark_block_bitmap2(cs->ctx->block_found_map,
+ cs->save_blocknr);
+ ext2fs_block_alloc_stats(cs->ctx->fs, cs->save_blocknr, -1);
+ }
cs->save_dup_cluster = NULL;
}
blockcnt, (unsigned long long) *block_nr,
(unsigned long long) 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);
/* xgettext:no-c-format */
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 */
/* 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
*/
}
#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;
} 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++;
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);
profile_set_syntax_err_cb(syntax_err_report);
profile_init(config_fn, &ctx->profile);
+ initialize_profile_options(ctx);
+
phys_mem_kb = get_memory_size() / 1024;
ctx->readahead_kb = ~0ULL;