Whamcloud - gitweb
Add initial support for extended attribute blocks
authorTheodore Ts'o <tytso@mit.edu>
Mon, 2 Jul 2001 15:54:09 +0000 (11:54 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 2 Jul 2001 15:54:09 +0000 (11:54 -0400)
18 files changed:
e2fsck/ChangeLog
e2fsck/Makefile.in
e2fsck/e2fsck.c
e2fsck/e2fsck.h
e2fsck/ea_refcount.c [new file with mode: 0644]
e2fsck/message.c
e2fsck/pass1.c
e2fsck/pass1b.c
e2fsck/pass2.c
e2fsck/problem.c
e2fsck/problem.h
lib/e2p/ChangeLog
lib/e2p/feature.c
lib/ext2fs/ChangeLog
lib/ext2fs/Makefile.in
lib/ext2fs/ext2_ext_attr.h [new file with mode: 0644]
lib/ext2fs/ext2fs.h
lib/ext2fs/ext_attr.c [new file with mode: 0644]

index ea0e6fb..b24e806 100644 (file)
@@ -1,3 +1,37 @@
+2001-07-02  Theodore Tso  <tytso@valinux.com>
+
+       * 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  <tytso@valinux.com>
+
+       * 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  <tytso@valinux.com>
 
        * unix.c (show_stats): Use long long to calculate the percentage
index e0c746e..2fde4b0 100644 (file)
@@ -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)
 
index 66e4a75..3d9750c 100644 (file)
@@ -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;
index da41cca..26f63cf 100644 (file)
@@ -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 (file)
index 0000000..9a3f652
--- /dev/null
@@ -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 <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+
+#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
index 26a7c02..2c7e771 100644 (file)
@@ -50,6 +50,7 @@
  *
  * The following '@' expansions are supported:
  *
+ *     @a      extended attribute
  *     @A      error allocating
  *     @b      block
  *     @B      bitmap
  * letter <i> in the table below.
  */
 static const char *abbrevs[] = {
+       N_("aextended attribute"),
        N_("Aerror allocating"),
        N_("bblock"),
        N_("Bbitmap"),
index f3ab667..a9731c6 100644 (file)
@@ -43,6 +43,8 @@
 #endif
 
 #include "e2fsck.h"
+#include <ext2fs/ext2_ext_attr.h>
+
 #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
index f300de3..f0584e8 100644 (file)
@@ -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;
 }
 
index 13b398e..a885f6f 100644 (file)
@@ -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)) {
index 84061b3..e5e62cf 100644 (file)
@@ -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 */
index 5df40ef..7136381 100644 (file)
@@ -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
  */
index 0df0008..4b06626 100644 (file)
@@ -1,5 +1,8 @@
 2001-06-23  Theodore Tso  <tytso@valinux.com>
 
+       * feature.c: Add entry in feature table to interpret
+               EXT2_FEATURE_COMPAT_EXT_ATTR.
+
        * Release of E2fsprogs 1.22
 
 2001-06-15  Theodore Tso  <tytso@valinux.com>
index c9d2427..2586727 100644 (file)
@@ -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,
index e954166..43e23f6 100644 (file)
@@ -1,5 +1,9 @@
 2001-06-23  Theodore Tso  <tytso@valinux.com>
 
+       * 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  <tytso@valinux.com>
index d5adcb6..948a873 100644 (file)
@@ -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 (file)
index 0000000..bbc90d9
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+  File: linux/ext2_ext_attr.h
+
+  On-disk format of extended attributes for the ext2 filesystem.
+
+  (C) 2000 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+*/
+
+#include <linux/config.h>
+
+/* 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<<EXT2_EXT_ATTR_PAD_BITS)
+#define EXT2_EXT_ATTR_ROUND            (EXT2_EXT_ATTR_PAD-1)
+#define EXT2_EXT_ATTR_LEN(name_len) \
+       (((name_len) + EXT2_EXT_ATTR_ROUND + \
+       sizeof(struct ext2_ext_attr_entry)) & ~EXT2_EXT_ATTR_ROUND)
+#define EXT2_EXT_ATTR_NEXT(entry) \
+       ( (struct ext2_ext_attr_entry *)( \
+         (char *)(entry) + EXT2_EXT_ATTR_LEN((entry)->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__ */
+
index f89ccc7..2b23e04 100644 (file)
@@ -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 (file)
index 0000000..480df36
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * ext_attr.c --- extended attribute blocks
+ * 
+ * Copyright (C) Andreas Gruenbacher, <a.gruenbacher@computer.org>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#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;
+}