From 342d847db355d81299218e07a1e58ece82080a04 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Mon, 2 Jul 2001 11:54:09 -0400 Subject: [PATCH] Add initial support for extended attribute blocks --- e2fsck/ChangeLog | 34 ++++ e2fsck/Makefile.in | 9 +- e2fsck/e2fsck.c | 12 ++ e2fsck/e2fsck.h | 25 +++ e2fsck/ea_refcount.c | 481 +++++++++++++++++++++++++++++++++++++++++++++ e2fsck/message.c | 2 + e2fsck/pass1.c | 115 ++++++++++- e2fsck/pass1b.c | 40 ++++ e2fsck/pass2.c | 14 ++ e2fsck/problem.c | 22 ++- e2fsck/problem.h | 12 ++ lib/e2p/ChangeLog | 3 + lib/e2p/feature.c | 2 + lib/ext2fs/ChangeLog | 4 + lib/ext2fs/Makefile.in | 4 +- lib/ext2fs/ext2_ext_attr.h | 60 ++++++ lib/ext2fs/ext2fs.h | 7 +- lib/ext2fs/ext_attr.c | 98 +++++++++ 18 files changed, 937 insertions(+), 7 deletions(-) create mode 100644 e2fsck/ea_refcount.c create mode 100644 lib/ext2fs/ext2_ext_attr.h create mode 100644 lib/ext2fs/ext_attr.c diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog index ea0e6fb..b24e806 100644 --- a/e2fsck/ChangeLog +++ b/e2fsck/ChangeLog @@ -1,3 +1,37 @@ +2001-07-02 Theodore Tso + + * pass1.c (e2fsck_pass1, check_ext_attr, check_blocks): Add + initial support for scanning extended attribute blocks. + + * e2fsck.c (e2fsck_reset_context): free extended attribute + refcount structure. + + * e2fsck.h: Add new fields for accounting for the extended + attribute blocks. + + * Makefile.in, ea_refcount.c, e2fsck.h: Add new file which + implements a reference count abstraction. + + * problem.c, problem.h: Add new problem codes PR_1_ALLOCATE_REFCOUNT, + PR_1_READ_EA_BLOCK, PR_1_BAD_EA_BLOCK, PR_2_FILE_ACL_BAD + + * message.c: Add "@a" as an abbreviation for "extended attribute" + + * pass1b.c (clone_file): Add code which handles cloning an + extended attribute block. + + * pass1b.c (delete_file): + * pass2.c (deallocate_inode): If the inode contains an extended + attribute block in the file_acl field, clear it and + deallocate the block. + +2001-06-28 Theodore Tso + + * pass2.c (e2fsck_process_bad_inode): Deal with inodes which are + marked bad because of an invalid file_acl (either because + EXT2_FEATURE_COMPAT_EXT_ATTR is not set, or because the + block number in file_acl is bad. + 2001-06-29 Theodore Tso * unix.c (show_stats): Use long long to calculate the percentage diff --git a/e2fsck/Makefile.in b/e2fsck/Makefile.in index e0c746e..2fde4b0 100644 --- a/e2fsck/Makefile.in +++ b/e2fsck/Makefile.in @@ -56,7 +56,7 @@ PROFILED_DEPLIBS= $(PROFILED_LIBEXT2FS) $(PROFILED_LIBCOM_ERR) \ OBJS= unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o pass3.o pass4.o \ pass5.o journal.o swapfs.o badblocks.o util.o dirinfo.o ehandler.o \ - problem.o message.o recovery.o revoke.o $(MTRACE_OBJ) + problem.o message.o recovery.o revoke.o ea_refcount.o $(MTRACE_OBJ) PROFILED_OBJS= profiled/unix.o profiled/e2fsck.o profiled/super.o \ profiled/pass1.o profiled/pass1b.o \ @@ -64,7 +64,7 @@ PROFILED_OBJS= profiled/unix.o profiled/e2fsck.o profiled/super.o \ profiled/journal.o profiled/badblocks.o profiled/util.o \ profiled/dirinfo.o profiled/ehandler.o profiled/message.o \ profiled/problem.o profiled/swapfs.o profiled/recovery.o \ - profiled/revoke.o + profiled/revoke.o profiled/ea_refcount.o SRCS= $(srcdir)/e2fsck.c \ $(srcdir)/super.c \ @@ -85,6 +85,7 @@ SRCS= $(srcdir)/e2fsck.c \ $(srcdir)/problem.c \ $(srcdir)/message.c \ $(srcdir)/swapfs.c \ + $(srcdir)/ea_refcount.c \ $(MTRACE_SRC) all:: profiled $(PROGS) e2fsck.static e2fsck.shared $(MANPAGES) @@ -106,6 +107,10 @@ e2fsck.profiled: $(PROFILED_OBJS) $(PROFILED_DEPLIBS) $(LD) $(ALL_LDFLAGS) -g -pg -o e2fsck.profiled $(PROFILED_OBJS) \ $(PROFILED_LIBS) +tst_refcount: ea_refcount.c + $(CC) -o tst_refcount $(srcdir)/ea_refcount.c \ + $(ALL_CFLAGS) -DTEST_PROGRAM -lcom_err + extend: extend.o $(LD) $(ALL_LDFLAGS) -o extend extend.o $(CHECKLIB) diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c index 66e4a75..3d9750c 100644 --- a/e2fsck/e2fsck.c +++ b/e2fsck/e2fsck.c @@ -67,10 +67,22 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx) ctx->fs->dblist = 0; } e2fsck_free_dir_info(ctx); + if (ctx->refcount) { + ea_refcount_free(ctx->refcount); + ctx->refcount = 0; + } + if (ctx->refcount_extra) { + ea_refcount_free(ctx->refcount_extra); + ctx->refcount_extra = 0; + } if (ctx->block_dup_map) { ext2fs_free_block_bitmap(ctx->block_dup_map); ctx->block_dup_map = 0; } + if (ctx->block_ea_map) { + ext2fs_free_block_bitmap(ctx->block_ea_map); + ctx->block_ea_map = 0; + } if (ctx->inode_bb_map) { ext2fs_free_inode_bitmap(ctx->inode_bb_map); ctx->inode_bb_map = 0; diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index da41cca..26f63cf 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -127,6 +127,11 @@ struct resource_track { #define E2F_PASS_1B 6 /* + * Define the extended attribute refcount structure + */ +typedef struct ea_refcount *ext2_refcount_t; + +/* * This is the global e2fsck structure. */ typedef struct e2fsck_struct *e2fsck_t; @@ -159,6 +164,7 @@ struct e2fsck_struct { ext2fs_block_bitmap block_found_map; /* Blocks which are in use */ ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */ + ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */ /* * Inode count arrays @@ -166,6 +172,9 @@ struct e2fsck_struct { ext2_icount_t inode_count; ext2_icount_t inode_link_info; + ext2_refcount_t refcount; + ext2_refcount_t refcount_extra; + /* * Array of flags indicating whether an inode bitmap, block * bitmap, or inode table is invalid @@ -231,6 +240,8 @@ struct e2fsck_struct { int fs_tind_count; int fs_fragmented; int large_files; + int fs_ext_attr_inodes; + int fs_ext_attr_blocks; /* * For the use of callers of the e2fsck functions; not used by @@ -271,6 +282,20 @@ extern int e2fsck_get_num_dirs(e2fsck_t ctx); extern int e2fsck_get_num_dirinfo(e2fsck_t ctx); extern struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control); +/* ea_refcount.c */ +extern errcode_t ea_refcount_create(int size, ext2_refcount_t *ret); +extern void ea_refcount_free(ext2_refcount_t refcount); +extern errcode_t ea_refcount_fetch(ext2_refcount_t refcount, blk_t blk, + int *ret); +extern errcode_t ea_refcount_increment(ext2_refcount_t refcount, + blk_t blk, int *ret); +extern errcode_t ea_refcount_decrement(ext2_refcount_t refcount, + blk_t blk, int *ret); +extern errcode_t ea_refcount_store(ext2_refcount_t refcount, + blk_t blk, int count); +extern void ea_refcount_intr_begin(ext2_refcount_t refcount); +extern blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret); + /* ehandler.c */ extern const char *ehandler_operation(const char *op); extern void ehandler_init(io_channel channel); diff --git a/e2fsck/ea_refcount.c b/e2fsck/ea_refcount.c new file mode 100644 index 0000000..9a3f652 --- /dev/null +++ b/e2fsck/ea_refcount.c @@ -0,0 +1,481 @@ +/* + * ea_refcount.c + * + * Copyright (C) 2001 Theodore Ts'o. This file may be + * redistributed under the terms of the GNU Public License. + */ + +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "e2fsck.h" + +/* + * The strategy we use for keeping track of EA refcounts is as + * follows. We keep a sorted array of first EA blocks and its + * reference counts. Once the refcount has dropped to zero, it is + * removed from the array to save memory space. Once the EA block is + * checked, its bit is set in the block_ea_map bitmap. + */ +struct ea_refcount_el { + blk_t ea_blk; + int ea_count; +}; + +struct ea_refcount { + blk_t count; + blk_t size; + int cursor; + struct ea_refcount_el *list; +}; + +void ea_refcount_free(ext2_refcount_t refcount) +{ + if (!refcount) + return; + + if (refcount->list) + ext2fs_free_mem((void **) &refcount->list); + ext2fs_free_mem((void **) &refcount); +} + +errcode_t ea_refcount_create(int size, ext2_refcount_t *ret) +{ + ext2_refcount_t refcount; + errcode_t retval; + size_t bytes; + int i; + + retval = ext2fs_get_mem(sizeof(struct ea_refcount), + (void **) &refcount); + if (retval) + return retval; + memset(refcount, 0, sizeof(struct ea_refcount)); + + if (!size) + size = 500; + refcount->size = size; + bytes = (size_t) (size * sizeof(struct ea_refcount_el)); +#ifdef DEBUG + printf("Refcount allocated %d entries, %d bytes.\n", + refcount->size, bytes); +#endif + retval = ext2fs_get_mem(bytes, (void **) &refcount->list); + if (retval) + goto errout; + memset(refcount->list, 0, bytes); + + refcount->count = 0; + refcount->cursor = 0; + + *ret = refcount; + return 0; + +errout: + ea_refcount_free(refcount); + return(retval); +} + +/* + * collapse_refcount() --- go through the refcount array, and get rid + * of any count == zero entries + */ +static void refcount_collapse(ext2_refcount_t refcount) +{ + int i, j; + struct ea_refcount_el *list; + + list = refcount->list; + for (i = 0, j = 0; i < refcount->count; i++) { + if (list[i].ea_count) { + if (i != j) + list[j] = list[i]; + j++; + } + } +#if defined(DEBUG) || defined(TEST_PROGRAM) + printf("Refcount_collapse: size was %d, now %d\n", + refcount->count, j); +#endif + refcount->count = j; +} + + +/* + * insert_refcount_el() --- Insert a new entry into the sorted list at a + * specified position. + */ +static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount, + blk_t blk, int pos) +{ + struct ea_refcount_el *el; + errcode_t retval; + blk_t new_size = 0; + int num; + + if (refcount->count >= refcount->size) { + new_size = refcount->size + 100; +#ifdef DEBUG + printf("Reallocating refcount %d entries...\n", new_size); +#endif + retval = ext2fs_resize_mem((size_t) refcount->size * + sizeof(struct ea_refcount_el), + (size_t) new_size * + sizeof(struct ea_refcount_el), + (void **) &refcount->list); + if (retval) + return 0; + refcount->size = new_size; + } + num = (int) refcount->count - pos; + if (num < 0) + return 0; /* should never happen */ + if (num) { + memmove(&refcount->list[pos+1], &refcount->list[pos], + sizeof(struct ea_refcount_el) * num); + } + refcount->count++; + el = &refcount->list[pos]; + el->ea_count = 0; + el->ea_blk = blk; + return el; +} + + +/* + * get_refcount_el() --- given an block number, try to find refcount + * information in the sorted list. If the create flag is set, + * and we can't find an entry, create one in the sorted list. + */ +static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount, + blk_t blk, int create) +{ + float range; + int low, high, mid; + blk_t lowval, highval; + + if (!refcount || !refcount->list) + return 0; +retry: + low = 0; + high = (int) refcount->count-1; + if (create && ((refcount->count == 0) || + (blk > refcount->list[high].ea_blk))) { + if (refcount->count >= refcount->size) + refcount_collapse(refcount); + + return insert_refcount_el(refcount, blk, + (unsigned) refcount->count); + } + if (refcount->count == 0) + return 0; + + if (refcount->cursor >= refcount->count) + refcount->cursor = 0; + if (blk == refcount->list[refcount->cursor].ea_blk) + return &refcount->list[refcount->cursor++]; +#ifdef DEBUG + printf("Non-cursor get_refcount_el: %u\n", blk); +#endif + while (low <= high) { +#if 0 + mid = (low+high)/2; +#else + if (low == high) + mid = low; + else { + /* Interpolate for efficiency */ + lowval = refcount->list[low].ea_blk; + highval = refcount->list[high].ea_blk; + + if (blk < lowval) + range = 0; + else if (blk > highval) + range = 1; + else + range = ((float) (blk - lowval)) / + (highval - lowval); + mid = low + ((int) (range * (high-low))); + } +#endif + if (blk == refcount->list[mid].ea_blk) { + refcount->cursor = mid+1; + return &refcount->list[mid]; + } + if (blk < refcount->list[mid].ea_blk) + high = mid-1; + else + low = mid+1; + } + /* + * If we need to create a new entry, it should be right at + * low (where high will be left at low-1). + */ + if (create) { + if (refcount->count >= refcount->size) { + refcount_collapse(refcount); + if (refcount->count < refcount->size) + goto retry; + } + return insert_refcount_el(refcount, blk, low); + } + return 0; +} + +errcode_t ea_refcount_fetch(ext2_refcount_t refcount, blk_t blk, + int *ret) +{ + struct ea_refcount_el *el; + + el = get_refcount_el(refcount, blk, 0); + if (!el) { + *ret = 0; + return 0; + } + *ret = el->ea_count; + return 0; +} + +errcode_t ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret) +{ + struct ea_refcount_el *el; + + el = get_refcount_el(refcount, blk, 1); + if (!el) + return EXT2_ET_NO_MEMORY; + el->ea_count++; + + if (ret) + *ret = el->ea_count; + return 0; +} + +errcode_t ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret) +{ + struct ea_refcount_el *el; + + el = get_refcount_el(refcount, blk, 0); + if (!el || el->ea_count == 0) + return EXT2_ET_INVALID_ARGUMENT; + + el->ea_count--; + + if (ret) + *ret = el->ea_count; + return 0; +} + +errcode_t ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count) +{ + struct ea_refcount_el *el; + + /* + * Get the refcount element + */ + el = get_refcount_el(refcount, blk, count ? 1 : 0); + if (!el) + return count ? EXT2_ET_NO_MEMORY : 0; + el->ea_count = count; + return 0; +} + +blk_t ext2fs_get_refcount_size(ext2_refcount_t refcount) +{ + if (!refcount) + return 0; + + return refcount->size; +} + +void ea_refcount_intr_begin(ext2_refcount_t refcount) +{ + refcount->cursor = 0; +} + + +blk_t ea_refcount_intr_next(ext2_refcount_t refcount, + int *ret) +{ + struct ea_refcount_el *list; + + while (1) { + if (refcount->cursor >= refcount->count) + return 0; + list = refcount->list; + if (list[refcount->cursor].ea_count) { + if (ret) + *ret = list[refcount->cursor].ea_count; + return list[refcount->cursor++].ea_blk; + } + refcount->cursor++; + } +} + + +#ifdef TEST_PROGRAM + +errcode_t ea_refcount_validate(ext2_refcount_t refcount, FILE *out) +{ + errcode_t ret = 0; + int i; + const char *bad = "bad refcount"; + + if (refcount->count > refcount->size) { + fprintf(out, "%s: count > size\n", bad); + return EXT2_ET_INVALID_ARGUMENT; + } + for (i=1; i < refcount->count; i++) { + if (refcount->list[i-1].ea_blk >= refcount->list[i].ea_blk) { + fprintf(out, "%s: list[%d].blk=%u, list[%d].blk=%u\n", + bad, i-1, refcount->list[i-1].ea_blk, + i, refcount->list[i].ea_blk); + ret = EXT2_ET_INVALID_ARGUMENT; + } + } + return ret; +} + +#define BCODE_END 0 +#define BCODE_CREATE 1 +#define BCODE_FREE 2 +#define BCODE_STORE 3 +#define BCODE_INCR 4 +#define BCODE_DECR 5 +#define BCODE_FETCH 6 +#define BCODE_VALIDATE 7 +#define BCODE_LIST 8 +#define BCODE_COLLAPSE 9 + +int bcode_program[] = { + BCODE_CREATE, 5, + BCODE_STORE, 3, 3, + BCODE_STORE, 4, 4, + BCODE_STORE, 1, 1, + BCODE_STORE, 8, 8, + BCODE_STORE, 2, 2, + BCODE_STORE, 4, 0, + BCODE_STORE, 2, 0, + BCODE_STORE, 6, 6, + BCODE_VALIDATE, + BCODE_STORE, 4, 4, + BCODE_STORE, 2, 2, + BCODE_FETCH, 1, + BCODE_FETCH, 2, + BCODE_INCR, 3, + BCODE_INCR, 3, + BCODE_DECR, 4, + BCODE_STORE, 4, 4, + BCODE_VALIDATE, + BCODE_STORE, 20, 20, + BCODE_STORE, 40, 40, + BCODE_STORE, 30, 30, + BCODE_STORE, 10, 10, + BCODE_DECR, 30, + BCODE_FETCH, 30, + BCODE_DECR, 2, + BCODE_DECR, 2, + BCODE_COLLAPSE, + BCODE_LIST, + BCODE_VALIDATE, + BCODE_FREE, + BCODE_END +}; + +int main(int argc, char **argv) +{ + int i = 0; + ext2_refcount_t refcount; + int size, arg; + blk_t blk; + errcode_t retval; + + while (1) { + switch (bcode_program[i++]) { + case BCODE_END: + exit(0); + case BCODE_CREATE: + size = bcode_program[i++]; + retval = ea_refcount_create(size, &refcount); + if (retval) { + com_err("ea_refcount_create", + retval, ""); + exit(1); + } else + printf("Creating refcount with size %d\n", + size); + break; + case BCODE_FREE: + ea_refcount_free(refcount); + refcount = 0; + printf("Freeing refcount\n"); + break; + case BCODE_STORE: + blk = (blk_t) bcode_program[i++]; + arg = bcode_program[i++]; + retval = ea_refcount_store(refcount, blk, arg); + printf("Storing blk %u with value %d\n", blk, arg); + if (retval) + com_err("ea_refcount_store", retval, ""); + break; + case BCODE_FETCH: + blk = (blk_t) bcode_program[i++]; + retval = ea_refcount_fetch(refcount, blk, &arg); + if (retval) + com_err("ea_refcount_fetch", retval, ""); + else + printf("bcode_fetch(%u) returns %d\n", + blk, arg); + break; + case BCODE_INCR: + blk = (blk_t) bcode_program[i++]; + retval = ea_refcount_increment(refcount, blk, + &arg); + if (retval) + com_err("ea_refcount_increment", retval, + ""); + else + printf("bcode_increment(%u) returns %d\n", + blk, arg); + break; + case BCODE_DECR: + blk = (blk_t) bcode_program[i++]; + retval = ea_refcount_decrement(refcount, blk, + &arg); + if (retval) + com_err("ea_refcount_decrement", retval, + "while decrementing blk %u", blk); + else + printf("bcode_decrement(%u) returns %d\n", + blk, arg); + break; + case BCODE_VALIDATE: + retval = ea_refcount_validate(refcount, stderr); + if (retval) + com_err("ea_refcount_validate", + retval, ""); + else + printf("Refcount validation OK.\n"); + break; + case BCODE_LIST: + ea_refcount_intr_begin(refcount); + while (1) { + blk = ea_refcount_intr_next(refcount, + &arg); + if (!blk) + break; + printf("\tblk=%u, count=%d\n", blk, + arg); + } + break; + case BCODE_COLLAPSE: + refcount_collapse(refcount); + break; + } + + } +} + +#endif diff --git a/e2fsck/message.c b/e2fsck/message.c index 26a7c02..2c7e771 100644 --- a/e2fsck/message.c +++ b/e2fsck/message.c @@ -50,6 +50,7 @@ * * The following '@' expansions are supported: * + * @a extended attribute * @A error allocating * @b block * @B bitmap @@ -99,6 +100,7 @@ * letter in the table below. */ static const char *abbrevs[] = { + N_("aextended attribute"), N_("Aerror allocating"), N_("bblock"), N_("Bbitmap"), diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index f3ab667..a9731c6 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -43,6 +43,8 @@ #endif #include "e2fsck.h" +#include + #include "problem.h" #ifdef NO_INLINE_FUNCS @@ -519,8 +521,7 @@ void e2fsck_pass1(e2fsck_t ctx) frag = fsize = 0; } - if (inode.i_faddr || frag || fsize - || inode.i_file_acl || + if (inode.i_faddr || frag || fsize || (LINUX_S_ISDIR(inode.i_mode) && inode.i_dir_acl)) mark_inode_bad(ctx, ino); if (inode.i_flags & EXT2_IMAGIC_FL) { @@ -859,6 +860,114 @@ static _INLINE_ void mark_block_used(e2fsck_t ctx, blk_t block) } /* + * Handle processing the extended attribute blocks + */ +static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, + char *block_buf) +{ + ext2_filsys fs = ctx->fs; + ext2_ino_t ino = pctx->ino; + struct ext2_inode *inode = pctx->inode; + blk_t blk; + static struct ext2_ext_attr_header *header; + int count; + + blk = inode->i_file_acl; + if (blk == 0) + return 0; + + /* + * If the Extended attribute flag isn't set, then a non-zero + * file acl means that the inode is corrupted. + * + * Or if the extended attribute block is an invalid block, + * then the inode is also corrupted. + */ + if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) || + (blk < fs->super->s_first_data_block) || + (blk >= fs->super->s_blocks_count)) { + mark_inode_bad(ctx, ino); + return 0; + } + + /* If ea bitmap hasn't been allocated, create it */ + if (!ctx->block_ea_map) { + pctx->errcode = ext2fs_allocate_block_bitmap(fs, + _("ext attr block map"), + &ctx->block_ea_map); + if (pctx->errcode) { + pctx->num = 2; + fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return 0; + } + } + + /* Create the EA refcount structure if necessary */ + if (!ctx->refcount) { + pctx->errcode = ea_refcount_create(0, &ctx->refcount); + if (pctx->errcode) { + pctx->num = 1; + fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return 0; + } + } + + /* Have we seen this EA block before? */ + if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) { + if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0) + return 1; + /* Ooops, this EA was referenced more than it stated */ + if (!ctx->refcount_extra) { + pctx->errcode = ea_refcount_create(0, + &ctx->refcount_extra); + if (pctx->errcode) { + pctx->num = 2; + fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return 1; + } + } + ea_refcount_increment(ctx->refcount_extra, blk, 0); + return 1; + } + + /* + * OK, we haven't seen this EA block yet. So we need to + * validate it + */ + pctx->blk = blk; + pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf); + if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx)) + goto clear_extattr; + /* XXX what if read_ext_attr returns an error */ + header = (struct ext2_ext_attr_header *) block_buf; + + if (header->h_magic != EXT2_EXT_ATTR_MAGIC) { + pctx->blk = inode->i_file_acl; + if (fix_problem, ctx, PR_1_BAD_EA_BLOCK, pctx) + goto clear_extattr; + } + + /* @@@ validate the contents of the EA block */ + + count = header->h_refcount - 1; + if (count) + ea_refcount_store(ctx->refcount, blk, count); + mark_block_used(ctx, blk); + ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk); + + return 1; + +clear_extattr: + inode->i_file_acl = 0; + e2fsck_write_inode(ctx, ino, inode, "check_ext_attr"); + return 0; +} + + +/* * This subroutine is called on each inode to account for all of the * blocks used by that inode. */ @@ -930,6 +1039,8 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, ctx->flags |= E2F_FLAG_RESTART; return; } + if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf)) + pb.num_blocks++; pb.num_blocks *= (fs->blocksize / 512); #if 0 diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c index f300de3..f0584e8 100644 --- a/e2fsck/pass1b.c +++ b/e2fsck/pass1b.c @@ -37,6 +37,9 @@ #include "problem.h" +/* Define an extension to the ext2 library's block count information */ +#define BLOCK_COUNT_EXTATTR (-5) + /* * This is structure is allocated for each time that a block is * claimed by more than one file. So if a particular block is claimed @@ -67,12 +70,15 @@ struct dup_block { blk_t block; /* Block number */ ext2_ino_t ino; /* Inode number */ int num_bad; + int flags; /* Pointer to next dup record with different block */ struct dup_block *next_block; /* Pointer to next dup record with different inode */ struct dup_block *next_inode; }; +#define FLAG_EXTATTR (1) + /* * This structure stores information about a particular inode which * is sharing blocks with other inodes. This information is collected @@ -199,6 +205,9 @@ static void pass1b(e2fsck_t ctx, char *block_buf) pb.dup_blocks = 0; pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, process_pass1b_block, &pb); + if (inode.i_file_acl) + process_pass1b_block(fs, &inode.i_file_acl, + BLOCK_COUNT_EXTATTR, 0, 0, &pb); if (pb.dup_blocks) { end_problem_latch(ctx, PR_LATCH_DBLOCK); dp = (struct dup_inode *) e2fsck_allocate_memory(ctx, @@ -261,6 +270,8 @@ static int process_pass1b_block(ext2_filsys fs, dp->block = *block_nr; dp->ino = p->ino; dp->num_bad = 0; + dp->flags = (blockcnt == BLOCK_COUNT_EXTATTR) ? + FLAG_EXTATTR : 0; q = dup_blk; while (q) { if (q->block == *block_nr) @@ -546,6 +557,9 @@ static void delete_file(e2fsck_t ctx, struct dup_inode *dp, char* block_buf) e2fsck_read_inode(ctx, dp->ino, &inode, "delete_file"); inode.i_links_count = 0; inode.i_dtime = time(0); + if (inode.i_file_acl) + delete_file_block(fs, &inode.i_file_acl, + BLOCK_COUNT_EXTATTR, 0, 0, &pb); e2fsck_write_inode(ctx, dp->ino, &inode, "delete_file"); } @@ -656,6 +670,32 @@ static int clone_file(e2fsck_t ctx, struct dup_inode *dp, char* block_buf) _("returned from clone_file_block")); return cs.errcode; } + if (dp->inode.i_file_acl && + (clone_file_block(fs, &dp->inode.i_file_acl, + BLOCK_COUNT_EXTATTR, 0, 0, &cs) == + BLOCK_CHANGED)) { + struct dup_block *p; + struct dup_inode *q; + + /* + * If we cloned the EA block, find all other inodes + * which refered to that EA block, and modify + * them to point to the new EA block. + */ + for (p = dup_blk; p; p = p->next_block) { + if (!(p->flags & FLAG_EXTATTR)) + continue; + for (q = dup_ino; q; q = q->next) + if (p->ino == q->ino) + break; + if (!q) + continue; /* Should never happen */ + q->inode.i_file_acl = dp->inode.i_file_acl; + e2fsck_write_inode(ctx, q->ino, &q->inode, + "clone file EA"); + } + } + return 0; } diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 13b398e..a885f6f 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -648,6 +648,12 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) if (!ext2fs_inode_has_valid_blocks(&inode)) return; + if (inode.i_file_acl) { + ext2fs_unmark_block_bitmap(ctx->block_found_map, + inode.i_file_acl); + ext2fs_unmark_block_bitmap(fs->block_map, inode.i_file_acl); + } + ext2fs_mark_bb_dirty(fs); pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, deallocate_inode_block, ctx); @@ -747,10 +753,18 @@ extern int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, } if (inode.i_file_acl && + !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) && fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) { inode.i_file_acl = 0; inode_modified++; } + if (inode.i_file_acl && + ((inode.i_file_acl < fs->super->s_first_data_block) || + (inode.i_file_acl >= fs->super->s_blocks_count)) && + fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) { + inode.i_file_acl = 0; + inode_modified++; + } if (inode.i_dir_acl && LINUX_S_ISDIR(inode.i_mode) && fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) { diff --git a/e2fsck/problem.c b/e2fsck/problem.c index 84061b3..e5e62cf 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -567,6 +567,21 @@ static const struct e2fsck_problem problem_table[] = { N_("@is that were part of a corrupted orphan linked list found. "), PROMPT_FIX, 0 }, + /* Error allocating refcount structure */ + { PR_1_ALLOCATE_REFCOUNT, + "@A refcount structure (%N): %m\n", + PROMPT_NONE, PR_FATAL }, + + /* Error reading extended attribute block */ + { PR_1_READ_EA_BLOCK, + N_("Error reading @a @b %b for @i %i. "), + PROMPT_CLEAR, 0 }, + + /* Invalid extended attribute block */ + { PR_1_BAD_EA_BLOCK, + N_("@i %i has a bad @a @b %b. "), + PROMPT_CLEAR, 0 }, + /* Pass 1b errors */ /* Pass 1B: Rescan for duplicate/bad blocks */ @@ -873,7 +888,12 @@ static const struct e2fsck_problem problem_table[] = { /* Invalid fast symlink size */ { PR_2_SYMLINK_SIZE, - N_("@i %i (%Q) is a fast symlink with an invalid size (%Is)\n"), + N_("@i %i (%Q) is a fast symlink with a bad size (%Is)\n"), + PROMPT_CLEAR, 0 }, + + /* i_file_acl (extended attribute block) is bad */ + { PR_2_FILE_ACL_BAD, + N_("@a @b @F is invalid (%If).\n"), PROMPT_CLEAR, 0 }, /* Pass 3 errors */ diff --git a/e2fsck/problem.h b/e2fsck/problem.h index 5df40ef..7136381 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -326,7 +326,16 @@ struct problem_context { /* Latch question which asks how to deal with low dtime inodes */ #define PR_1_ORPHAN_LIST_REFUGEES 0x010037 + +/* Error allocating refcount structure */ +#define PR_1_ALLOCATE_REFCOUNT 0x010038 +/* Error reading Extended Attribute block */ +#define PR_1_READ_EA_BLOCK 0x010039 + +/* Invalid Extended Attribute block */ +#define PR_1_BAD_EA_BLOCK 0x01003A + /* * Pass 1b errors */ @@ -520,6 +529,9 @@ struct problem_context { /* Invalid fast symlink size */ #define PR_2_SYMLINK_SIZE 0x020031 +/* i_file_acl (extended attribute) is bad */ +#define PR_2_FILE_ACL_BAD 0x020032 + /* * Pass 3 errors */ diff --git a/lib/e2p/ChangeLog b/lib/e2p/ChangeLog index 0df0008..4b06626 100644 --- a/lib/e2p/ChangeLog +++ b/lib/e2p/ChangeLog @@ -1,5 +1,8 @@ 2001-06-23 Theodore Tso + * feature.c: Add entry in feature table to interpret + EXT2_FEATURE_COMPAT_EXT_ATTR. + * Release of E2fsprogs 1.22 2001-06-15 Theodore Tso diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c index c9d2427..2586727 100644 --- a/lib/e2p/feature.c +++ b/lib/e2p/feature.c @@ -29,6 +29,8 @@ static struct feature feature_list[] = { "has_journal" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_IMAGIC_INODES, "imagic_inodes" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXT_ATTR, + "ext_attr" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX, "dir_index" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INODE, diff --git a/lib/ext2fs/ChangeLog b/lib/ext2fs/ChangeLog index e954166..43e23f6 100644 --- a/lib/ext2fs/ChangeLog +++ b/lib/ext2fs/ChangeLog @@ -1,5 +1,9 @@ 2001-06-23 Theodore Tso + * Makefile.in, ext_attr.c, ext2_attr.c, ext2fs.h: Add new files + ext2_ext_attr.h and ext_attr.c for extended attributes + support. + * Release of E2fsprogs 1.22 2001-06-22 Theodore Tso diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in index d5adcb6..948a873 100644 --- a/lib/ext2fs/Makefile.in +++ b/lib/ext2fs/Makefile.in @@ -31,6 +31,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \ dirblock.o \ dir_iterate.o \ expanddir.o \ + ext_attr.o \ finddev.o \ flushb.o \ freefs.o \ @@ -78,6 +79,7 @@ SRCS= ext2_err.c \ $(srcdir)/dir_iterate.c \ $(srcdir)/dupfs.c \ $(srcdir)/expanddir.c \ + $(srcdir)/ext_attr.c \ $(srcdir)/fileio.c \ $(srcdir)/finddev.c \ $(srcdir)/flushb.c \ @@ -112,7 +114,7 @@ SRCS= ext2_err.c \ $(srcdir)/version.c \ $(srcdir)/write_bb_file.c -HFILES= bitops.h ext2fs.h ext2_io.h ext2_fs.h +HFILES= bitops.h ext2fs.h ext2_io.h ext2_fs.h ext2_ext_attr.h HFILES_IN= ext2_err.h ext2_types.h LIBRARY= libext2fs diff --git a/lib/ext2fs/ext2_ext_attr.h b/lib/ext2fs/ext2_ext_attr.h new file mode 100644 index 0000000..bbc90d9 --- /dev/null +++ b/lib/ext2fs/ext2_ext_attr.h @@ -0,0 +1,60 @@ +/* + File: linux/ext2_ext_attr.h + + On-disk format of extended attributes for the ext2 filesystem. + + (C) 2000 Andreas Gruenbacher, +*/ + +#include + +/* Magic value in attribute blocks */ +#define EXT2_EXT_ATTR_MAGIC 0xEA010000 + +/* Maximum number of references to one attribute block */ +#define EXT2_EXT_ATTR_REFCOUNT_MAX 1024 + +struct ext2_ext_attr_header { + __u32 h_magic; /* magic number for identification */ + __u32 h_refcount; /* reference count */ + __u32 h_blocks; /* number of disk blocks used */ + __u32 h_hash; /* hash value of all attributes */ + __u32 h_reserved[4]; /* zero right now */ +}; + +struct ext2_ext_attr_entry { + __u8 e_name_len; /* length of name */ + __u8 e_name_index; /* index into table of names (n/i) */ + __u16 e_value_offs; /* offset in disk block of value */ + __u32 e_value_block; /* disk block attribute is stored on (n/i) */ + __u32 e_value_size; /* size of attribute value */ + __u32 e_hash; /* hash value of name and value */ + char e_name[0]; /* attribute name */ +}; + +#define EXT2_EXT_ATTR_PAD_BITS 2 +#define EXT2_EXT_ATTR_PAD (1<e_name_len)) ) +#define EXT2_EXT_ATTR_SIZE(size) \ + (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) + +#ifdef __KERNEL__ +# ifdef CONFIG_EXT2_FS_EXT_ATTR +extern int ext2_get_ext_attr(struct inode *, const char *, char *, size_t, int); +extern int ext2_set_ext_attr(struct inode *, const char *, char *, size_t, int); +extern void ext2_ext_attr_free_inode(struct inode *inode); +extern void ext2_ext_attr_put_super(struct super_block *sb); +extern int ext2_ext_attr_init(void); +extern void ext2_ext_attr_done(void); +# else +# define ext2_get_ext_attr NULL +# define ext2_set_ext_attr NULL +# endif +#endif /* __KERNEL__ */ + diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index f89ccc7..2b23e04 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -402,7 +402,8 @@ typedef struct ext2_icount *ext2_icount_t; */ #define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\ EXT2_FEATURE_COMPAT_IMAGIC_INODES|\ - EXT3_FEATURE_COMPAT_HAS_JOURNAL) + EXT3_FEATURE_COMPAT_HAS_JOURNAL|\ + EXT2_FEATURE_COMPAT_EXT_ATTR) /* This #ifdef is temporary until compression is fully supported */ #ifdef ENABLE_COMPRESSION @@ -607,6 +608,10 @@ extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest); /* expanddir.c */ extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir); +/* ext_attr.c */ +void ext2fs_swap_ext_attr(ext2_filsys fs, char *to, char *from); +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); /* fileio.c */ extern errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, int flags, ext2_file_t *ret); diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c new file mode 100644 index 0000000..480df36 --- /dev/null +++ b/lib/ext2fs/ext_attr.c @@ -0,0 +1,98 @@ +/* + * ext_attr.c --- extended attribute blocks + * + * Copyright (C) Andreas Gruenbacher, + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2_ext_attr.h" + +#include "ext2fs.h" + +#ifdef EXT2FS_ENABLE_SWAPFS +void ext2fs_swap_ext_attr(ext2_filsys fs, char *to, char *from) +{ + struct ext2_ext_attr_header *from_header = + (struct ext2_ext_attr_header *)from; + struct ext2_ext_attr_header *to_header = + (struct ext2_ext_attr_header *)to; + struct ext2_ext_attr_entry *from_entry, *to_entry; + char *from_end = (char *)from_header + fs->blocksize; + int n; + + if (to_header != from_header) + memcpy(to_header, from_header, fs->blocksize); + + 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]); + + from_entry = (struct ext2_ext_attr_entry *)(from_header+1); + to_entry = (struct ext2_ext_attr_entry *)(to_header+1); + 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); + from_entry = EXT2_EXT_ATTR_NEXT(from_entry); + to_entry = EXT2_EXT_ATTR_NEXT(to_entry); + } +} +#endif + +errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) +{ + errcode_t retval; + struct ext2_ext_attr_entry *entry; + + retval = io_channel_read_blk(fs->io, block, 1, buf); + if (retval) + return retval; +#ifdef EXT2FS_ENABLE_SWAPFS + if ((fs->flags & (EXT2_FLAG_SWAP_BYTES| + EXT2_FLAG_SWAP_BYTES_READ)) != 0) + ext2fs_swap_ext_attr(fs, buf, buf); +#endif + return 0; +} + +errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) +{ + errcode_t retval; + char *p, *end, *write_buf; + char *buf = NULL; + struct ext2_dir_entry *dirent; + +#ifdef EXT2FS_ENABLE_SWAPFS + if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) { + retval = ext2fs_get_mem(fs->blocksize, (void **) &buf); + if (retval) + return retval; + write_buf = buf; + ext2fs_swap_ext_attr(fs, buf, inbuf); + } else +#endif + write_buf = (char *) inbuf; + retval = io_channel_write_blk(fs->io, block, 1, write_buf); + if (buf) + ext2fs_free_mem((void **) &buf); + return retval; +} -- 1.8.3.1