forked from pool/e2fsprogs
Set link to e2fsprogs.14587 via maintenance_release request
Rev SUSE:SLE-15:Update/6 Md5 d8f0e1e9351003e8fa23e76a764813bf 2020-04-08 11:34:04 hlohr None
This commit is contained in:
parent
0328894a8d
commit
b8b5069980
96
e2fsck-clarify-overflow-link-count-error-message.patch
Normal file
96
e2fsck-clarify-overflow-link-count-error-message.patch
Normal file
@ -0,0 +1,96 @@
|
||||
From 125850eb92f042c76b6f001bf63833ffc15e7916 Mon Sep 17 00:00:00 2001
|
||||
From: Jan Kara <jack@suse.cz>
|
||||
Date: Thu, 13 Feb 2020 11:15:56 +0100
|
||||
Subject: [PATCH] e2fsck: clarify overflow link count error message
|
||||
References: bsc#1160979
|
||||
|
||||
When directory link count is set to overflow value (1) but during pass 4
|
||||
we find out the exact link count would fit, we either silently fix this
|
||||
(which is not great because e2fsck then reports the fs was modified but
|
||||
output doesn't indicate why in any way), or we report that link count is
|
||||
wrong and ask whether we should fix it (in case -n option was
|
||||
specified). The second case is even more misleading because it suggests
|
||||
non-trivial fs corruption which then gets silently fixed on the next
|
||||
run. Similarly to how we fix up other non-problems, just create a new
|
||||
error message for the case directory link count is not overflown anymore
|
||||
and always report it to clarify what is going on.
|
||||
|
||||
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
|
||||
Signed-off-by: Jan Kara <jack@suse.cz>
|
||||
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
|
||||
(cherry picked from commit 4ebce13292f54c96f43dcb1bd1d5b8df5dc8749d)
|
||||
Acked-by: Jan Kara <jack@suse.cz>
|
||||
---
|
||||
e2fsck/pass4.c | 20 ++++++++++++++++----
|
||||
e2fsck/problem.c | 5 +++++
|
||||
e2fsck/problem.h | 3 +++
|
||||
3 files changed, 24 insertions(+), 4 deletions(-)
|
||||
|
||||
Index: e2fsprogs-1.43.8/e2fsck/pass4.c
|
||||
===================================================================
|
||||
--- e2fsprogs-1.43.8.orig/e2fsck/pass4.c
|
||||
+++ e2fsprogs-1.43.8/e2fsck/pass4.c
|
||||
@@ -169,6 +169,8 @@ void e2fsck_pass4(e2fsck_t ctx)
|
||||
if (isdir && (link_counted > EXT2_LINK_MAX))
|
||||
link_counted = 1;
|
||||
if (link_counted != link_count) {
|
||||
+ int fix_nlink = 0;
|
||||
+
|
||||
e2fsck_read_inode_full(ctx, i, EXT2_INODE(inode),
|
||||
inode_size, "pass4");
|
||||
pctx.ino = i;
|
||||
@@ -182,10 +184,20 @@ void e2fsck_pass4(e2fsck_t ctx)
|
||||
pctx.num = link_counted;
|
||||
/* i_link_count was previously exceeded, but no longer
|
||||
* is, fix this but don't consider it an error */
|
||||
- if ((isdir && link_counted > 1 &&
|
||||
- (inode->i_flags & EXT2_INDEX_FL) &&
|
||||
- link_count == 1 && !(ctx->options & E2F_OPT_NO)) ||
|
||||
- fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
|
||||
+ if (isdir && link_counted > 1 &&
|
||||
+ (inode->i_flags & EXT2_INDEX_FL) &&
|
||||
+ link_count == 1) {
|
||||
+ if ((ctx->options & E2F_OPT_READONLY) == 0) {
|
||||
+ fix_nlink =
|
||||
+ fix_problem(ctx,
|
||||
+ PR_4_DIR_OVERFLOW_REF_COUNT,
|
||||
+ &pctx);
|
||||
+ }
|
||||
+ } else {
|
||||
+ fix_nlink = fix_problem(ctx, PR_4_BAD_REF_COUNT,
|
||||
+ &pctx);
|
||||
+ }
|
||||
+ if (fix_nlink) {
|
||||
inode->i_links_count = link_counted;
|
||||
e2fsck_write_inode_full(ctx, i,
|
||||
EXT2_INODE(inode),
|
||||
Index: e2fsprogs-1.43.8/e2fsck/problem.c
|
||||
===================================================================
|
||||
--- e2fsprogs-1.43.8.orig/e2fsck/problem.c
|
||||
+++ e2fsprogs-1.43.8/e2fsck/problem.c
|
||||
@@ -1857,6 +1857,11 @@ static struct e2fsck_problem problem_tab
|
||||
"They @s the same!\n"),
|
||||
PROMPT_NONE, 0 },
|
||||
|
||||
+ /* Directory inode ref count set to overflow but could be exact value */
|
||||
+ { PR_4_DIR_OVERFLOW_REF_COUNT,
|
||||
+ N_("@d @i %i ref count set to overflow but could be exact value %N. "),
|
||||
+ PROMPT_FIX, PR_PREEN_OK, 0, 0, 0 },
|
||||
+
|
||||
/* Pass 5 errors */
|
||||
|
||||
/* Pass 5: Checking group summary information */
|
||||
Index: e2fsprogs-1.43.8/e2fsck/problem.h
|
||||
===================================================================
|
||||
--- e2fsprogs-1.43.8.orig/e2fsck/problem.h
|
||||
+++ e2fsprogs-1.43.8/e2fsck/problem.h
|
||||
@@ -1123,6 +1123,9 @@ struct problem_context {
|
||||
/* Inconsistent inode count information cached */
|
||||
#define PR_4_INCONSISTENT_COUNT 0x040004
|
||||
|
||||
+/* Directory ref count set to overflow but it doesn't have to be */
|
||||
+#define PR_4_DIR_OVERFLOW_REF_COUNT 0x040007
|
||||
+
|
||||
/*
|
||||
* Pass 5 errors
|
||||
*/
|
@ -1,3 +1,17 @@
|
||||
-------------------------------------------------------------------
|
||||
Fri Mar 27 16:00:29 UTC 2020 - Jan Kara <jack@suse.cz>
|
||||
|
||||
- e2fsck-clarify-overflow-link-count-error-message.patch: e2fsck: clarify
|
||||
overflow link count error message (bsc#1160979)
|
||||
- ext2fs-update-allocation-info-earlier-in-ext2fs_mkdi.patch: ext2fs: update
|
||||
allocation info earlier in ext2fs_mkdir() (bsc#1160979)
|
||||
- ext2fs-implement-dir-entry-creation-in-htree-directo.patch: ext2fs: implement
|
||||
dir entry creation in htree directories (bsc#1160979)
|
||||
- tests-add-test-to-excercise-indexed-directories-with.patch: tests: add test
|
||||
to excercise indexed directories with metadata_csum (bsc#1160979)
|
||||
- tune2fs-update-dir-checksums-when-clearing-dir_index.patch: tune2fs: update
|
||||
dir checksums when clearing dir_index feature (bsc#1160979)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Jan 9 14:25:34 UTC 2020 - Jan Kara <jack@suse.cz>
|
||||
|
||||
|
@ -88,6 +88,11 @@ Patch9: libsupport-add-checks-to-prevent-buffer-overrun-bugs.patch
|
||||
Patch10: resize2fs-Make-minimum-size-estimates-more-reliable.patch
|
||||
Patch11: e2fsck-abort-if-there-is-a-corrupted-directory-block.patch
|
||||
Patch12: e2fsck-don-t-try-to-rehash-a-deleted-directory.patch
|
||||
Patch13: e2fsck-clarify-overflow-link-count-error-message.patch
|
||||
Patch14: ext2fs-update-allocation-info-earlier-in-ext2fs_mkdi.patch
|
||||
Patch15: ext2fs-implement-dir-entry-creation-in-htree-directo.patch
|
||||
Patch16: tests-add-test-to-excercise-indexed-directories-with.patch
|
||||
Patch17: tune2fs-update-dir-checksums-when-clearing-dir_index.patch
|
||||
# Do not suppress make commands
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||
|
||||
@ -239,6 +244,11 @@ Development files for the com_err error message display library. Static librarie
|
||||
%patch10 -p1
|
||||
%patch11 -p1
|
||||
%patch12 -p1
|
||||
%patch13 -p1
|
||||
%patch14 -p1
|
||||
%patch15 -p1
|
||||
%patch16 -p1
|
||||
%patch17 -p1
|
||||
cp %{SOURCE2} .
|
||||
|
||||
%build
|
||||
|
616
ext2fs-implement-dir-entry-creation-in-htree-directo.patch
Normal file
616
ext2fs-implement-dir-entry-creation-in-htree-directo.patch
Normal file
@ -0,0 +1,616 @@
|
||||
From 9a4d2dcc8deaa1c28b3a713c2f610be503855946 Mon Sep 17 00:00:00 2001
|
||||
From: Jan Kara <jack@suse.cz>
|
||||
Date: Thu, 13 Feb 2020 11:15:59 +0100
|
||||
Subject: [PATCH 2/5] ext2fs: implement dir entry creation in htree directories
|
||||
References: bsc#1160979
|
||||
|
||||
Implement proper creation of new directory entries in htree directories
|
||||
in ext2fs_link(). So far we just cleared EXT2_INDEX_FL and treated
|
||||
directory as unindexed however this results in mismatched checksums if
|
||||
metadata checksums are in use because checksums are placed in different
|
||||
places depending on htree node type.
|
||||
|
||||
Signed-off-by: Jan Kara <jack@suse.cz>
|
||||
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
|
||||
Acked-by: Jan Kara <jack@suse.cz>
|
||||
---
|
||||
lib/ext2fs/link.c | 549 ++++++++++++++++++++++++++++++++++++++++++++++++------
|
||||
1 file changed, 497 insertions(+), 52 deletions(-)
|
||||
|
||||
Index: e2fsprogs-1.43.8/lib/ext2fs/link.c
|
||||
===================================================================
|
||||
--- e2fsprogs-1.43.8.orig/lib/ext2fs/link.c
|
||||
+++ e2fsprogs-1.43.8/lib/ext2fs/link.c
|
||||
@@ -18,6 +18,153 @@
|
||||
|
||||
#include "ext2_fs.h"
|
||||
#include "ext2fs.h"
|
||||
+#include "ext2fsP.h"
|
||||
+
|
||||
+#define EXT2_DX_ROOT_OFF 24
|
||||
+
|
||||
+struct dx_frame {
|
||||
+ void *buf;
|
||||
+ blk64_t pblock;
|
||||
+ struct ext2_dx_countlimit *head;
|
||||
+ struct ext2_dx_entry *entries;
|
||||
+ struct ext2_dx_entry *at;
|
||||
+};
|
||||
+
|
||||
+struct dx_lookup_info {
|
||||
+ const char *name;
|
||||
+ int namelen;
|
||||
+ int hash_alg;
|
||||
+ __u32 hash;
|
||||
+ int levels;
|
||||
+ struct dx_frame frames[2];
|
||||
+};
|
||||
+
|
||||
+static errcode_t alloc_dx_frame(ext2_filsys fs, struct dx_frame *frame)
|
||||
+{
|
||||
+ return ext2fs_get_mem(fs->blocksize, &frame->buf);
|
||||
+}
|
||||
+
|
||||
+static void dx_release(struct dx_lookup_info *info)
|
||||
+{
|
||||
+ struct ext2_dx_root_info *root;
|
||||
+ int level;
|
||||
+
|
||||
+ for (level = 0; level < info->levels; level++) {
|
||||
+ if (info->frames[level].buf == NULL)
|
||||
+ break;
|
||||
+ ext2fs_free_mem(&(info->frames[level].buf));
|
||||
+ }
|
||||
+ info->levels = 0;
|
||||
+}
|
||||
+
|
||||
+static void dx_search_entry(struct dx_frame *frame, int count, __u32 hash)
|
||||
+{
|
||||
+ struct ext2_dx_entry *p, *q, *m;
|
||||
+
|
||||
+ p = frame->entries + 1;
|
||||
+ q = frame->entries + count - 1;
|
||||
+ while (p <= q) {
|
||||
+ m = p + (q - p) / 2;
|
||||
+ if (ext2fs_le32_to_cpu(m->hash) > hash)
|
||||
+ q = m - 1;
|
||||
+ else
|
||||
+ p = m + 1;
|
||||
+ }
|
||||
+ frame->at = p - 1;
|
||||
+}
|
||||
+
|
||||
+static errcode_t load_logical_dir_block(ext2_filsys fs, ext2_ino_t dir,
|
||||
+ struct ext2_inode *diri, blk64_t block,
|
||||
+ blk64_t *pblk, void *buf)
|
||||
+{
|
||||
+ errcode_t errcode;
|
||||
+ int ret_flags;
|
||||
+
|
||||
+ errcode = ext2fs_bmap2(fs, dir, diri, NULL, 0, block, &ret_flags,
|
||||
+ pblk);
|
||||
+ if (errcode)
|
||||
+ return errcode;
|
||||
+ if (ret_flags & BMAP_RET_UNINIT)
|
||||
+ return EXT2_ET_DIR_CORRUPTED;
|
||||
+ return ext2fs_read_dir_block4(fs, *pblk, buf, 0, dir);
|
||||
+}
|
||||
+
|
||||
+static errcode_t dx_lookup(ext2_filsys fs, ext2_ino_t dir,
|
||||
+ struct ext2_inode *diri, struct dx_lookup_info *info)
|
||||
+{
|
||||
+ struct ext2_dx_root_info *root;
|
||||
+ errcode_t errcode;
|
||||
+ int level = 0;
|
||||
+ int count, limit;
|
||||
+ int hash_alg;
|
||||
+ __u32 hash, minor_hash;
|
||||
+ struct dx_frame *frame;
|
||||
+
|
||||
+ errcode = alloc_dx_frame(fs, &(info->frames[0]));
|
||||
+ if (errcode)
|
||||
+ return errcode;
|
||||
+ info->levels = 1;
|
||||
+
|
||||
+ errcode = load_logical_dir_block(fs, dir, diri, 0,
|
||||
+ &(info->frames[0].pblock),
|
||||
+ info->frames[0].buf);
|
||||
+ if (errcode)
|
||||
+ goto out_err;
|
||||
+ root = info->frames[0].buf + EXT2_DX_ROOT_OFF;
|
||||
+ hash_alg = root->hash_version;
|
||||
+ if (hash_alg != EXT2_HASH_TEA && hash_alg != EXT2_HASH_HALF_MD4 &&
|
||||
+ hash_alg != EXT2_HASH_LEGACY) {
|
||||
+ errcode = EXT2_ET_DIRHASH_UNSUPP;
|
||||
+ goto out_err;
|
||||
+ }
|
||||
+ if (hash_alg <= EXT2_HASH_TEA &&
|
||||
+ fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)
|
||||
+ hash_alg += 3;
|
||||
+ if (root->indirect_levels >= 2) {
|
||||
+ errcode = EXT2_ET_DIR_CORRUPTED;
|
||||
+ goto out_err;
|
||||
+ }
|
||||
+ info->hash_alg = hash_alg;
|
||||
+
|
||||
+ errcode = ext2fs_dirhash(hash_alg, info->name, info->namelen,
|
||||
+ fs->super->s_hash_seed, &info->hash,
|
||||
+ &minor_hash);
|
||||
+ if (errcode)
|
||||
+ goto out_err;
|
||||
+
|
||||
+ for (level = 0; level <= root->indirect_levels; level++) {
|
||||
+ frame = &(info->frames[level]);
|
||||
+ if (level > 0) {
|
||||
+ errcode = alloc_dx_frame(fs, frame);
|
||||
+ if (errcode)
|
||||
+ goto out_err;
|
||||
+ info->levels++;
|
||||
+
|
||||
+ errcode = load_logical_dir_block(fs, dir, diri,
|
||||
+ ext2fs_le32_to_cpu(info->frames[level-1].at->block) & 0x0fffffff,
|
||||
+ &(frame->pblock), frame->buf);
|
||||
+ if (errcode)
|
||||
+ goto out_err;
|
||||
+ }
|
||||
+ errcode = ext2fs_get_dx_countlimit(fs, frame->buf,
|
||||
+ &(frame->head), NULL);
|
||||
+ if (errcode)
|
||||
+ goto out_err;
|
||||
+ count = ext2fs_le16_to_cpu(frame->head->count);
|
||||
+ limit = ext2fs_le16_to_cpu(frame->head->limit);
|
||||
+ frame->entries = (struct ext2_dx_entry *)(frame->head);
|
||||
+ if (!count || count > limit) {
|
||||
+ errcode = EXT2_ET_DIR_CORRUPTED;
|
||||
+ goto out_err;
|
||||
+ }
|
||||
+
|
||||
+ dx_search_entry(frame, count, info->hash);
|
||||
+ }
|
||||
+ return 0;
|
||||
+out_err:
|
||||
+ dx_release(info);
|
||||
+ return errcode;
|
||||
+}
|
||||
|
||||
struct link_struct {
|
||||
ext2_filsys fs;
|
||||
@@ -31,7 +178,9 @@ struct link_struct {
|
||||
struct ext2_super_block *sb;
|
||||
};
|
||||
|
||||
-static int link_proc(struct ext2_dir_entry *dirent,
|
||||
+static int link_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
|
||||
+ int entru EXT2FS_ATTR((unused)),
|
||||
+ struct ext2_dir_entry *dirent,
|
||||
int offset,
|
||||
int blocksize,
|
||||
char *buf,
|
||||
@@ -71,40 +220,6 @@ static int link_proc(struct ext2_dir_ent
|
||||
}
|
||||
|
||||
/*
|
||||
- * Since ext2fs_link blows away htree data, we need to be
|
||||
- * careful -- if metadata_csum is enabled and we're passed in
|
||||
- * a dirent that contains htree data, we need to create the
|
||||
- * fake entry at the end of the block that hides the checksum.
|
||||
- */
|
||||
-
|
||||
- /* De-convert a dx_node block */
|
||||
- if (csum_size &&
|
||||
- curr_rec_len == ls->fs->blocksize &&
|
||||
- !dirent->inode) {
|
||||
- curr_rec_len -= csum_size;
|
||||
- ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
|
||||
- if (ls->err)
|
||||
- return DIRENT_ABORT;
|
||||
- t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
|
||||
- ext2fs_initialize_dirent_tail(ls->fs, t);
|
||||
- ret = DIRENT_CHANGED;
|
||||
- }
|
||||
-
|
||||
- /* De-convert a dx_root block */
|
||||
- if (csum_size &&
|
||||
- curr_rec_len == ls->fs->blocksize - EXT2_DIR_REC_LEN(1) &&
|
||||
- offset == EXT2_DIR_REC_LEN(1) &&
|
||||
- dirent->name[0] == '.' && dirent->name[1] == '.') {
|
||||
- curr_rec_len -= csum_size;
|
||||
- ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
|
||||
- if (ls->err)
|
||||
- return DIRENT_ABORT;
|
||||
- t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
|
||||
- ext2fs_initialize_dirent_tail(ls->fs, t);
|
||||
- ret = DIRENT_CHANGED;
|
||||
- }
|
||||
-
|
||||
- /*
|
||||
* If the directory entry is used, see if we can split the
|
||||
* directory entry to make room for the new name. If so,
|
||||
* truncate it and return.
|
||||
@@ -144,6 +259,341 @@ static int link_proc(struct ext2_dir_ent
|
||||
return DIRENT_ABORT|DIRENT_CHANGED;
|
||||
}
|
||||
|
||||
+static errcode_t add_dirent_to_buf(ext2_filsys fs, e2_blkcnt_t blockcnt,
|
||||
+ char *buf, ext2_ino_t dir,
|
||||
+ struct ext2_inode *diri, const char *name,
|
||||
+ ext2_ino_t ino, int flags, blk64_t *pblkp)
|
||||
+{
|
||||
+ struct dir_context ctx;
|
||||
+ struct link_struct ls;
|
||||
+ errcode_t retval;
|
||||
+
|
||||
+ retval = load_logical_dir_block(fs, dir, diri, blockcnt, pblkp, buf);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ ctx.errcode = 0;
|
||||
+ ctx.func = link_proc;
|
||||
+ ctx.dir = dir;
|
||||
+ ctx.flags = DIRENT_FLAG_INCLUDE_EMPTY;
|
||||
+ ctx.buf = buf;
|
||||
+ ctx.priv_data = &ls;
|
||||
+
|
||||
+ ls.fs = fs;
|
||||
+ ls.name = name;
|
||||
+ ls.namelen = strlen(name);
|
||||
+ ls.inode = ino;
|
||||
+ ls.flags = flags;
|
||||
+ ls.done = 0;
|
||||
+ ls.sb = fs->super;
|
||||
+ ls.blocksize = fs->blocksize;
|
||||
+ ls.err = 0;
|
||||
+
|
||||
+ ext2fs_process_dir_block(fs, pblkp, blockcnt, 0, 0, &ctx);
|
||||
+ if (ctx.errcode)
|
||||
+ return ctx.errcode;
|
||||
+ if (ls.err)
|
||||
+ return ls.err;
|
||||
+ if (!ls.done)
|
||||
+ return EXT2_ET_DIR_NO_SPACE;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+struct dx_hash_map {
|
||||
+ __u32 hash;
|
||||
+ int size;
|
||||
+ int off;
|
||||
+};
|
||||
+
|
||||
+static EXT2_QSORT_TYPE dx_hash_map_cmp(const void *ap, const void *bp)
|
||||
+{
|
||||
+ const struct dx_hash_map *a = ap, *b = bp;
|
||||
+
|
||||
+ if (a->hash < b->hash)
|
||||
+ return -1;
|
||||
+ if (a->hash > b->hash)
|
||||
+ return 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static errcode_t dx_move_dirents(ext2_filsys fs, struct dx_hash_map *map,
|
||||
+ int count, void *from, void *to)
|
||||
+{
|
||||
+ struct ext2_dir_entry *de;
|
||||
+ int i;
|
||||
+ int rec_len;
|
||||
+ errcode_t retval;
|
||||
+ int csum_size = 0;
|
||||
+ void *base = to;
|
||||
+
|
||||
+ if (ext2fs_has_feature_metadata_csum(fs->super))
|
||||
+ csum_size = sizeof(struct ext2_dir_entry_tail);
|
||||
+
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ de = from + map[i].off;
|
||||
+ rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(de));
|
||||
+ memcpy(to, de, rec_len);
|
||||
+ retval = ext2fs_set_rec_len(fs, rec_len, to);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ to += rec_len;
|
||||
+ }
|
||||
+ /*
|
||||
+ * Update rec_len of the last dir entry to stretch to the end of block
|
||||
+ */
|
||||
+ to -= rec_len;
|
||||
+ rec_len = fs->blocksize - (to - base) - csum_size;
|
||||
+ retval = ext2fs_set_rec_len(fs, rec_len, to);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ if (csum_size)
|
||||
+ ext2fs_initialize_dirent_tail(fs,
|
||||
+ EXT2_DIRENT_TAIL(base, fs->blocksize));
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static errcode_t dx_insert_entry(ext2_filsys fs, ext2_ino_t dir,
|
||||
+ struct dx_lookup_info *info, int level,
|
||||
+ __u32 hash, blk64_t lblk)
|
||||
+{
|
||||
+ int pcount;
|
||||
+ struct ext2_dx_entry *top, *new;
|
||||
+
|
||||
+ pcount = ext2fs_le16_to_cpu(info->frames[level].head->count);
|
||||
+ top = info->frames[level].entries + pcount;
|
||||
+ new = info->frames[level].at + 1;
|
||||
+ memmove(new + 1, new, (char *)top - (char *)new);
|
||||
+ new->hash = ext2fs_cpu_to_le32(hash);
|
||||
+ new->block = ext2fs_cpu_to_le32(lblk);
|
||||
+ info->frames[level].head->count = ext2fs_cpu_to_le16(pcount + 1);
|
||||
+ return ext2fs_write_dir_block4(fs, info->frames[level].pblock,
|
||||
+ info->frames[level].buf, 0, dir);
|
||||
+}
|
||||
+
|
||||
+static errcode_t dx_split_leaf(ext2_filsys fs, ext2_ino_t dir,
|
||||
+ struct ext2_inode *diri,
|
||||
+ struct dx_lookup_info *info, void *buf,
|
||||
+ blk64_t leaf_pblk, blk64_t new_lblk,
|
||||
+ blk64_t new_pblk)
|
||||
+{
|
||||
+ struct ext2_dir_entry *de;
|
||||
+ void *buf2;
|
||||
+ errcode_t retval = 0;
|
||||
+ int rec_len;
|
||||
+ int offset, move_size;
|
||||
+ int i, count = 0;
|
||||
+ struct dx_hash_map *map;
|
||||
+ int continued;
|
||||
+ __u32 minor_hash;
|
||||
+
|
||||
+ retval = ext2fs_get_mem(fs->blocksize, &buf2);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ retval = ext2fs_get_array(fs->blocksize / 12,
|
||||
+ sizeof(struct dx_hash_map), &map);
|
||||
+ if (retval) {
|
||||
+ ext2fs_free_mem(&buf2);
|
||||
+ return retval;
|
||||
+ }
|
||||
+ for (offset = 0; offset < fs->blocksize; offset += rec_len) {
|
||||
+ de = buf + offset;
|
||||
+ retval = ext2fs_get_rec_len(fs, de, &rec_len);
|
||||
+ if (retval)
|
||||
+ goto out;
|
||||
+ if (ext2fs_dirent_name_len(de) > 0 && de->inode) {
|
||||
+ map[count].off = offset;
|
||||
+ map[count].size = rec_len;
|
||||
+ retval = ext2fs_dirhash(info->hash_alg, de->name,
|
||||
+ ext2fs_dirent_name_len(de),
|
||||
+ fs->super->s_hash_seed,
|
||||
+ &(map[count].hash),
|
||||
+ &minor_hash);
|
||||
+ if (retval)
|
||||
+ goto out;
|
||||
+ count++;
|
||||
+ }
|
||||
+ }
|
||||
+ qsort(map, count, sizeof(struct dx_hash_map), dx_hash_map_cmp);
|
||||
+ move_size = 0;
|
||||
+ /* Find place to split block */
|
||||
+ for (i = count - 1; i >= 0; i--) {
|
||||
+ if (move_size + map[i].size / 2 > fs->blocksize / 2)
|
||||
+ break;
|
||||
+ move_size += map[i].size;
|
||||
+ }
|
||||
+ /* Let i be the first entry to move */
|
||||
+ i++;
|
||||
+ /* Move selected directory entries to new block */
|
||||
+ retval = dx_move_dirents(fs, map + i, count - i, buf, buf2);
|
||||
+ if (retval)
|
||||
+ goto out;
|
||||
+ retval = ext2fs_write_dir_block4(fs, new_pblk, buf2, 0, dir);
|
||||
+ if (retval)
|
||||
+ goto out;
|
||||
+ /* Repack remaining entries in the old block */
|
||||
+ retval = dx_move_dirents(fs, map, i, buf, buf2);
|
||||
+ if (retval)
|
||||
+ goto out;
|
||||
+ retval = ext2fs_write_dir_block4(fs, leaf_pblk, buf2, 0, dir);
|
||||
+ if (retval)
|
||||
+ goto out;
|
||||
+ /* Update parent node */
|
||||
+ continued = map[i].hash == map[i-1].hash;
|
||||
+ retval = dx_insert_entry(fs, dir, info, info->levels - 1,
|
||||
+ map[i].hash + continued, new_lblk);
|
||||
+out:
|
||||
+ ext2fs_free_mem(&buf2);
|
||||
+ ext2fs_free_mem(&map);
|
||||
+ return retval;
|
||||
+}
|
||||
+
|
||||
+static errcode_t dx_grow_tree(ext2_filsys fs, ext2_ino_t dir,
|
||||
+ struct ext2_inode *diri,
|
||||
+ struct dx_lookup_info *info, void *buf,
|
||||
+ blk64_t leaf_pblk)
|
||||
+{
|
||||
+ int i;
|
||||
+ errcode_t retval;
|
||||
+ ext2_off64_t size = EXT2_I_SIZE(diri);
|
||||
+ blk64_t lblk, pblk;
|
||||
+ struct ext2_dir_entry *de;
|
||||
+ struct ext2_dx_countlimit *head;
|
||||
+ int csum_size = 0;
|
||||
+ int count;
|
||||
+
|
||||
+ if (ext2fs_has_feature_metadata_csum(fs->super))
|
||||
+ csum_size = sizeof(struct ext2_dx_tail);
|
||||
+
|
||||
+ /* Find level which can accommodate new child */
|
||||
+ for (i = info->levels - 1; i >= 0; i--)
|
||||
+ if (ext2fs_le16_to_cpu(info->frames[i].head->count) <
|
||||
+ ext2fs_le16_to_cpu(info->frames[i].head->limit))
|
||||
+ break;
|
||||
+ /* Need to grow tree depth? */
|
||||
+ if (i < 0 && info->levels >= 2)
|
||||
+ return EXT2_ET_DIR_NO_SPACE;
|
||||
+ lblk = size / fs->blocksize;
|
||||
+ size += fs->blocksize;
|
||||
+ retval = ext2fs_inode_size_set(fs, diri, size);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ retval = ext2fs_fallocate(fs,
|
||||
+ EXT2_FALLOCATE_FORCE_INIT | EXT2_FALLOCATE_ZERO_BLOCKS,
|
||||
+ dir, diri, 0, lblk, 1);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ retval = ext2fs_write_inode(fs, dir, diri);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ retval = ext2fs_bmap2(fs, dir, diri, NULL, 0, lblk, NULL, &pblk);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ /* Only leaf addition needed? */
|
||||
+ if (i == info->levels - 1)
|
||||
+ return dx_split_leaf(fs, dir, diri, info, buf, leaf_pblk,
|
||||
+ lblk, pblk);
|
||||
+
|
||||
+ de = buf;
|
||||
+ de->inode = 0;
|
||||
+ ext2fs_dirent_set_name_len(de, 0);
|
||||
+ ext2fs_dirent_set_file_type(de, 0);
|
||||
+ retval = ext2fs_set_rec_len(fs, fs->blocksize, de);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ head = buf + 8;
|
||||
+ count = ext2fs_le16_to_cpu(info->frames[i+1].head->count);
|
||||
+ /* Growing tree depth? */
|
||||
+ if (i < 0) {
|
||||
+ struct ext2_dx_root_info *root;
|
||||
+
|
||||
+ memcpy(head, info->frames[0].entries,
|
||||
+ count * sizeof(struct ext2_dx_entry));
|
||||
+ head->limit = ext2fs_cpu_to_le16(
|
||||
+ (fs->blocksize - (8 + csum_size)) /
|
||||
+ sizeof(struct ext2_dx_entry));
|
||||
+ /* head->count gets set by memcpy above to correct value */
|
||||
+
|
||||
+ /* Now update tree root */
|
||||
+ info->frames[0].head->count = ext2fs_cpu_to_le16(1);
|
||||
+ info->frames[0].entries[0].block = ext2fs_cpu_to_le32(lblk);
|
||||
+ root = info->frames[0].buf + EXT2_DX_ROOT_OFF;
|
||||
+ root->indirect_levels++;
|
||||
+ } else {
|
||||
+ /* Splitting internal node in two */
|
||||
+ int count1 = count / 2;
|
||||
+ int count2 = count - count1;
|
||||
+ __u32 split_hash = ext2fs_le32_to_cpu(info->frames[i+1].entries[count1].hash);
|
||||
+
|
||||
+ memcpy(head, info->frames[i+1].entries + count1,
|
||||
+ count2 * sizeof(struct ext2_dx_entry));
|
||||
+ head->count = ext2fs_cpu_to_le16(count2);
|
||||
+ head->limit = ext2fs_cpu_to_le16(
|
||||
+ (fs->blocksize - (8 + csum_size)) /
|
||||
+ sizeof(struct ext2_dx_entry));
|
||||
+ info->frames[i+1].head->count = ext2fs_cpu_to_le16(count1);
|
||||
+
|
||||
+ /* Update parent node */
|
||||
+ retval = dx_insert_entry(fs, dir, info, i, split_hash, lblk);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+
|
||||
+ }
|
||||
+ /* Writeout split block / updated root */
|
||||
+ retval = ext2fs_write_dir_block4(fs, info->frames[i+1].pblock,
|
||||
+ info->frames[i+1].buf, 0, dir);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ /* Writeout new tree block */
|
||||
+ retval = ext2fs_write_dir_block4(fs, pblk, buf, 0, dir);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static errcode_t dx_link(ext2_filsys fs, ext2_ino_t dir,
|
||||
+ struct ext2_inode *diri, const char *name,
|
||||
+ ext2_ino_t ino, int flags)
|
||||
+{
|
||||
+ struct dx_lookup_info dx_info;
|
||||
+ errcode_t retval;
|
||||
+ void *blockbuf;
|
||||
+ int restart = 0;
|
||||
+ blk64_t leaf_pblk;
|
||||
+
|
||||
+ retval = ext2fs_get_mem(fs->blocksize, &blockbuf);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+
|
||||
+ dx_info.name = name;
|
||||
+ dx_info.namelen = strlen(name);
|
||||
+again:
|
||||
+ retval = dx_lookup(fs, dir, diri, &dx_info);
|
||||
+ if (retval)
|
||||
+ goto free_buf;
|
||||
+
|
||||
+ retval = add_dirent_to_buf(fs,
|
||||
+ ext2fs_le32_to_cpu(dx_info.frames[dx_info.levels-1].at->block) & 0x0fffffff,
|
||||
+ blockbuf, dir, diri, name, ino, flags, &leaf_pblk);
|
||||
+ /*
|
||||
+ * Success or error other than ENOSPC...? We are done. We may need upto
|
||||
+ * two tries to add entry. One to split htree node and another to add
|
||||
+ * new leaf block.
|
||||
+ */
|
||||
+ if (restart >= dx_info.levels || retval != EXT2_ET_DIR_NO_SPACE)
|
||||
+ goto free_frames;
|
||||
+ retval = dx_grow_tree(fs, dir, diri, &dx_info, blockbuf, leaf_pblk);
|
||||
+ if (retval)
|
||||
+ goto free_frames;
|
||||
+ /* Restart everything now that the tree is larger */
|
||||
+ restart++;
|
||||
+ dx_release(&dx_info);
|
||||
+ goto again;
|
||||
+free_frames:
|
||||
+ dx_release(&dx_info);
|
||||
+free_buf:
|
||||
+ ext2fs_free_mem(&blockbuf);
|
||||
+ return retval;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Note: the low 3 bits of the flags field are used as the directory
|
||||
* entry filetype.
|
||||
@@ -163,6 +613,12 @@ errcode_t ext2fs_link(ext2_filsys fs, ex
|
||||
if (!(fs->flags & EXT2_FLAG_RW))
|
||||
return EXT2_ET_RO_FILSYS;
|
||||
|
||||
+ if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
|
||||
+ return retval;
|
||||
+
|
||||
+ if (inode.i_flags & EXT2_INDEX_FL)
|
||||
+ return dx_link(fs, dir, &inode, name, ino, flags);
|
||||
+
|
||||
ls.fs = fs;
|
||||
ls.name = name;
|
||||
ls.namelen = name ? strlen(name) : 0;
|
||||
@@ -173,8 +629,8 @@ errcode_t ext2fs_link(ext2_filsys fs, ex
|
||||
ls.blocksize = fs->blocksize;
|
||||
ls.err = 0;
|
||||
|
||||
- retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
|
||||
- 0, link_proc, &ls);
|
||||
+ retval = ext2fs_dir_iterate2(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
|
||||
+ NULL, link_proc, &ls);
|
||||
if (retval)
|
||||
return retval;
|
||||
if (ls.err)
|
||||
@@ -182,20 +638,5 @@ errcode_t ext2fs_link(ext2_filsys fs, ex
|
||||
|
||||
if (!ls.done)
|
||||
return EXT2_ET_DIR_NO_SPACE;
|
||||
-
|
||||
- if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
|
||||
- return retval;
|
||||
-
|
||||
- /*
|
||||
- * If this function changes to preserve the htree, remove the
|
||||
- * two hunks in link_proc that shove checksum tails into the
|
||||
- * former dx_root/dx_node blocks.
|
||||
- */
|
||||
- if (inode.i_flags & EXT2_INDEX_FL) {
|
||||
- inode.i_flags &= ~EXT2_INDEX_FL;
|
||||
- if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0)
|
||||
- return retval;
|
||||
- }
|
||||
-
|
||||
return 0;
|
||||
}
|
128
ext2fs-update-allocation-info-earlier-in-ext2fs_mkdi.patch
Normal file
128
ext2fs-update-allocation-info-earlier-in-ext2fs_mkdi.patch
Normal file
@ -0,0 +1,128 @@
|
||||
From a25a84c6aa2a19987b0aeced363c4bf1dcc89ec8 Mon Sep 17 00:00:00 2001
|
||||
From: Jan Kara <jack@suse.cz>
|
||||
Date: Thu, 13 Feb 2020 11:15:58 +0100
|
||||
Subject: [PATCH 1/5] ext2fs: update allocation info earlier in ext2fs_mkdir()
|
||||
and ext2fs_symlink()
|
||||
References: bsc#1160979
|
||||
|
||||
Currently, ext2fs_mkdir() and ext2fs_symlink() update allocation bitmaps
|
||||
and other information only close to the end of the function, in
|
||||
particular after calling to ext2fs_link(). When ext2fs_link() will
|
||||
support indexed directories, it will also need to allocate blocks and
|
||||
that would cause filesystem corruption in case allocation info isn't
|
||||
properly updated. So make sure ext2fs_mkdir() and ext2fs_symlink()
|
||||
update allocation info before calling into ext2fs_link().
|
||||
|
||||
[ Added error handling so the calls to ext2fs_{block,inode}_alloc_stats()
|
||||
can be undone if the newly created directory or symlink can not be linked
|
||||
into the directory. -- TYT ]
|
||||
|
||||
Signed-off-by: Jan Kara <jack@suse.cz>
|
||||
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
|
||||
Acked-by: Jan Kara <jack@suse.cz>
|
||||
---
|
||||
lib/ext2fs/mkdir.c | 22 +++++++++++++++-------
|
||||
lib/ext2fs/symlink.c | 22 +++++++++++++++-------
|
||||
2 files changed, 30 insertions(+), 14 deletions(-)
|
||||
|
||||
Index: e2fsprogs-1.43.8/lib/ext2fs/mkdir.c
|
||||
===================================================================
|
||||
--- e2fsprogs-1.43.8.orig/lib/ext2fs/mkdir.c
|
||||
+++ e2fsprogs-1.43.8/lib/ext2fs/mkdir.c
|
||||
@@ -43,6 +43,7 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, e
|
||||
blk64_t blk;
|
||||
char *block = 0;
|
||||
int inline_data = 0;
|
||||
+ int drop_refcount = 0;
|
||||
|
||||
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
|
||||
|
||||
@@ -144,6 +145,14 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, e
|
||||
}
|
||||
|
||||
/*
|
||||
+ * Update accounting....
|
||||
+ */
|
||||
+ if (!inline_data)
|
||||
+ ext2fs_block_alloc_stats2(fs, blk, +1);
|
||||
+ ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
|
||||
+ drop_refcount = 1;
|
||||
+
|
||||
+ /*
|
||||
* Link the directory into the filesystem hierarchy
|
||||
*/
|
||||
if (name) {
|
||||
@@ -174,17 +183,16 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, e
|
||||
if (retval)
|
||||
goto cleanup;
|
||||
}
|
||||
-
|
||||
- /*
|
||||
- * Update accounting....
|
||||
- */
|
||||
- if (!inline_data)
|
||||
- ext2fs_block_alloc_stats2(fs, blk, +1);
|
||||
- ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
|
||||
+ drop_refcount = 0;
|
||||
|
||||
cleanup:
|
||||
if (block)
|
||||
ext2fs_free_mem(&block);
|
||||
+ if (drop_refcount) {
|
||||
+ if (!inline_data)
|
||||
+ ext2fs_block_alloc_stats2(fs, blk, -1);
|
||||
+ ext2fs_inode_alloc_stats2(fs, ino, -1, 1);
|
||||
+ }
|
||||
return retval;
|
||||
|
||||
}
|
||||
Index: e2fsprogs-1.43.8/lib/ext2fs/symlink.c
|
||||
===================================================================
|
||||
--- e2fsprogs-1.43.8.orig/lib/ext2fs/symlink.c
|
||||
+++ e2fsprogs-1.43.8/lib/ext2fs/symlink.c
|
||||
@@ -38,6 +38,7 @@ errcode_t ext2fs_symlink(ext2_filsys fs,
|
||||
int fastlink, inlinelink;
|
||||
unsigned int target_len;
|
||||
char *block_buf = 0;
|
||||
+ int drop_refcount = 0;
|
||||
|
||||
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
|
||||
|
||||
@@ -146,6 +147,14 @@ need_block:
|
||||
}
|
||||
|
||||
/*
|
||||
+ * Update accounting....
|
||||
+ */
|
||||
+ if (!fastlink && !inlinelink)
|
||||
+ ext2fs_block_alloc_stats2(fs, blk, +1);
|
||||
+ ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
|
||||
+ drop_refcount = 1;
|
||||
+
|
||||
+ /*
|
||||
* Link the symlink into the filesystem hierarchy
|
||||
*/
|
||||
if (name) {
|
||||
@@ -161,16 +170,15 @@ need_block:
|
||||
if (retval)
|
||||
goto cleanup;
|
||||
}
|
||||
-
|
||||
- /*
|
||||
- * Update accounting....
|
||||
- */
|
||||
- if (!fastlink && !inlinelink)
|
||||
- ext2fs_block_alloc_stats2(fs, blk, +1);
|
||||
- ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
|
||||
+ drop_refcount = 0;
|
||||
|
||||
cleanup:
|
||||
if (block_buf)
|
||||
ext2fs_free_mem(&block_buf);
|
||||
+ if (drop_refcount) {
|
||||
+ if (!fastlink && !inlinelink)
|
||||
+ ext2fs_block_alloc_stats2(fs, blk, -1);
|
||||
+ ext2fs_inode_alloc_stats2(fs, ino, -1, 0);
|
||||
+ }
|
||||
return retval;
|
||||
}
|
158
tests-add-test-to-excercise-indexed-directories-with.patch
Normal file
158
tests-add-test-to-excercise-indexed-directories-with.patch
Normal file
@ -0,0 +1,158 @@
|
||||
From 4ed27a5bb9bfabaf437b45aaad315f028da5a6f1 Mon Sep 17 00:00:00 2001
|
||||
From: Jan Kara <jack@suse.cz>
|
||||
Date: Thu, 13 Feb 2020 11:16:01 +0100
|
||||
Subject: [PATCH 4/5] tests: add test to excercise indexed directories with
|
||||
metadata_csum
|
||||
References: bsc#1160979
|
||||
|
||||
Indexed directories have somewhat different format when metadata_csum is
|
||||
enabled. Add test to excercise linking in indexed directories and e2fsck
|
||||
rehash code in this case.
|
||||
|
||||
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
|
||||
Signed-off-by: Jan Kara <jack@suse.cz>
|
||||
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
|
||||
Acked-by: Jan Kara <jack@suse.cz>
|
||||
---
|
||||
tests/f_large_dir_csum/expect | 32 ++++++++++++++
|
||||
tests/f_large_dir_csum/is_slow_test | 0
|
||||
tests/f_large_dir_csum/name | 1 +
|
||||
tests/f_large_dir_csum/script | 84 +++++++++++++++++++++++++++++++++++++
|
||||
4 files changed, 117 insertions(+)
|
||||
create mode 100644 tests/f_large_dir_csum/expect
|
||||
create mode 100644 tests/f_large_dir_csum/is_slow_test
|
||||
create mode 100644 tests/f_large_dir_csum/name
|
||||
create mode 100644 tests/f_large_dir_csum/script
|
||||
|
||||
Index: e2fsprogs-1.43.8/tests/f_large_dir_csum/expect
|
||||
===================================================================
|
||||
--- /dev/null
|
||||
+++ e2fsprogs-1.43.8/tests/f_large_dir_csum/expect
|
||||
@@ -0,0 +1,32 @@
|
||||
+Creating filesystem with 30996 1k blocks and 64 inodes
|
||||
+Superblock backups stored on blocks:
|
||||
+ 8193, 24577
|
||||
+
|
||||
+Allocating group tables: done
|
||||
+Writing inode tables: done
|
||||
+Writing superblocks and filesystem accounting information: done
|
||||
+
|
||||
+Pass 1: Checking inodes, blocks, and sizes
|
||||
+Pass 2: Checking directory structure
|
||||
+Pass 3: Checking directory connectivity
|
||||
+Pass 3A: Optimizing directories
|
||||
+Pass 4: Checking reference counts
|
||||
+Inode 13 ref count is 1, should be 5. Fix? yes
|
||||
+
|
||||
+Pass 5: Checking group summary information
|
||||
+
|
||||
+test.img: ***** FILE SYSTEM WAS MODIFIED *****
|
||||
+test.img: 13/64 files (0.0% non-contiguous), 766/30996 blocks
|
||||
+Exit status is 1
|
||||
+Pass 1: Checking inodes, blocks, and sizes
|
||||
+Pass 2: Checking directory structure
|
||||
+Pass 3: Checking directory connectivity
|
||||
+Pass 3A: Optimizing directories
|
||||
+Pass 4: Checking reference counts
|
||||
+Inode 13 ref count is 5, should be 15499. Fix? yes
|
||||
+
|
||||
+Pass 5: Checking group summary information
|
||||
+
|
||||
+test.img: ***** FILE SYSTEM WAS MODIFIED *****
|
||||
+test.img: 13/64 files (0.0% non-contiguous), 5971/30996 blocks
|
||||
+Exit status is 1
|
||||
Index: e2fsprogs-1.43.8/tests/f_large_dir_csum/name
|
||||
===================================================================
|
||||
--- /dev/null
|
||||
+++ e2fsprogs-1.43.8/tests/f_large_dir_csum/name
|
||||
@@ -0,0 +1 @@
|
||||
+optimize 3 level htree directories with metadata checksums
|
||||
Index: e2fsprogs-1.43.8/tests/f_large_dir_csum/script
|
||||
===================================================================
|
||||
--- /dev/null
|
||||
+++ e2fsprogs-1.43.8/tests/f_large_dir_csum/script
|
||||
@@ -0,0 +1,84 @@
|
||||
+OUT=$test_name.log
|
||||
+EXP=$test_dir/expect
|
||||
+E2FSCK=../e2fsck/e2fsck
|
||||
+
|
||||
+NAMELEN=255
|
||||
+DIRENT_SZ=8
|
||||
+BLOCKSZ=1024
|
||||
+INODESZ=128
|
||||
+CSUM_SZ=8
|
||||
+CSUM_TAIL_SZ=12
|
||||
+DIRENT_PER_LEAF=$(((BLOCKSZ - CSUM_TAIL_SZ) / (NAMELEN + DIRENT_SZ)))
|
||||
+HEADER=32
|
||||
+INDEX_SZ=8
|
||||
+INDEX_L1=$(((BLOCKSZ - HEADER - CSUM_SZ) / INDEX_SZ))
|
||||
+INDEX_L2=$(((BLOCKSZ - DIRENT_SZ - CSUM_SZ) / INDEX_SZ))
|
||||
+DIRBLK=$((INDEX_L1 * INDEX_L2))
|
||||
+ENTRIES=$((DIRBLK * DIRENT_PER_LEAF / 3))
|
||||
+# directory leaf blocks - get twice as much because the leaves won't be full
|
||||
+# and there are also other filesystem blocks.
|
||||
+FSIZE=$((DIRBLK * 2))
|
||||
+
|
||||
+$MKE2FS -b 1024 -O extents,64bit,uninit_bg,metadata_csum -N 50 \
|
||||
+ -I $INODESZ -F $TMPFILE $FSIZE > $OUT.new 2>&1
|
||||
+RC=$?
|
||||
+if [ $RC -eq 0 ]; then
|
||||
+{
|
||||
+ # First some initial fs setup to create indexed dir
|
||||
+ echo "mkdir /foo"
|
||||
+ echo "cd /foo"
|
||||
+ touch $TMPFILE.tmp
|
||||
+ echo "write $TMPFILE.tmp foofile"
|
||||
+ i=0
|
||||
+ while test $i -lt $DIRENT_PER_LEAF ; do
|
||||
+ printf "ln foofile f%0254u\n" $i
|
||||
+ i=$((i + 1));
|
||||
+ done
|
||||
+ echo "expand ./"
|
||||
+ printf "ln foofile f%0254u\n" $i
|
||||
+} | $DEBUGFS -w $TMPFILE > /dev/null 2>> $OUT.new
|
||||
+ RC=$?
|
||||
+ # e2fsck should optimize the dir to become indexed
|
||||
+ $E2FSCK -yfD $TMPFILE >> $OUT.new 2>&1
|
||||
+ status=$?
|
||||
+ echo Exit status is $status >> $OUT.new
|
||||
+fi
|
||||
+
|
||||
+if [ $RC -eq 0 ]; then
|
||||
+{
|
||||
+ START=$SECONDS
|
||||
+ i=$(($DIRENT_PER_LEAF+1))
|
||||
+ last=$i
|
||||
+ echo "cd /foo"
|
||||
+ while test $i -lt $ENTRIES ; do
|
||||
+ ELAPSED=$((SECONDS - START))
|
||||
+ if test $((i % 5000)) -eq 0 -a $ELAPSED -gt 10; then
|
||||
+ RATE=$(((i - last) / ELAPSED))
|
||||
+ echo "$test_name: $i/$ENTRIES links, ${ELAPSED}s @ $RATE/s" >&2
|
||||
+ START=$SECONDS
|
||||
+ last=$i
|
||||
+ fi
|
||||
+ printf "ln foofile f%0254u\n" $i
|
||||
+ i=$((i + 1))
|
||||
+ done
|
||||
+} | $DEBUGFS -w $TMPFILE > /dev/null 2>> $OUT.new
|
||||
+ RC=$?
|
||||
+fi
|
||||
+
|
||||
+if [ $RC -eq 0 ]; then
|
||||
+ $E2FSCK -yfD $TMPFILE >> $OUT.new 2>&1
|
||||
+ status=$?
|
||||
+ echo Exit status is $status >> $OUT.new
|
||||
+ sed -f $cmd_dir/filter.sed -e "s;$TMPFILE;test.img;" $OUT.new > $OUT
|
||||
+ rm -f $OUT.new
|
||||
+
|
||||
+ cmp -s $OUT $EXP
|
||||
+ RC=$?
|
||||
+fi
|
||||
+if [ $RC -eq 0 ]; then
|
||||
+ echo "$test_name: $test_description: ok"
|
||||
+ touch $test_name.ok
|
||||
+else
|
||||
+ echo "$test_name: $test_description: failed"
|
||||
+ diff -u $EXP $OUT > $test_name.failed
|
||||
+fi
|
391
tune2fs-update-dir-checksums-when-clearing-dir_index.patch
Normal file
391
tune2fs-update-dir-checksums-when-clearing-dir_index.patch
Normal file
@ -0,0 +1,391 @@
|
||||
From 514fde0296c2085fb11685820f3d396d5c41026e Mon Sep 17 00:00:00 2001
|
||||
From: Jan Kara <jack@suse.cz>
|
||||
Date: Thu, 13 Feb 2020 11:16:02 +0100
|
||||
Subject: [PATCH 5/5] tune2fs: update dir checksums when clearing dir_index
|
||||
feature
|
||||
References: bsc#1160979
|
||||
|
||||
When clearing dir_index feature while metadata_csum is enabled, we have
|
||||
to rewrite checksums of all indexed directories to update checksums of
|
||||
internal tree nodes.
|
||||
|
||||
Signed-off-by: Jan Kara <jack@suse.cz>
|
||||
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
|
||||
Acked-by: Jan Kara <jack@suse.cz>
|
||||
---
|
||||
misc/tune2fs.c | 143 ++++++++++++++++++++++++++++++++++++++-------------------
|
||||
1 file changed, 95 insertions(+), 48 deletions(-)
|
||||
|
||||
Index: e2fsprogs-1.43.8/misc/tune2fs.c
|
||||
===================================================================
|
||||
--- e2fsprogs-1.43.8.orig/misc/tune2fs.c
|
||||
+++ e2fsprogs-1.43.8/misc/tune2fs.c
|
||||
@@ -553,7 +553,8 @@ struct rewrite_dir_context {
|
||||
char *buf;
|
||||
errcode_t errcode;
|
||||
ext2_ino_t dir;
|
||||
- int is_htree;
|
||||
+ int is_htree:1;
|
||||
+ int clear_htree:1;
|
||||
};
|
||||
|
||||
static int rewrite_dir_block(ext2_filsys fs,
|
||||
@@ -572,8 +573,13 @@ static int rewrite_dir_block(ext2_filsys
|
||||
if (ctx->errcode)
|
||||
return BLOCK_ABORT;
|
||||
|
||||
- /* if htree node... */
|
||||
- if (ctx->is_htree)
|
||||
+ /*
|
||||
+ * if htree node... Note that if we are clearing htree structures from
|
||||
+ * the directory, we treat the htree internal block as an ordinary leaf.
|
||||
+ * The code below will do the right thing and make space for checksum
|
||||
+ * there.
|
||||
+ */
|
||||
+ if (ctx->is_htree && !ctx->clear_htree)
|
||||
ext2fs_get_dx_countlimit(fs, (struct ext2_dir_entry *)ctx->buf,
|
||||
&dcl, &dcl_offset);
|
||||
if (dcl) {
|
||||
@@ -702,7 +708,8 @@ static errcode_t rewrite_directory(ext2_
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
- ctx.is_htree = (inode->i_flags & EXT2_INDEX_FL);
|
||||
+ ctx.is_htree = !!(inode->i_flags & EXT2_INDEX_FL);
|
||||
+ ctx.clear_htree = !ext2fs_has_feature_dir_index(fs->super);
|
||||
ctx.dir = dir;
|
||||
ctx.errcode = 0;
|
||||
retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY |
|
||||
@@ -713,121 +720,169 @@ static errcode_t rewrite_directory(ext2_
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
+ if (ctx.is_htree && ctx.clear_htree) {
|
||||
+ inode->i_flags &= ~EXT2_INDEX_FL;
|
||||
+ retval = ext2fs_write_inode(fs, dir, inode);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ }
|
||||
+
|
||||
return ctx.errcode;
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Forcibly set checksums in all inodes.
|
||||
- */
|
||||
-static void rewrite_inodes(ext2_filsys fs)
|
||||
+struct rewrite_context {
|
||||
+ ext2_filsys fs;
|
||||
+ struct ext2_inode *zero_inode;
|
||||
+ char *ea_buf;
|
||||
+ int inode_size;
|
||||
+};
|
||||
+
|
||||
+static void rewrite_one_inode(struct rewrite_context *ctx, ext2_ino_t ino,
|
||||
+ struct ext2_inode *inode)
|
||||
{
|
||||
- int length = EXT2_INODE_SIZE(fs->super);
|
||||
- struct ext2_inode *inode, *zero;
|
||||
- char *ea_buf;
|
||||
- ext2_inode_scan scan;
|
||||
- errcode_t retval;
|
||||
- ext2_ino_t ino;
|
||||
- blk64_t file_acl_block;
|
||||
- int inode_dirty;
|
||||
+ ext2_filsys fs = ctx->fs;
|
||||
+ int inode_dirty;
|
||||
+ errcode_t retval;
|
||||
+ blk64_t file_acl_block;
|
||||
|
||||
- if (fs->super->s_creator_os == EXT2_OS_HURD)
|
||||
- return;
|
||||
+ if (ext2fs_test_inode_bitmap2(fs->inode_map, ino)) {
|
||||
+ inode_dirty = 1;
|
||||
+ } else {
|
||||
+ if (memcmp(inode, ctx->zero_inode, ctx->inode_size) != 0) {
|
||||
+ memset(inode, 0, ctx->inode_size);
|
||||
+ inode_dirty = 1;
|
||||
+ } else {
|
||||
+ inode_dirty = 0;
|
||||
+ }
|
||||
+ }
|
||||
|
||||
- retval = ext2fs_open_inode_scan(fs, 0, &scan);
|
||||
+ if (inode_dirty) {
|
||||
+ retval = ext2fs_write_inode_full(fs, ino, inode,
|
||||
+ ctx->inode_size);
|
||||
+ if (retval) {
|
||||
+ com_err("set_csum", retval, "while writing "
|
||||
+ "inode");
|
||||
+ exit(1);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ retval = rewrite_extents(fs, ino, inode);
|
||||
if (retval) {
|
||||
- com_err("set_csum", retval, "while opening inode scan");
|
||||
+ com_err("rewrite_extents", retval,
|
||||
+ "while rewriting extents");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
- retval = ext2fs_get_mem(length, &inode);
|
||||
+ if (LINUX_S_ISDIR(inode->i_mode) &&
|
||||
+ ext2fs_inode_has_valid_blocks2(fs, inode)) {
|
||||
+ retval = rewrite_directory(fs, ino, inode);
|
||||
+ if (retval) {
|
||||
+ com_err("rewrite_directory", retval,
|
||||
+ "while rewriting directories");
|
||||
+ exit(1);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ file_acl_block = ext2fs_file_acl_block(fs, inode);
|
||||
+ if (!file_acl_block)
|
||||
+ return;
|
||||
+ retval = ext2fs_read_ext_attr3(fs, file_acl_block, ctx->ea_buf, ino);
|
||||
if (retval) {
|
||||
- com_err("set_csum", retval, "while allocating memory");
|
||||
+ com_err("rewrite_eablock", retval,
|
||||
+ "while rewriting extended attribute");
|
||||
+ exit(1);
|
||||
+ }
|
||||
+ retval = ext2fs_write_ext_attr3(fs, file_acl_block, ctx->ea_buf,
|
||||
+ ino);
|
||||
+ if (retval) {
|
||||
+ com_err("rewrite_eablock", retval,
|
||||
+ "while rewriting extended attribute");
|
||||
exit(1);
|
||||
}
|
||||
+}
|
||||
+
|
||||
+#define REWRITE_DIR_FL 0x02 /* Rewrite directories */
|
||||
+#define REWRITE_NONDIR_FL 0x04 /* Rewrite other inodes */
|
||||
+#define REWRITE_ALL (REWRITE_DIR_FL | REWRITE_NONDIR_FL)
|
||||
+
|
||||
+static void rewrite_inodes_pass(struct rewrite_context *ctx, unsigned int flags)
|
||||
+{
|
||||
+ ext2_filsys fs = ctx->fs;
|
||||
+ struct ext2_inode *inode;
|
||||
+ ext2_inode_scan scan;
|
||||
+ errcode_t retval;
|
||||
+ ext2_ino_t ino;
|
||||
+ int rewrite;
|
||||
|
||||
- retval = ext2fs_get_memzero(length, &zero);
|
||||
+ if (fs->super->s_creator_os == EXT2_OS_HURD)
|
||||
+ return;
|
||||
+
|
||||
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
|
||||
if (retval) {
|
||||
- com_err("set_csum", retval, "while allocating memory");
|
||||
+ com_err("set_csum", retval, "while opening inode scan");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
- retval = ext2fs_get_mem(fs->blocksize, &ea_buf);
|
||||
+ retval = ext2fs_get_mem(ctx->inode_size, &inode);
|
||||
if (retval) {
|
||||
com_err("set_csum", retval, "while allocating memory");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
do {
|
||||
- retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
|
||||
+ retval = ext2fs_get_next_inode_full(scan, &ino, inode, ctx->inode_size);
|
||||
if (retval) {
|
||||
com_err("set_csum", retval, "while getting next inode");
|
||||
exit(1);
|
||||
}
|
||||
if (!ino)
|
||||
break;
|
||||
- if (ext2fs_test_inode_bitmap2(fs->inode_map, ino)) {
|
||||
- inode_dirty = 1;
|
||||
+ rewrite = 0;
|
||||
+ if (LINUX_S_ISDIR(inode->i_mode)) {
|
||||
+ if (flags & REWRITE_DIR_FL)
|
||||
+ rewrite = 1;
|
||||
} else {
|
||||
- if (memcmp(inode, zero, length) != 0) {
|
||||
- memset(inode, 0, length);
|
||||
- inode_dirty = 1;
|
||||
- } else {
|
||||
- inode_dirty = 0;
|
||||
- }
|
||||
+ if (flags & REWRITE_NONDIR_FL)
|
||||
+ rewrite = 1;
|
||||
}
|
||||
+ if (rewrite)
|
||||
+ rewrite_one_inode(ctx, ino, inode);
|
||||
+ } while (ino);
|
||||
|
||||
- if (inode_dirty) {
|
||||
- retval = ext2fs_write_inode_full(fs, ino, inode,
|
||||
- length);
|
||||
- if (retval) {
|
||||
- com_err("set_csum", retval, "while writing "
|
||||
- "inode");
|
||||
- exit(1);
|
||||
- }
|
||||
- }
|
||||
+ ext2fs_free_mem(&inode);
|
||||
+ ext2fs_close_inode_scan(scan);
|
||||
+}
|
||||
|
||||
- retval = rewrite_extents(fs, ino, inode);
|
||||
- if (retval) {
|
||||
- com_err("rewrite_extents", retval,
|
||||
- "while rewriting extents");
|
||||
- exit(1);
|
||||
- }
|
||||
+/*
|
||||
+ * Forcibly rewrite checksums in inodes specified by 'flags'
|
||||
+ */
|
||||
+static void rewrite_inodes(ext2_filsys fs, unsigned int flags)
|
||||
+{
|
||||
+ struct rewrite_context ctx = {
|
||||
+ .fs = fs,
|
||||
+ .inode_size = EXT2_INODE_SIZE(fs->super),
|
||||
+ };
|
||||
+ errcode_t retval;
|
||||
|
||||
- if (LINUX_S_ISDIR(inode->i_mode) &&
|
||||
- ext2fs_inode_has_valid_blocks2(fs, inode)) {
|
||||
- retval = rewrite_directory(fs, ino, inode);
|
||||
- if (retval) {
|
||||
- com_err("rewrite_directory", retval,
|
||||
- "while rewriting directories");
|
||||
- exit(1);
|
||||
- }
|
||||
- }
|
||||
+ retval = ext2fs_get_memzero(ctx.inode_size, &ctx.zero_inode);
|
||||
+ if (retval) {
|
||||
+ com_err("set_csum", retval, "while allocating memory");
|
||||
+ exit(1);
|
||||
+ }
|
||||
|
||||
- file_acl_block = ext2fs_file_acl_block(fs, inode);
|
||||
- if (!file_acl_block)
|
||||
- continue;
|
||||
- retval = ext2fs_read_ext_attr3(fs, file_acl_block, ea_buf, ino);
|
||||
- if (retval) {
|
||||
- com_err("rewrite_eablock", retval,
|
||||
- "while rewriting extended attribute");
|
||||
- exit(1);
|
||||
- }
|
||||
- retval = ext2fs_write_ext_attr3(fs, file_acl_block, ea_buf,
|
||||
- ino);
|
||||
- if (retval) {
|
||||
- com_err("rewrite_eablock", retval,
|
||||
- "while rewriting extended attribute");
|
||||
- exit(1);
|
||||
- }
|
||||
- } while (ino);
|
||||
+ retval = ext2fs_get_mem(fs->blocksize, &ctx.ea_buf);
|
||||
+ if (retval) {
|
||||
+ com_err("set_csum", retval, "while allocating memory");
|
||||
+ exit(1);
|
||||
+ }
|
||||
|
||||
- ext2fs_free_mem(&zero);
|
||||
- ext2fs_free_mem(&inode);
|
||||
- ext2fs_free_mem(&ea_buf);
|
||||
- ext2fs_close_inode_scan(scan);
|
||||
+ rewrite_inodes_pass(&ctx, flags);
|
||||
+
|
||||
+ ext2fs_free_mem(&ctx.zero_inode);
|
||||
+ ext2fs_free_mem(&ctx.ea_buf);
|
||||
}
|
||||
|
||||
-static void rewrite_metadata_checksums(ext2_filsys fs)
|
||||
+static void rewrite_metadata_checksums(ext2_filsys fs, unsigned int flags)
|
||||
{
|
||||
errcode_t retval;
|
||||
dgrp_t i;
|
||||
@@ -842,7 +897,7 @@ static void rewrite_metadata_checksums(e
|
||||
"while reading bitmaps");
|
||||
exit(1);
|
||||
}
|
||||
- rewrite_inodes(fs);
|
||||
+ rewrite_inodes(fs, flags);
|
||||
ext2fs_mark_ib_dirty(fs);
|
||||
ext2fs_mark_bb_dirty(fs);
|
||||
ext2fs_mmp_update2(fs, 1);
|
||||
@@ -1141,6 +1196,23 @@ mmp_error:
|
||||
uuid_generate((unsigned char *) sb->s_hash_seed);
|
||||
}
|
||||
|
||||
+ if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX) &&
|
||||
+ ext2fs_has_feature_metadata_csum(sb)) {
|
||||
+ check_fsck_needed(fs,
|
||||
+ _("Disabling directory index on filesystem with "
|
||||
+ "checksums could take some time."));
|
||||
+ if (mount_flags & EXT2_MF_MOUNTED) {
|
||||
+ fputs(_("Cannot disable dir_index on a mounted "
|
||||
+ "filesystem!\n"), stderr);
|
||||
+ exit(1);
|
||||
+ }
|
||||
+ /*
|
||||
+ * Clearing dir_index on checksummed filesystem requires
|
||||
+ * rewriting all directories to update checksums.
|
||||
+ */
|
||||
+ rewrite_checksums |= REWRITE_DIR_FL;
|
||||
+ }
|
||||
+
|
||||
if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG)) {
|
||||
if (ext2fs_check_desc(fs)) {
|
||||
fputs(_("Clearing the flex_bg flag would "
|
||||
@@ -1184,7 +1256,7 @@ mmp_error:
|
||||
"The larger fields afforded by this feature "
|
||||
"enable full-strength checksumming. "
|
||||
"Run resize2fs -b to rectify.\n"));
|
||||
- rewrite_checksums = 1;
|
||||
+ rewrite_checksums = REWRITE_ALL;
|
||||
/* metadata_csum supersedes uninit_bg */
|
||||
ext2fs_clear_feature_gdt_csum(fs->super);
|
||||
|
||||
@@ -1212,7 +1284,7 @@ mmp_error:
|
||||
"filesystem!\n"), stderr);
|
||||
exit(1);
|
||||
}
|
||||
- rewrite_checksums = 1;
|
||||
+ rewrite_checksums = REWRITE_ALL;
|
||||
|
||||
/* Enable uninit_bg unless the user expressly turned it off */
|
||||
memcpy(test_features, old_features, sizeof(test_features));
|
||||
@@ -1375,7 +1447,7 @@ mmp_error:
|
||||
return 1;
|
||||
}
|
||||
|
||||
- rewrite_checksums = 1;
|
||||
+ rewrite_checksums = REWRITE_ALL;
|
||||
}
|
||||
|
||||
if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
|
||||
@@ -3148,7 +3220,7 @@ retry_open:
|
||||
ext2fs_mark_super_dirty(fs);
|
||||
if (ext2fs_has_feature_metadata_csum(fs->super) &&
|
||||
!ext2fs_has_feature_csum_seed(fs->super))
|
||||
- rewrite_checksums = 1;
|
||||
+ rewrite_checksums = REWRITE_ALL;
|
||||
}
|
||||
|
||||
if (I_flag) {
|
||||
@@ -3180,7 +3252,7 @@ retry_open:
|
||||
if (retval == 0) {
|
||||
printf(_("Setting inode size %lu\n"),
|
||||
new_inode_size);
|
||||
- rewrite_checksums = 1;
|
||||
+ rewrite_checksums = REWRITE_ALL;
|
||||
} else {
|
||||
printf("%s", _("Failed to change inode size\n"));
|
||||
rc = 1;
|
||||
@@ -3189,7 +3261,7 @@ retry_open:
|
||||
}
|
||||
|
||||
if (rewrite_checksums)
|
||||
- rewrite_metadata_checksums(fs);
|
||||
+ rewrite_metadata_checksums(fs, rewrite_checksums);
|
||||
|
||||
if (l_flag)
|
||||
list_super(sb);
|
Loading…
Reference in New Issue
Block a user