mirror of https://github.com/vitalif/e2fsprogs
Improve support for in-inode EA's
Add vertificaton of the in-inode EA information, and allow in-inode EA's to have a checksum. Signed-off-by: Andreas Dilger <adilger@clusterfs.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>bitmap-optimize
parent
0465bee402
commit
fefaef39e0
|
@ -481,6 +481,9 @@ extern void init_resource_track(struct resource_track *track,
|
|||
extern int inode_has_valid_blocks(struct ext2_inode *inode);
|
||||
extern void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
|
||||
struct ext2_inode * inode, const char * proc);
|
||||
extern void e2fsck_read_inode_full(e2fsck_t ctx, unsigned long ino,
|
||||
struct ext2_inode *inode,
|
||||
const int bufsize, const char *proc);
|
||||
extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
|
||||
struct ext2_inode * inode, const char * proc);
|
||||
extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
|
||||
|
|
|
@ -264,6 +264,7 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
|
|||
remain = storage_size - sizeof(__u32);
|
||||
|
||||
while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
|
||||
__u32 hash;
|
||||
|
||||
/* header eats this space */
|
||||
remain -= sizeof(struct ext2_ext_attr_entry);
|
||||
|
@ -291,9 +292,12 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
|
|||
problem = PR_1_ATTR_VALUE_BLOCK;
|
||||
goto fix;
|
||||
}
|
||||
|
||||
/* e_hash must be 0 in inode's ea */
|
||||
if (entry->e_hash != 0) {
|
||||
|
||||
hash = ext2fs_ext_attr_hash_entry(entry,
|
||||
start + entry->e_value_offs);
|
||||
|
||||
/* e_hash may be 0 in older inode's ea */
|
||||
if (entry->e_hash != 0 && entry->e_hash != hash) {
|
||||
pctx->num = entry->e_hash;
|
||||
problem = PR_1_ATTR_HASH;
|
||||
goto fix;
|
||||
|
@ -308,13 +312,10 @@ fix:
|
|||
* it seems like a corruption. it's very unlikely we could repair
|
||||
* EA(s) in automatic fashion -bzzz
|
||||
*/
|
||||
#if 0
|
||||
problem = PR_1_ATTR_HASH;
|
||||
#endif
|
||||
if (problem == 0 || !fix_problem(ctx, problem, pctx))
|
||||
return;
|
||||
|
||||
/* simple remove all possible EA(s) */
|
||||
/* simply remove all possible EA(s) */
|
||||
*((__u32 *)start) = 0UL;
|
||||
e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
|
||||
EXT2_INODE_SIZE(sb), "pass1");
|
||||
|
@ -1388,10 +1389,13 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
|
|||
entry = (struct ext2_ext_attr_entry *)(header+1);
|
||||
end = block_buf + fs->blocksize;
|
||||
while ((char *)entry < end && *(__u32 *)entry) {
|
||||
__u32 hash;
|
||||
|
||||
if (region_allocate(region, (char *)entry - (char *)header,
|
||||
EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
|
||||
if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
|
||||
goto clear_extattr;
|
||||
break;
|
||||
}
|
||||
if ((ctx->ext_attr_ver == 1 &&
|
||||
(entry->e_name_len == 0 || entry->e_name_index != 0)) ||
|
||||
|
@ -1399,6 +1403,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
|
|||
entry->e_name_index == 0)) {
|
||||
if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
|
||||
goto clear_extattr;
|
||||
break;
|
||||
}
|
||||
if (entry->e_value_block != 0) {
|
||||
if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
|
||||
|
@ -1415,6 +1420,17 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
|
|||
if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
|
||||
goto clear_extattr;
|
||||
}
|
||||
|
||||
hash = ext2fs_ext_attr_hash_entry(entry, block_buf +
|
||||
entry->e_value_offs);
|
||||
|
||||
if (entry->e_hash != hash) {
|
||||
pctx->num = entry->e_hash;
|
||||
if (fix_problem(ctx, PR_1_ATTR_HASH, pctx))
|
||||
goto clear_extattr;
|
||||
entry->e_hash = hash;
|
||||
}
|
||||
|
||||
entry = EXT2_EXT_ATTR_NEXT(entry);
|
||||
}
|
||||
if (region_allocate(region, (char *)entry - (char *)header, 4)) {
|
||||
|
@ -1678,8 +1694,11 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
|
|||
}
|
||||
}
|
||||
|
||||
if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
|
||||
if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf)) {
|
||||
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
||||
goto out;
|
||||
pb.num_blocks++;
|
||||
}
|
||||
|
||||
if (ext2fs_inode_has_valid_blocks(inode)) {
|
||||
if ((ctx->fs->super->s_feature_incompat &
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "e2fsck.h"
|
||||
#include "problem.h"
|
||||
#include <ext2fs/ext2_ext_attr.h>
|
||||
|
||||
/*
|
||||
* This routine is called when an inode is not connected to the
|
||||
|
@ -23,34 +24,45 @@
|
|||
* This subroutine returns 1 then the caller shouldn't bother with the
|
||||
* rest of the pass 4 tests.
|
||||
*/
|
||||
static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
|
||||
static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i,
|
||||
struct ext2_inode *inode)
|
||||
{
|
||||
ext2_filsys fs = ctx->fs;
|
||||
struct ext2_inode inode;
|
||||
struct problem_context pctx;
|
||||
__u32 eamagic = 0;
|
||||
int extra_size = 0;
|
||||
|
||||
e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
|
||||
if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE) {
|
||||
e2fsck_read_inode_full(ctx, i, inode,EXT2_INODE_SIZE(fs->super),
|
||||
"pass4: disconnect_inode");
|
||||
extra_size = ((struct ext2_inode_large *)inode)->i_extra_isize;
|
||||
} else {
|
||||
e2fsck_read_inode(ctx, i, inode, "pass4: disconnect_inode");
|
||||
}
|
||||
clear_problem_context(&pctx);
|
||||
pctx.ino = i;
|
||||
pctx.inode = &inode;
|
||||
pctx.inode = inode;
|
||||
|
||||
if (EXT2_INODE_SIZE(fs->super) -EXT2_GOOD_OLD_INODE_SIZE -extra_size >0)
|
||||
eamagic = *(__u32 *)(((char *)inode) +EXT2_GOOD_OLD_INODE_SIZE +
|
||||
extra_size);
|
||||
/*
|
||||
* Offer to delete any zero-length files that does not have
|
||||
* blocks. If there is an EA block, it might have useful
|
||||
* information, so we won't prompt to delete it, but let it be
|
||||
* reconnected to lost+found.
|
||||
*/
|
||||
if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
|
||||
LINUX_S_ISDIR(inode.i_mode))) {
|
||||
if (!inode->i_blocks && eamagic != EXT2_EXT_ATTR_MAGIC &&
|
||||
(LINUX_S_ISREG(inode->i_mode) || LINUX_S_ISDIR(inode->i_mode))) {
|
||||
if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
|
||||
e2fsck_clear_inode(ctx, i, &inode, 0,
|
||||
e2fsck_clear_inode(ctx, i, inode, 0,
|
||||
"disconnect_inode");
|
||||
/*
|
||||
* Fix up the bitmaps...
|
||||
*/
|
||||
e2fsck_read_bitmaps(ctx);
|
||||
ext2fs_inode_alloc_stats2(fs, i, -1,
|
||||
LINUX_S_ISDIR(inode.i_mode));
|
||||
LINUX_S_ISDIR(inode->i_mode));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +90,7 @@ void e2fsck_pass4(e2fsck_t ctx)
|
|||
{
|
||||
ext2_filsys fs = ctx->fs;
|
||||
ext2_ino_t i;
|
||||
struct ext2_inode inode;
|
||||
struct ext2_inode *inode;
|
||||
#ifdef RESOURCE_TRACK
|
||||
struct resource_track rtrack;
|
||||
#endif
|
||||
|
@ -106,6 +118,9 @@ void e2fsck_pass4(e2fsck_t ctx)
|
|||
if ((ctx->progress)(ctx, 4, 0, maxgroup))
|
||||
return;
|
||||
|
||||
inode = e2fsck_allocate_memory(ctx, EXT2_INODE_SIZE(fs->super),
|
||||
"scratch inode");
|
||||
|
||||
/* Protect loop from wrap-around if s_inodes_count maxed */
|
||||
for (i=1; i <= fs->super->s_inodes_count && i > 0; i++) {
|
||||
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
||||
|
@ -133,7 +148,7 @@ void e2fsck_pass4(e2fsck_t ctx)
|
|||
fs->blocksize, "bad_inode buffer");
|
||||
if (e2fsck_process_bad_inode(ctx, 0, i, buf))
|
||||
continue;
|
||||
if (disconnect_inode(ctx, i))
|
||||
if (disconnect_inode(ctx, i, inode))
|
||||
continue;
|
||||
ext2fs_icount_fetch(ctx->inode_link_info, i,
|
||||
&link_count);
|
||||
|
@ -141,18 +156,18 @@ void e2fsck_pass4(e2fsck_t ctx)
|
|||
&link_counted);
|
||||
}
|
||||
if (link_counted != link_count) {
|
||||
e2fsck_read_inode(ctx, i, &inode, "pass4");
|
||||
e2fsck_read_inode(ctx, i, inode, "pass4");
|
||||
pctx.ino = i;
|
||||
pctx.inode = &inode;
|
||||
if (link_count != inode.i_links_count) {
|
||||
pctx.inode = inode;
|
||||
if (link_count != inode->i_links_count) {
|
||||
pctx.num = link_count;
|
||||
fix_problem(ctx,
|
||||
PR_4_INCONSISTENT_COUNT, &pctx);
|
||||
}
|
||||
pctx.num = link_counted;
|
||||
if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
|
||||
inode.i_links_count = link_counted;
|
||||
e2fsck_write_inode(ctx, i, &inode, "pass4");
|
||||
inode->i_links_count = link_counted;
|
||||
e2fsck_write_inode(ctx, i, inode, "pass4");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,6 +180,8 @@ void e2fsck_pass4(e2fsck_t ctx)
|
|||
errout:
|
||||
if (buf)
|
||||
ext2fs_free_mem(&buf);
|
||||
|
||||
ext2fs_free_mem(&inode);
|
||||
#ifdef RESOURCE_TRACK
|
||||
if (ctx->options & E2F_OPT_TIME2) {
|
||||
e2fsck_clear_progbar(ctx);
|
||||
|
|
|
@ -776,7 +776,7 @@ static struct e2fsck_problem problem_table[] = {
|
|||
|
||||
/* invalid ea entry->e_hash */
|
||||
{ PR_1_ATTR_HASH,
|
||||
N_("@a in @i %i has a hash (%N) which is @n (must be 0)\n"),
|
||||
N_("@a in @i %i has a hash (%N) which is @n\n"),
|
||||
PROMPT_CLEAR, PR_PREEN_OK },
|
||||
|
||||
/* inode appears to be a directory */
|
||||
|
|
|
@ -393,6 +393,20 @@ void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
|
|||
}
|
||||
}
|
||||
|
||||
void e2fsck_read_inode_full(e2fsck_t ctx, unsigned long ino,
|
||||
struct ext2_inode *inode, int bufsize,
|
||||
const char *proc)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = ext2fs_read_inode_full(ctx->fs, ino, inode, bufsize);
|
||||
if (retval) {
|
||||
com_err("ext2fs_read_inode_full", retval,
|
||||
_("while reading inode %ld in %s"), ino, proc);
|
||||
fatal_error(ctx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
|
||||
struct ext2_inode * inode, int bufsize,
|
||||
const char *proc)
|
||||
|
|
|
@ -75,10 +75,12 @@ typedef __u32 ext2_dirhash_t;
|
|||
#include "com_err.h"
|
||||
#include "ext2_io.h"
|
||||
#include "ext2_err.h"
|
||||
#include "ext2_ext_attr.h"
|
||||
#else
|
||||
#include <et/com_err.h>
|
||||
#include <ext2fs/ext2_io.h>
|
||||
#include <ext2fs/ext2_err.h>
|
||||
#include <ext2fs/ext2_ext_attr.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -784,6 +786,8 @@ extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest);
|
|||
extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir);
|
||||
|
||||
/* ext_attr.c */
|
||||
extern __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry,
|
||||
void *data);
|
||||
extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf);
|
||||
extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block,
|
||||
void *buf);
|
||||
|
@ -1050,6 +1054,10 @@ extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
|
|||
/* swapfs.c */
|
||||
extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
|
||||
int has_header);
|
||||
extern void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header,
|
||||
struct ext2_ext_attr_header *from_hdr);
|
||||
extern void ext2fs_swap_ext_attr_entry(struct ext2_ext_attr_entry *to_entry,
|
||||
struct ext2_ext_attr_entry *from_entry);
|
||||
extern void ext2fs_swap_super(struct ext2_super_block * super);
|
||||
extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp);
|
||||
extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
|
||||
|
|
|
@ -23,6 +23,43 @@
|
|||
|
||||
#include "ext2fs.h"
|
||||
|
||||
#define NAME_HASH_SHIFT 5
|
||||
#define VALUE_HASH_SHIFT 16
|
||||
|
||||
/*
|
||||
* ext2_xattr_hash_entry()
|
||||
*
|
||||
* Compute the hash of an extended attribute.
|
||||
*/
|
||||
__u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data)
|
||||
{
|
||||
__u32 hash = 0;
|
||||
char *name = ((char *) entry) + sizeof(struct ext2_ext_attr_entry);
|
||||
int n;
|
||||
|
||||
for (n = 0; n < entry->e_name_len; n++) {
|
||||
hash = (hash << NAME_HASH_SHIFT) ^
|
||||
(hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
|
||||
*name++;
|
||||
}
|
||||
|
||||
/* The hash needs to be calculated on the data in little-endian. */
|
||||
if (entry->e_value_block == 0 && entry->e_value_size != 0) {
|
||||
__u32 *value = (__u32 *)data;
|
||||
for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >>
|
||||
EXT2_EXT_ATTR_PAD_BITS; n; n--) {
|
||||
hash = (hash << VALUE_HASH_SHIFT) ^
|
||||
(hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
|
||||
ext2fs_le32_to_cpu(*value++);
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
#undef NAME_HASH_SHIFT
|
||||
#undef VALUE_HASH_SHIFT
|
||||
|
||||
errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
|
||||
{
|
||||
errcode_t retval;
|
||||
|
|
|
@ -90,6 +90,29 @@ void ext2fs_swap_group_desc(struct ext2_group_desc *gdp)
|
|||
gdp->bg_checksum = ext2fs_swab16(gdp->bg_checksum);
|
||||
}
|
||||
|
||||
void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header,
|
||||
struct ext2_ext_attr_header *from_header)
|
||||
{
|
||||
int n;
|
||||
|
||||
to_header->h_magic = ext2fs_swab32(from_header->h_magic);
|
||||
to_header->h_blocks = ext2fs_swab32(from_header->h_blocks);
|
||||
to_header->h_refcount = ext2fs_swab32(from_header->h_refcount);
|
||||
to_header->h_hash = ext2fs_swab32(from_header->h_hash);
|
||||
for (n = 0; n < 4; n++)
|
||||
to_header->h_reserved[n] =
|
||||
ext2fs_swab32(from_header->h_reserved[n]);
|
||||
}
|
||||
|
||||
void ext2fs_swap_ext_attr_entry(struct ext2_ext_attr_entry *to_entry,
|
||||
struct ext2_ext_attr_entry *from_entry)
|
||||
{
|
||||
to_entry->e_value_offs = ext2fs_swab16(from_entry->e_value_offs);
|
||||
to_entry->e_value_block = ext2fs_swab32(from_entry->e_value_block);
|
||||
to_entry->e_value_size = ext2fs_swab32(from_entry->e_value_size);
|
||||
to_entry->e_hash = ext2fs_swab32(from_entry->e_hash);
|
||||
}
|
||||
|
||||
void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header)
|
||||
{
|
||||
struct ext2_ext_attr_header *from_header =
|
||||
|
@ -98,32 +121,22 @@ void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header)
|
|||
(struct ext2_ext_attr_header *)to;
|
||||
struct ext2_ext_attr_entry *from_entry, *to_entry;
|
||||
char *from_end = (char *)from_header + bufsize;
|
||||
int n;
|
||||
|
||||
if (to_header != from_header)
|
||||
memcpy(to_header, from_header, bufsize);
|
||||
|
||||
from_entry = (struct ext2_ext_attr_entry *)from_header;
|
||||
to_entry = (struct ext2_ext_attr_entry *)to_header;
|
||||
|
||||
if (has_header) {
|
||||
to_header->h_magic = ext2fs_swab32(from_header->h_magic);
|
||||
to_header->h_blocks = ext2fs_swab32(from_header->h_blocks);
|
||||
to_header->h_refcount = ext2fs_swab32(from_header->h_refcount);
|
||||
for (n=0; n<4; n++)
|
||||
to_header->h_reserved[n] =
|
||||
ext2fs_swab32(from_header->h_reserved[n]);
|
||||
ext2fs_swap_ext_attr_header(to_header, from_header);
|
||||
|
||||
from_entry = (struct ext2_ext_attr_entry *)(from_header+1);
|
||||
to_entry = (struct ext2_ext_attr_entry *)(to_header+1);
|
||||
} else {
|
||||
from_entry = (struct ext2_ext_attr_entry *)from_header;
|
||||
to_entry = (struct ext2_ext_attr_entry *)to_header;
|
||||
}
|
||||
|
||||
while ((char *)from_entry < from_end && *(__u32 *)from_entry) {
|
||||
to_entry->e_value_offs =
|
||||
ext2fs_swab16(from_entry->e_value_offs);
|
||||
to_entry->e_value_block =
|
||||
ext2fs_swab32(from_entry->e_value_block);
|
||||
to_entry->e_value_size =
|
||||
ext2fs_swab32(from_entry->e_value_size);
|
||||
ext2fs_swap_ext_attr_entry(to_entry, from_entry);
|
||||
from_entry = EXT2_EXT_ATTR_NEXT(from_entry);
|
||||
to_entry = EXT2_EXT_ATTR_NEXT(to_entry);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue