From: Tahsin Erdogan <tahsin@google.com>
To: Andreas Dilger <adilger@dilger.ca>,
"Darrick J . Wong" <darrick.wong@oracle.com>,
Theodore Ts'o <tytso@mit.edu>,
linux-ext4@vger.kernel.org
Cc: Tahsin Erdogan <tahsin@google.com>
Subject: [PATCH 09/12] tune2fs: update ea_inode hashes when fs uuid changes
Date: Mon, 26 Jun 2017 06:43:45 -0700 [thread overview]
Message-ID: <20170626134348.1240-9-tahsin@google.com> (raw)
In-Reply-To: <20170626134348.1240-1-tahsin@google.com>
Extended attribute inodes maintain a crc32c hash that is used for
deduplication. The crc seed derives from uuid so ea_inode hashes
must be updated when uuid changes.
The ea_inode hash is also incorporated into the xattr entry e_hash
so the entries that reference the inode also must be updated.
Signed-off-by: Tahsin Erdogan <tahsin@google.com>
---
lib/ext2fs/csum.c | 3 +-
lib/ext2fs/ext2fs.h | 4 +
lib/ext2fs/ext_attr.c | 44 +++++++-
misc/tune2fs.c | 299 ++++++++++++++++++++++++++++++++++----------------
4 files changed, 249 insertions(+), 101 deletions(-)
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index e67850fa47af..214a099762ee 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -34,7 +34,8 @@ void ext2fs_init_csum_seed(ext2_filsys fs)
{
if (ext2fs_has_feature_csum_seed(fs->super))
fs->csum_seed = fs->super->s_checksum_seed;
- else if (ext2fs_has_feature_metadata_csum(fs->super))
+ else if (ext2fs_has_feature_metadata_csum(fs->super) ||
+ ext2fs_has_feature_ea_inode(fs->super))
fs->csum_seed = ext2fs_crc32c_le(~0, fs->super->s_uuid,
sizeof(fs->super->s_uuid));
}
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index f72cbe17a8cd..90df2182bd7b 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1247,6 +1247,10 @@ errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
#define XATTR_HANDLE_FLAG_RAW 0x0001
errcode_t ext2fs_xattrs_flags(struct ext2_xattr_handle *handle,
unsigned int *new_flags, unsigned int *old_flags);
+extern void ext2fs_ext_attr_block_rehash(struct ext2_ext_attr_header *header,
+ struct ext2_ext_attr_entry *end);
+extern __u32 ext2fs_get_ea_inode_hash(struct ext2_inode *inode);
+extern void ext2fs_set_ea_inode_hash(struct ext2_inode *inode, __u32 hash);
/* extent.c */
extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index d48ab5c55270..41789c7dcbf2 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -33,7 +33,7 @@ static errcode_t read_ea_inode_hash(ext2_filsys fs, ext2_ino_t ino, __u32 *hash)
retval = ext2fs_read_inode(fs, ino, &inode);
if (retval)
return retval;
- *hash = inode.i_atime;
+ *hash = ext2fs_get_ea_inode_hash(&inode);
return 0;
}
@@ -100,6 +100,45 @@ errcode_t ext2fs_ext_attr_hash_entry2(ext2_filsys fs,
return 0;
}
+#undef NAME_HASH_SHIFT
+#undef VALUE_HASH_SHIFT
+
+#define BLOCK_HASH_SHIFT 16
+
+/* Mirrors ext4_xattr_rehash() implementation in kernel. */
+void ext2fs_ext_attr_block_rehash(struct ext2_ext_attr_header *header,
+ struct ext2_ext_attr_entry *end)
+{
+ struct ext2_ext_attr_entry *here;
+ __u32 hash = 0;
+
+ here = (struct ext2_ext_attr_entry *)(header+1);
+ while (here < end && !EXT2_EXT_IS_LAST_ENTRY(here)) {
+ if (!here->e_hash) {
+ /* Block is not shared if an entry's hash value == 0 */
+ hash = 0;
+ break;
+ }
+ hash = (hash << BLOCK_HASH_SHIFT) ^
+ (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^
+ here->e_hash;
+ here = EXT2_EXT_ATTR_NEXT(here);
+ }
+ header->h_hash = hash;
+}
+
+#undef BLOCK_HASH_SHIFT
+
+__u32 ext2fs_get_ea_inode_hash(struct ext2_inode *inode)
+{
+ return inode->i_atime;
+}
+
+void ext2fs_set_ea_inode_hash(struct ext2_inode *inode, __u32 hash)
+{
+ inode->i_atime = hash;
+}
+
static errcode_t check_ext_attr_header(struct ext2_ext_attr_header *header)
{
if ((header->h_magic != EXT2_EXT_ATTR_MAGIC_v1 &&
@@ -110,9 +149,6 @@ static errcode_t check_ext_attr_header(struct ext2_ext_attr_header *header)
return 0;
}
-#undef NAME_HASH_SHIFT
-#undef VALUE_HASH_SHIFT
-
errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block, void *buf,
ext2_ino_t inum)
{
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 2a437b9fc7b7..9b53dc28e9cb 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -718,115 +718,224 @@ static errcode_t rewrite_directory(ext2_filsys fs, ext2_ino_t dir,
return ctx.errcode;
}
+/*
+ * Context information that does not change across rewrite_one_inode()
+ * invocations.
+ */
+struct rewrite_context {
+ ext2_filsys fs;
+ struct ext2_inode *zero_inode;
+ char *ea_buf;
+ int inode_size;
+};
+
+#define fatal_err(code, args...) \
+ do { \
+ com_err(__func__, code, args); \
+ exit(1); \
+ } while (0);
+
+static void update_ea_inode_hash(struct rewrite_context *ctx, ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ errcode_t retval;
+ ext2_file_t file;
+ __u32 hash;
+
+ retval = ext2fs_file_open(ctx->fs, ino, 0, &file);
+ if (retval)
+ fatal_err(retval, "open ea_inode");
+ retval = ext2fs_file_read(file, ctx->ea_buf, inode->i_size,
+ NULL);
+ if (retval)
+ fatal_err(retval, "read ea_inode");
+ retval = ext2fs_file_close(file);
+ if (retval)
+ fatal_err(retval, "close ea_inode");
+
+ hash = ext2fs_crc32c_le(ctx->fs->csum_seed, ctx->ea_buf, inode->i_size);
+ ext2fs_set_ea_inode_hash(inode, hash);
+}
+
+static int update_xattr_entry_hashes(ext2_filsys fs,
+ struct ext2_ext_attr_entry *entry,
+ struct ext2_ext_attr_entry *end)
+{
+ int modified = 0;
+ errcode_t retval;
+
+ while (entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ if (entry->e_value_inum) {
+ retval = ext2fs_ext_attr_hash_entry2(fs, entry, NULL,
+ &entry->e_hash);
+ if (retval)
+ fatal_err(retval, "hash ea_inode entry");
+ modified = 1;
+ }
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+ return modified;
+}
+
+static void update_inline_xattr_hashes(struct rewrite_context *ctx,
+ struct ext2_inode_large *inode)
+{
+ struct ext2_ext_attr_entry *start, *end;
+ __u32 *ea_magic;
+
+ if (inode->i_extra_isize == 0)
+ return;
+
+ if (inode->i_extra_isize & 3 ||
+ inode->i_extra_isize > ctx->inode_size - EXT2_GOOD_OLD_INODE_SIZE)
+ fatal_err(EXT2_ET_INODE_CORRUPTED, "bad i_extra_isize")
+
+ ea_magic = (__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize);
+ if (*ea_magic != EXT2_EXT_ATTR_MAGIC)
+ return;
+
+ start = (struct ext2_ext_attr_entry *)(ea_magic + 1);
+ end = (struct ext2_ext_attr_entry *)((char *)inode + ctx->inode_size);
+
+ update_xattr_entry_hashes(ctx->fs, start, end);
+}
+
+static void update_block_xattr_hashes(struct rewrite_context *ctx,
+ void *block_buf)
+{
+ struct ext2_ext_attr_header *header;
+ struct ext2_ext_attr_entry *start, *end;
+
+ header = (struct ext2_ext_attr_header *)block_buf;
+ if (header->h_magic != EXT2_EXT_ATTR_MAGIC)
+ return;
+
+ start = (struct ext2_ext_attr_entry *)(header+1);
+ end = (struct ext2_ext_attr_entry *)(block_buf + ctx->fs->blocksize);
+
+ if (update_xattr_entry_hashes(ctx->fs, start, end))
+ ext2fs_ext_attr_block_rehash(header, end);
+}
+
+static void rewrite_one_inode(struct rewrite_context *ctx, ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ blk64_t file_acl_block;
+ errcode_t retval;
+
+ if (!ext2fs_test_inode_bitmap2(ctx->fs->inode_map, ino)) {
+ if (!memcmp(inode, ctx->zero_inode, ctx->inode_size))
+ return;
+ memset(inode, 0, ctx->inode_size);
+ }
+
+ if (inode->i_flags & EXT4_EA_INODE_FL)
+ update_ea_inode_hash(ctx, ino, inode);
+
+ if (ctx->inode_size != EXT2_GOOD_OLD_INODE_SIZE)
+ update_inline_xattr_hashes(ctx,
+ (struct ext2_inode_large *)inode);
+
+ retval = ext2fs_write_inode_full(ctx->fs, ino, inode, ctx->inode_size);
+ if (retval)
+ fatal_err(retval, "while writing inode");
+
+ retval = rewrite_extents(ctx->fs, ino, inode);
+ if (retval)
+ fatal_err(retval, "while rewriting extents");
+
+ if (LINUX_S_ISDIR(inode->i_mode) &&
+ ext2fs_inode_has_valid_blocks2(ctx->fs, inode)) {
+ retval = rewrite_directory(ctx->fs, ino, inode);
+ if (retval)
+ fatal_err(retval, "while rewriting directories");
+ }
+
+ file_acl_block = ext2fs_file_acl_block(ctx->fs, inode);
+ if (!file_acl_block)
+ return;
+
+ retval = ext2fs_read_ext_attr3(ctx->fs, file_acl_block, ctx->ea_buf,
+ ino);
+ if (retval)
+ fatal_err(retval, "while rewriting extended attribute");
+
+ update_block_xattr_hashes(ctx, ctx->ea_buf);
+ retval = ext2fs_write_ext_attr3(ctx->fs, file_acl_block, ctx->ea_buf,
+ ino);
+ if (retval)
+ fatal_err(retval, "while rewriting extended attribute");
+}
+
/*
* Forcibly set checksums in all inodes.
*/
static void rewrite_inodes(ext2_filsys fs)
{
- 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;
+ struct ext2_inode *inode;
+ int pass;
+ struct rewrite_context ctx = {
+ .fs = fs,
+ .inode_size = EXT2_INODE_SIZE(fs->super),
+ };
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 opening inode scan");
- exit(1);
- }
-
- retval = ext2fs_get_mem(length, &inode);
- if (retval) {
- com_err("set_csum", retval, "while allocating memory");
- exit(1);
- }
-
- retval = ext2fs_get_memzero(length, &zero);
- if (retval) {
- com_err("set_csum", retval, "while allocating memory");
- exit(1);
- }
+ retval = ext2fs_get_mem(ctx.inode_size, &inode);
+ if (retval)
+ fatal_err(retval, "while allocating memory");
- retval = ext2fs_get_mem(fs->blocksize, &ea_buf);
- if (retval) {
- com_err("set_csum", retval, "while allocating memory");
- exit(1);
- }
+ retval = ext2fs_get_memzero(ctx.inode_size, &ctx.zero_inode);
+ if (retval)
+ fatal_err(retval, "while allocating memory");
- do {
- retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
- 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;
- } else {
- if (memcmp(inode, zero, length) != 0) {
- memset(inode, 0, length);
- inode_dirty = 1;
- } else {
- inode_dirty = 0;
- }
- }
+ retval = ext2fs_get_mem(64 * 1024, &ctx.ea_buf);
+ if (retval)
+ fatal_err(retval, "while allocating memory");
- if (inode_dirty) {
- retval = ext2fs_write_inode_full(fs, ino, inode,
- length);
- if (retval) {
- com_err("set_csum", retval, "while writing "
- "inode");
- exit(1);
- }
- }
+ /*
+ * Extended attribute inodes have a lookup hash that needs to be
+ * recalculated with the new csum_seed. Other inodes referencing xattr
+ * inodes need this value to be up to date. That's why we do two passes:
+ *
+ * pass 1: update xattr inodes to update their lookup hash as well as
+ * other checksums.
+ *
+ * pass 2: go over other inodes to update their checksums.
+ */
+ if (ext2fs_has_feature_ea_inode(fs->super))
+ pass = 1;
+ else
+ pass = 2;
+ for (;pass <= 2; pass++) {
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval)
+ fatal_err(retval, "while opening inode scan");
- retval = rewrite_extents(fs, ino, inode);
- if (retval) {
- com_err("rewrite_extents", retval,
- "while rewriting extents");
- exit(1);
- }
+ do {
+ retval = ext2fs_get_next_inode_full(scan, &ino, inode,
+ ctx.inode_size);
+ if (retval)
+ fatal_err(retval, "while getting next inode");
+ if (!ino)
+ break;
- 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);
- }
- }
+ if (pass == 1 && (inode->i_flags & EXT4_EA_INODE_FL) ||
+ pass == 2 && !(inode->i_flags & EXT4_EA_INODE_FL))
+ rewrite_one_inode(&ctx, ino, inode);
+ } while (ino);
- 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);
+ ext2fs_close_inode_scan(scan);
+ }
- ext2fs_free_mem(&zero);
+ ext2fs_free_mem(&ctx.zero_inode);
+ ext2fs_free_mem(&ctx.ea_buf);
ext2fs_free_mem(&inode);
- ext2fs_free_mem(&ea_buf);
- ext2fs_close_inode_scan(scan);
}
static void rewrite_metadata_checksums(ext2_filsys fs)
@@ -839,11 +948,8 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
for (i = 0; i < fs->group_desc_count; i++)
ext2fs_group_desc_csum_set(fs, i);
retval = ext2fs_read_bitmaps(fs);
- if (retval) {
- com_err("rewrite_metadata_checksums", retval,
- "while reading bitmaps");
- exit(1);
- }
+ if (retval)
+ fatal_err(retval, "while reading bitmaps");
rewrite_inodes(fs);
ext2fs_mark_ib_dirty(fs);
ext2fs_mark_bb_dirty(fs);
@@ -3135,8 +3241,9 @@ retry_open:
}
ext2fs_mark_super_dirty(fs);
- if (ext2fs_has_feature_metadata_csum(fs->super) &&
- !ext2fs_has_feature_csum_seed(fs->super))
+ if (!ext2fs_has_feature_csum_seed(fs->super) &&
+ (ext2fs_has_feature_metadata_csum(fs->super) ||
+ ext2fs_has_feature_ea_inode(fs->super)))
rewrite_checksums = 1;
}
--
2.13.1.611.g7e3b11ae1-goog
next prev parent reply other threads:[~2017-06-26 13:44 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-06-26 13:43 [PATCH 01/12] e2fsck: add support for large xattrs in external inodes Tahsin Erdogan
2017-06-26 13:43 ` [PATCH 02/12] tune2fs: do not allow disabling ea_inode feature Tahsin Erdogan
2017-06-26 13:43 ` [PATCH 03/12] e2fsck: ea_inode hash validation Tahsin Erdogan
2017-06-26 21:21 ` Andreas Dilger
2017-06-28 1:41 ` [PATCH v2 " Tahsin Erdogan
2017-06-28 1:47 ` Tahsin Erdogan
2017-06-26 13:43 ` [PATCH 04/12] e2fsck: do not early terminate extra space check Tahsin Erdogan
2017-06-26 21:23 ` Andreas Dilger
2017-06-26 23:57 ` Tahsin Erdogan
2017-06-26 13:43 ` [PATCH 05/12] e2fsck: generalize ea_refcount Tahsin Erdogan
2017-06-26 13:43 ` [PATCH 06/12] e2fsck: update i_blocks accounting for ea_inode feature Tahsin Erdogan
2017-06-26 21:36 ` Andreas Dilger
2017-06-27 11:37 ` Tahsin Erdogan
2017-06-30 1:39 ` Tahsin Erdogan
2017-06-26 13:43 ` [PATCH 07/12] e2fsck: track ea_inode references Tahsin Erdogan
2017-06-26 21:45 ` Andreas Dilger
2017-06-27 1:25 ` Tahsin Erdogan
2017-06-27 18:43 ` Andreas Dilger
2017-06-28 1:26 ` Tahsin Erdogan
2017-06-26 13:43 ` [PATCH 08/12] e2fsck: add test for ea_inode feature Tahsin Erdogan
2017-06-26 13:43 ` Tahsin Erdogan [this message]
2017-06-26 13:43 ` [PATCH 10/12] fuse2fs: refuse to mount fs with " Tahsin Erdogan
2017-06-26 13:43 ` [PATCH 11/12] mke2fs: ea_inode is not supported for hurd Tahsin Erdogan
2017-06-26 13:43 ` [PATCH 12/12] resize2fs: moving xattr inodes is not supported Tahsin Erdogan
2017-07-05 4:04 ` [PATCH 01/12] e2fsck: add support for large xattrs in external inodes Theodore Ts'o
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170626134348.1240-9-tahsin@google.com \
--to=tahsin@google.com \
--cc=adilger@dilger.ca \
--cc=darrick.wong@oracle.com \
--cc=linux-ext4@vger.kernel.org \
--cc=tytso@mit.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.