@@ -180,6 +180,19 @@ 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.
+.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
@@ -129,6 +129,19 @@ This boolean relation controls whether or not
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
@@ -192,6 +192,17 @@ struct resource_track {
#define E2F_PASS_5 5
#define E2F_PASS_1B 6
+typedef enum {
+ E2F_SHARED_PRESERVE = 0,
+ E2F_SHARED_DELETE,
+ E2F_SHARED_LPF
+} shared_opt_t;
+
+typedef enum {
+ E2F_CLONE_DUP = 0,
+ E2F_CLONE_ZERO
+} clone_opt_t;
+
/*
* Define the extended attribute refcount structure
*/
@@ -350,6 +361,8 @@ struct e2fsck_struct {
int ext_attr_ver;
profile_t profile;
int blocks_per_page;
+ shared_opt_t shared;
+ clone_opt_t clone;
/*
* For the use of callers of the e2fsck functions; not used by
@@ -63,6 +63,11 @@ struct inode_el {
struct inode_el *next;
};
+struct dir_el {
+ ext2_ino_t dir;
+ struct dir_el *next;
+};
+
struct dup_block {
int num_bad;
struct inode_el *inode_list;
@@ -76,10 +81,10 @@ struct dup_block {
* of multiply-claimed blocks.
*/
struct dup_inode {
- ext2_ino_t dir;
int num_dupblocks;
struct ext2_inode inode;
struct block_el *block_list;
+ struct dir_el *dir_list;
};
static int process_pass1b_block(ext2_filsys fs, blk64_t *blocknr,
@@ -123,6 +128,7 @@ static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk64_t blk,
struct dup_inode *di;
struct block_el *blk_el;
struct inode_el *ino_el;
+ struct dir_el *dir_el;
n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
if (n)
@@ -147,11 +153,17 @@ static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk64_t blk,
else {
di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
sizeof(struct dup_inode), "duplicate inode header");
+ di->dir_list = NULL;
if (ino == EXT2_ROOT_INO) {
- di->dir = EXT2_ROOT_INO;
+ dir_el = (struct dir_el *) e2fsck_allocate_memory(ctx,
+ sizeof(struct dir_el),
+ "duplicate inode element");
+ dir_el->dir = EXT2_ROOT_INO;
+ dir_el->next = di->dir_list;
+ di->dir_list = dir_el;
dup_inode_founddir++;
} else
- di->dir = 0;
+ di->dir_list = NULL;
di->num_dupblocks = 0;
di->block_list = 0;
@@ -174,12 +186,17 @@ static void inode_dnode_free(dnode_t *node,
{
struct dup_inode *di;
struct block_el *p, *next;
+ struct dir_el *dp, *dnext;
di = (struct dup_inode *) dnode_get(node);
for (p = di->block_list; p; p = next) {
next = p->next;
free(p);
}
+ for (dp = di->dir_list; dp; dp = dnext) {
+ dnext = dp->next;
+ free(dp);
+ }
free(di);
free(node);
}
@@ -371,6 +388,8 @@ struct search_dir_struct {
int count;
ext2_ino_t first_inode;
ext2_ino_t max_inode;
+ e2fsck_t ctx;
+ int allflag;
};
static int search_dirent_proc(ext2_ino_t dir, int entry,
@@ -382,6 +401,7 @@ static int search_dirent_proc(ext2_ino_t dir, int entry,
{
struct search_dir_struct *sd;
struct dup_inode *p;
+ struct dir_el *dd_el;
dnode_t *n;
sd = (struct search_dir_struct *) priv_data;
@@ -398,12 +418,16 @@ static int search_dirent_proc(ext2_ino_t dir, int entry,
if (!n)
return 0;
p = (struct dup_inode *) dnode_get(n);
- if (!p->dir) {
- p->dir = dir;
+ if (sd->allflag || !p->dir_list) {
+ dd_el = (struct dir_el *) e2fsck_allocate_memory(sd->ctx,
+ sizeof(struct dir_el), "duplicate directory element");
+ dd_el->dir = dir;
+ dd_el->next = p->dir_list;
+ p->dir_list = dd_el;
sd->count--;
}
- return(sd->count ? 0 : DIRENT_ABORT);
+ return((sd->allflag || sd->count) ? 0 : DIRENT_ABORT);
}
@@ -420,15 +444,30 @@ static void pass1c(e2fsck_t ctx, char *block_buf)
/*
* Search through all directories to translate inodes to names
- * (by searching for the containing directory for that inode.)
+ * (by searching for the containing directories for that inode.)
*/
sd.count = dup_inode_count - dup_inode_founddir;
sd.first_inode = EXT2_FIRST_INODE(fs->super);
sd.max_inode = fs->super->s_inodes_count;
+ sd.ctx = ctx;
+ sd.allflag = (ctx->shared == E2F_SHARED_LPF);
ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
search_dirent_proc, &sd);
}
+static errcode_t unlink_all(e2fsck_t ctx, struct dup_inode *dup, ext2_ino_t ino)
+{
+ struct dir_el *dp;
+ errcode_t err, result = 0;
+
+ for (dp = dup->dir_list; dp; dp = dp->next) {
+ err = ext2fs_unlink(ctx->fs, dp->dir, NULL, ino, 0);
+ if (err)
+ result = err;
+ }
+ return result;
+}
+
static void pass1d(e2fsck_t ctx, char *block_buf)
{
ext2_filsys fs = ctx->fs;
@@ -476,6 +515,9 @@ static void pass1d(e2fsck_t ctx, char *block_buf)
q = (struct dup_block *) 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_block(ctx, s->block)) {
file_ok = 0;
meta_data = 1;
@@ -504,7 +546,7 @@ static void pass1d(e2fsck_t ctx, char *block_buf)
*/
pctx.inode = &p->inode;
pctx.ino = ino;
- pctx.dir = p->dir;
+ pctx.dir = p->dir_list ? p->dir_list->dir : 0;
pctx.blkcount = p->num_dupblocks;
pctx.num = meta_data ? shared_len+1 : shared_len;
fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
@@ -524,20 +566,32 @@ static void pass1d(e2fsck_t ctx, char *block_buf)
*/
pctx.inode = &t->inode;
pctx.ino = shared[i];
- pctx.dir = t->dir;
+ pctx.dir = t->dir_list ? t->dir_list->dir : 0;
fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
}
if (file_ok) {
fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
continue;
}
- if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
+ if (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 = unlink_all(ctx, p, ino);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1D_DISCONNECT_ERROR,
+ &pctx);
+ goto delete;
+ }
+ }
+ continue;
}
+delete:
if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
delete_file(ctx, ino, p, block_buf);
else
@@ -554,7 +608,8 @@ static void decrement_badcount(e2fsck_t ctx, blk64_t block, struct dup_block *p)
{
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))
ext2fs_unmark_block_bitmap2(ctx->block_dup_map, block);
}
@@ -700,11 +755,15 @@ static int clone_file_block(ext2_filsys fs,
printf("Cloning block %u to %u\n", *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_blk(fs->io, *block_nr,
+ 1, cs->buf);
+ if (retval) {
+ cs->errcode = retval;
+ return BLOCK_ABORT;
+ }
}
retval = io_channel_write_blk64(fs->io, new_block, 1,
cs->buf);
@@ -713,6 +772,11 @@ static int clone_file_block(ext2_filsys fs,
return BLOCK_ABORT;
}
decrement_badcount(ctx, *block_nr, p);
+ 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);
+ }
*block_nr = new_block;
ext2fs_mark_block_bitmap2(ctx->block_found_map,
new_block);
@@ -998,6 +998,14 @@ static struct e2fsck_problem problem_table[] = {
{ PR_1D_CLONE_ERROR,
N_("Couldn't clone file: %m\n"), PROMPT_NONE, 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 2 errors */
/* Pass 2: Checking directory structure */
@@ -589,6 +589,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 2 errors
*/
@@ -593,6 +593,49 @@ static void signal_cancel(int sig EXT2FS_ATTR((unused)))
}
#endif
+static void initialize_profile_options(e2fsck_t ctx)
+{
+ char *tmp = NULL;
+
+ /* [options] shared=preserve|lost+found|delete */
+ 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,
+ _("unknown configuration option: '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,
+ _("unknown configuration option: '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;
@@ -628,6 +671,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++;
@@ -658,6 +731,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);
fputc('\n', stderr);
exit(1);
}
@@ -717,6 +792,8 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
else
ctx->program_name = "e2fsck";
+ initialize_profile_options(ctx);
+
while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
switch (c) {
case 'C':