]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsprogs-extents.patch
authorAndreas Dilger <adilger@sun.com>
Sat, 2 Feb 2008 08:27:01 +0000 (01:27 -0700)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 11 Feb 2008 02:58:12 +0000 (21:58 -0500)
Support for checking 32-bit extents format inodes and the INCOMPAT_EXTENTS
feature.

Clear the high 16 bits of extents and index entries, since the
extents patches did not do this explicitly.  Some parts of this
code need fixing for checking > 32-bit block filesystems (when
INCOMPAT_64BIT support is added), marked "FIXME: 48-bit support".

Verify extent headers in blocks, logical ordering of extents,
logical ordering of indexes.

Add explicit checking of {d,t,}indirect and index blocks to detect
corruption instead of implicitly doing this by checking the referred
blocks and only block-at-a-time correctness.  This avoids incorrectly
invoking the very lengthy duplicate blocks pass for bad indirect/index
blocks.  We may want to tune the "threshold" for how many errors make
a "bad" indirect/index block.

Add ability to split or remove extents in order to allow extent
reallocation during the duplicate blocks pass.

23 files changed:
e2fsck/Makefile.in
e2fsck/e2fsck.h
e2fsck/pass1.c
e2fsck/pass2.c
e2fsck/problem.c
e2fsck/problem.h
lib/ext2fs/Makefile.in
lib/ext2fs/block.c
lib/ext2fs/block.h [new file with mode: 0644]
lib/ext2fs/bmap.c
lib/ext2fs/ext2_err.et.in
lib/ext2fs/ext2fs.h
lib/ext2fs/extents.c [new file with mode: 0644]
lib/ext2fs/ind_block.c
lib/ext2fs/swapfs.c
lib/ext2fs/valid_blk.c
tests/f_bad_disconnected_inode/expect.1
tests/f_bbfile/expect.1
tests/f_bbfile/expect.2
tests/f_lotsbad/expect.1
tests/f_lotsbad/expect.2
tests/f_messy_inode/expect.1
tests/f_messy_inode/expect.2

index 595c104024f03b4973601c96032ed03bb5fff690..e70c1694cc40f383c87a82343d06ea72d607cd0f 100644 (file)
@@ -257,6 +257,7 @@ super.o: $(srcdir)/super.c $(top_srcdir)/lib/uuid/uuid.h $(srcdir)/e2fsck.h \
 pass1.o: $(srcdir)/pass1.c $(srcdir)/e2fsck.h \
  $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
  $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
+ $(top_srcdir)/lib/ext2fs/ext3_extents.h \
  $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
  $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \
  $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \
index 8e5674f0f07ba3f63717b6f3905c688d856582a4..b0ba227107eec9e92649c27e41e571364c1a9af8 100644 (file)
@@ -326,6 +326,7 @@ struct e2fsck_struct {
        __u32 large_files;
        __u32 fs_ext_attr_inodes;
        __u32 fs_ext_attr_blocks;
+       __u32 extent_files;
 
        /* misc fields */
        time_t now;
index 53382c384e5a233292319b52660837ec26f7b2db..27e749fedc17354904c2b930a9b4700ddf45d8d0 100644 (file)
@@ -46,6 +46,7 @@
 
 #include "e2fsck.h"
 #include <ext2fs/ext2_ext_attr.h>
+#include <ext2fs/ext3_extents.h>
 
 #include "problem.h"
 
@@ -79,16 +80,19 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
 struct process_block_struct {
        ext2_ino_t      ino;
        unsigned        is_dir:1, is_reg:1, clear:1, suppress:1,
-                               fragmented:1, compressed:1, bbcheck:1;
+                       fragmented:1, compressed:1, bbcheck:1, extent:1;
        blk_t           num_blocks;
        blk_t           max_blocks;
        e2_blkcnt_t     last_block;
        int             num_illegal_blocks;
+       int             last_illegal_blocks;
        blk_t           previous_block;
        struct ext2_inode *inode;
        struct problem_context *pctx;
        ext2fs_block_bitmap fs_meta_blocks;
        e2fsck_t        ctx;
+       struct ext3_extent_header *eh_prev;
+       void            *block_buf;
 };
 
 struct process_inode_block {
@@ -137,7 +141,7 @@ int e2fsck_pass1_check_device_inode(ext2_filsys fs EXT2FS_ATTR((unused)),
         * If the index flag is set, then this is a bogus
         * device/fifo/socket
         */
-       if (inode->i_flags & EXT2_INDEX_FL)
+       if (inode->i_flags & (EXT2_INDEX_FL | EXT4_EXTENTS_FL))
                return 0;
 
        /*
@@ -171,7 +175,7 @@ int e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode,
        blk_t   blocks;
 
        if ((inode->i_size_high || inode->i_size == 0) ||
-           (inode->i_flags & EXT2_INDEX_FL))
+           (inode->i_flags & (EXT2_INDEX_FL | EXT4_EXTENTS_FL)))
                return 0;
 
        blocks = ext2fs_inode_data_blocks(fs, inode);
@@ -484,7 +488,9 @@ void e2fsck_pass1(e2fsck_t ctx)
        int             imagic_fs;
        int             busted_fs_time = 0;
        int             inode_size;
-       
+       struct ext3_extent_header *eh;
+       int             extent_fs;
+
 #ifdef RESOURCE_TRACK
        init_resource_track(&rtrack);
 #endif
@@ -515,6 +521,7 @@ void e2fsck_pass1(e2fsck_t ctx)
 #undef EXT2_BPP
 
        imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
+       extent_fs = (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS);
 
        /*
         * Allocate bitmaps structures
@@ -895,8 +902,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                                check_blocks(ctx, &pctx, block_buf);
                                continue;
                        }
-               }
-               else if (LINUX_S_ISFIFO (inode->i_mode) &&
+               } else if (LINUX_S_ISFIFO (inode->i_mode) &&
                         e2fsck_pass1_check_device_inode(fs, inode)) {
                        check_immutable(ctx, &pctx);
                        check_size(ctx, &pctx);
@@ -908,21 +914,75 @@ void e2fsck_pass1(e2fsck_t ctx)
                        ctx->fs_sockets_count++;
                } else
                        mark_inode_bad(ctx, ino);
-               if (inode->i_block[EXT2_IND_BLOCK])
-                       ctx->fs_ind_count++;
-               if (inode->i_block[EXT2_DIND_BLOCK])
-                       ctx->fs_dind_count++;
-               if (inode->i_block[EXT2_TIND_BLOCK])
-                       ctx->fs_tind_count++;
-               if (inode->i_block[EXT2_IND_BLOCK] ||
-                   inode->i_block[EXT2_DIND_BLOCK] ||
-                   inode->i_block[EXT2_TIND_BLOCK] ||
-                   inode->i_file_acl) {
-                       inodes_to_process[process_inode_count].ino = ino;
-                       inodes_to_process[process_inode_count].inode = *inode;
-                       process_inode_count++;
-               } else
-                       check_blocks(ctx, &pctx, block_buf);
+
+               eh = (struct ext3_extent_header *)inode->i_block;
+               if ((inode->i_flags & EXT4_EXTENTS_FL)) {
+                       if ((LINUX_S_ISREG(inode->i_mode) ||
+                            LINUX_S_ISDIR(inode->i_mode)) &&
+                           ext2fs_extent_header_verify(eh, EXT2_N_BLOCKS *
+                                                       sizeof(__u32)) == 0) {
+                               if (!extent_fs &&
+                                   fix_problem(ctx,PR_1_EXTENT_FEATURE,&pctx)){
+                                       sb->s_feature_incompat |=
+                                               EXT3_FEATURE_INCOMPAT_EXTENTS;
+                                       ext2fs_mark_super_dirty(fs);
+                                       extent_fs = 1;
+                               }
+                       } else if (fix_problem(ctx, PR_1_SET_EXTENT_FL, &pctx)){
+                               inode->i_flags &= ~EXT4_EXTENTS_FL;
+                               e2fsck_write_inode(ctx, ino, inode, "pass1");
+                               goto check_ind_inode;
+                       }
+               } else if (extent_fs &&
+                          (LINUX_S_ISREG(inode->i_mode) ||
+                           LINUX_S_ISDIR(inode->i_mode)) &&
+                          ext2fs_extent_header_verify(eh, EXT2_N_BLOCKS *
+                                                      sizeof(__u32)) == 0 &&
+                          fix_problem(ctx, PR_1_UNSET_EXTENT_FL, &pctx)) {
+                       inode->i_flags |= EXT4_EXTENTS_FL;
+                       e2fsck_write_inode(ctx, ino, inode, "pass1");
+               }
+               if (extent_fs && inode->i_flags & EXT4_EXTENTS_FL) {
+                       ctx->extent_files++;
+                       switch(eh->eh_depth) {
+                       case 0:
+                               break;
+                       case 1:
+                               ctx->fs_ind_count++;
+                               break;
+                       case 2:
+                               ctx->fs_dind_count++;
+                               break;
+                       default:
+                               ctx->fs_tind_count++;
+                               break;
+                       }
+                       if (eh->eh_depth > 0) {
+                               inodes_to_process[process_inode_count].ino = ino;
+                               inodes_to_process[process_inode_count].inode = *inode;
+                               process_inode_count++;
+                       } else {
+                               check_blocks(ctx, &pctx, block_buf);
+                       }
+               } else {
+               check_ind_inode:
+                       if (inode->i_block[EXT2_IND_BLOCK])
+                               ctx->fs_ind_count++;
+                       if (inode->i_block[EXT2_DIND_BLOCK])
+                               ctx->fs_dind_count++;
+                       if (inode->i_block[EXT2_TIND_BLOCK])
+                               ctx->fs_tind_count++;
+                       if (inode->i_block[EXT2_IND_BLOCK] ||
+                           inode->i_block[EXT2_DIND_BLOCK] ||
+                           inode->i_block[EXT2_TIND_BLOCK] ||
+                           inode->i_file_acl) {
+                               inodes_to_process[process_inode_count].ino = ino;
+                               inodes_to_process[process_inode_count].inode = *inode;
+                               process_inode_count++;
+                       } else {
+                               check_blocks(ctx, &pctx, block_buf);
+                       }
+               }
 
                if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
                        return;
@@ -1430,10 +1490,23 @@ clear_extattr:
        return 0;
 }
 
+static int htree_blk_iter_cb(ext2_filsys fs EXT2FS_ATTR((unused)),
+                            blk_t *blocknr,
+                            e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
+                            blk_t ref_blk EXT2FS_ATTR((unused)),
+                            int ref_offset EXT2FS_ATTR((unused)),
+                            void *priv_data)
+{
+       blk_t *blk = priv_data;
+
+       *blk = *blocknr;
+
+       return BLOCK_ABORT;
+}
+
 /* Returns 1 if bad htree, 0 if OK */
 static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
-                       ext2_ino_t ino EXT2FS_ATTR((unused)),
-                       struct ext2_inode *inode,
+                       ext2_ino_t ino, struct ext2_inode *inode,
                        char *block_buf)
 {
        struct ext2_dx_root_info        *root;
@@ -1447,7 +1520,8 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
             fix_problem(ctx, PR_1_HTREE_SET, pctx)))
                return 1;
 
-       blk = inode->i_block[0];
+       ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DATA_ONLY | BLOCK_FLAG_HOLE,
+                             block_buf, htree_blk_iter_cb, &blk);
        if (((blk == 0) ||
             (blk < fs->super->s_first_data_block) ||
             (blk >= fs->super->s_blocks_count)) &&
@@ -1484,6 +1558,135 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
        return 0;
 }
 
+/* sort 0 to the end of the list so we can exit early */
+static EXT2_QSORT_TYPE verify_ind_cmp(const void *a, const void *b)
+{
+       const __u32 blk_a = *(__u32 *)a - 1, blk_b = *(__u32 *)b - 1;
+
+       return blk_b > blk_a ? -1 : blk_a - blk_b;
+}
+
+/* Verify whether an indirect block is sane.  If it has multiple references
+ * to the same block, or if it has a large number of bad or duplicate blocks
+ * chances are that it is corrupt and we should just clear it instead of
+ * trying to salvage it.
+ * NOTE: this needs to get a copy of the blocks, since it reorders them */
+static int e2fsck_ind_block_verify(struct process_block_struct *p,
+                                  void *block_buf, int buflen)
+{
+       __u32 blocks[EXT2_N_BLOCKS], *indir = block_buf;
+       int num_indir = buflen / sizeof(*indir);
+       int i, bad = 0;
+
+       if (num_indir == EXT2_N_BLOCKS) {
+               memcpy(blocks, block_buf, buflen);
+               indir = blocks;
+       }
+       qsort(indir, num_indir, sizeof(*indir), verify_ind_cmp);
+
+       for (i = 0; i < num_indir; i++) {
+               if (indir[i] == 0)
+                       break;
+
+               /* bad block number, or duplicate block */
+               if (indir[i] < p->ctx->fs->super->s_first_data_block ||
+                   indir[i] > p->ctx->fs->super->s_blocks_count ||
+                   ext2fs_fast_test_block_bitmap(p->ctx->block_found_map,
+                                                 indir[i]))
+                       bad++;
+
+               /* shouldn't reference the same block twice within a block */
+               if (i > 0 && indir[i] == indir[i - 1])
+                       bad++;
+       }
+
+       if ((num_indir <= EXT2_N_BLOCKS && bad > 4) || bad > 8)
+               return PR_1_INDIRECT_BAD;
+
+#if DEBUG_E2FSCK
+       /* For debugging, clobber buffer to ensure it doesn't appear sane */
+       memset(indir, 0xca, buflen);
+#endif
+       return 0;
+}
+
+static int e2fsck_ext_block_verify(struct process_block_struct *p,
+                                  void *block_buf, int buflen)
+{
+       struct ext3_extent_header *eh = block_buf, *eh_sav;
+       e2fsck_t ctx = p->ctx;
+       struct problem_context *pctx = p->pctx;
+       int i, problem = 0, high_bits_ok = 0;
+
+       if (ext2fs_extent_header_verify(eh, buflen))
+               return PR_1_EXTENT_IDX_BAD;
+
+       if (p->eh_prev && p->eh_prev->eh_depth != eh->eh_depth + 1)
+               return PR_1_EXTENT_IDX_BAD;
+
+       if (ctx->fs->super->s_blocks_count_hi) /* FIXME: 48-bit support ??? */
+               high_bits_ok = 1;
+
+       eh_sav = p->eh_prev;
+       p->eh_prev = eh;
+
+       if (eh->eh_depth == 0) {
+               struct ext3_extent *ex = EXT_FIRST_EXTENT(eh), *ex_prev = NULL;
+
+               for (i = 0; i < eh->eh_entries; i++, ex++) {
+                       if (ex->ee_start_hi && !high_bits_ok &&
+                           fix_problem(ctx, PR_1_EXTENT_HI, pctx)) {
+                               ex->ee_start_hi = 0;
+                               problem = PR_1_EXTENT_CHANGED;
+                       }
+
+                       if (ext2fs_extent_verify(ctx->fs, ex, ex_prev, NULL,0)){
+                               p->num_illegal_blocks++;
+                               pctx->blkcount = ex->ee_start;
+                               pctx->num = ex->ee_len;
+                               pctx->blk = ex->ee_block;
+                               if (fix_problem(ctx, PR_1_EXTENT_BAD, pctx)) {
+                                       ext2fs_extent_remove(eh, ex);
+                                       i--; ex--; /* check next (moved) item */
+                                       problem = PR_1_EXTENT_CHANGED;
+                                       continue;
+                               }
+                       }
+
+                       ex_prev = ex;
+               }
+       } else {
+               struct ext3_extent_idx *ix =EXT_FIRST_INDEX(eh), *ix_prev =NULL;
+
+               for (i = 0; i < eh->eh_entries; i++, ix++) {
+                       if (ix->ei_leaf_hi && !high_bits_ok &&
+                           fix_problem(ctx, PR_1_EXTENT_HI, pctx)) {
+                               ix->ei_leaf_hi = ix->ei_unused = 0;
+                               problem = PR_1_EXTENT_CHANGED;
+                       }
+
+                       if (ext2fs_extent_index_verify(ctx->fs, ix, ix_prev)) {
+                               p->num_illegal_blocks++;
+                               pctx->blkcount = ix->ei_leaf;;
+                               pctx->num = i;
+                               pctx->blk = ix->ei_block;
+                               if (fix_problem(ctx, PR_1_EXTENT_IDX_BAD,pctx)){
+                                       ext2fs_extent_index_remove(eh, ix);
+                                       i--; ix--; /* check next (moved) item */
+                                       problem = PR_1_EXTENT_CHANGED;
+                                       continue;
+                               }
+                       }
+
+                       ix_prev = ix;
+               }
+       }
+
+       p->eh_prev = eh_sav;
+
+       return problem;
+}
+
 /*
  * This subroutine is called on each inode to account for all of the
  * blocks used by that inode.
@@ -1503,9 +1706,11 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
        pb.num_blocks = 0;
        pb.last_block = -1;
        pb.num_illegal_blocks = 0;
+       pb.last_illegal_blocks = 0;
        pb.suppress = 0; pb.clear = 0;
        pb.fragmented = 0;
        pb.compressed = 0;
+       pb.extent = !!(inode->i_flags & EXT4_EXTENTS_FL);
        pb.previous_block = 0;
        pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
        pb.is_reg = LINUX_S_ISREG(inode->i_mode);
@@ -1513,6 +1718,8 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
        pb.inode = inode;
        pb.pctx = pctx;
        pb.ctx = ctx;
+       pb.eh_prev = NULL;
+       pb.block_buf = block_buf;
        pctx->ino = ino;
        pctx->errcode = 0;
 
@@ -1534,10 +1741,27 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                pb.num_blocks++;
        }
 
-       if (ext2fs_inode_has_valid_blocks(inode))
-               pctx->errcode = ext2fs_block_iterate2(fs, ino,
-                                      pb.is_dir ? BLOCK_FLAG_HOLE : 0,
-                                      block_buf, process_block, &pb);
+       if (ext2fs_inode_has_valid_blocks(inode)) {
+               int problem = 0;
+
+               if (pb.extent)
+                       problem = e2fsck_ext_block_verify(&pb, inode->i_block,
+                                                       sizeof(inode->i_block));
+               else
+                       problem = e2fsck_ind_block_verify(&pb, inode->i_block,
+                                                       sizeof(inode->i_block));
+               if (problem == PR_1_EXTENT_CHANGED) {
+                       dirty_inode++;
+                       problem = 0;
+               }
+
+               if (problem && fix_problem(ctx, problem, pctx))
+                       pb.clear = 1;
+               else
+                       pctx->errcode = ext2fs_block_iterate2(fs, ino,
+                                               pb.is_dir ? BLOCK_FLAG_HOLE : 0,
+                                               block_buf, process_block, &pb);
+       }
        end_problem_latch(ctx, PR_LATCH_BLOCK);
        end_problem_latch(ctx, PR_LATCH_TOOBIG);
        if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
@@ -1701,6 +1925,9 @@ static char *describe_illegal_block(ext2_filsys fs, blk_t block)
 }
 #endif
 
+#define IND_BLKCNT(_b) ((_b) == BLOCK_COUNT_IND || (_b) == BLOCK_COUNT_DIND ||\
+                       (_b) == BLOCK_COUNT_TIND)
+
 /*
  * This is a helper function for check_blocks().
  */
@@ -1779,7 +2006,8 @@ static int process_block(ext2_filsys fs,
         * file be contiguous.  (Which can never be true for really
         * big files that are greater than a block group.)
         */
-       if (!HOLE_BLKADDR(p->previous_block)) {
+       if (!HOLE_BLKADDR(p->previous_block) &&
+           !(p->extent && IND_BLKCNT(blockcnt))) {
                if (p->previous_block+1 != blk)
                        p->fragmented = 1;
        }
@@ -1796,9 +2024,34 @@ static int process_block(ext2_filsys fs,
            blk >= fs->super->s_blocks_count)
                problem = PR_1_ILLEGAL_BLOCK_NUM;
 
+       if (!problem && IND_BLKCNT(blockcnt) && p->ino != EXT2_RESIZE_INO) {
+               if (p->extent) {
+                       if (ext2fs_read_ext_block(ctx->fs, blk, p->block_buf))
+                               problem = PR_1_BLOCK_ITERATE;
+                       else
+                               problem = e2fsck_ext_block_verify(p,
+                                                                p->block_buf,
+                                                                fs->blocksize);
+                       if (problem == PR_1_EXTENT_CHANGED) {
+                               if (ext2fs_write_ext_block(ctx->fs, blk,
+                                                          p->block_buf))
+                                       problem = PR_1_BLOCK_ITERATE;
+                       }
+
+               } else {
+                       if (ext2fs_read_ind_block(ctx->fs, blk, p->block_buf))
+                               problem = PR_1_BLOCK_ITERATE;
+                       else
+                               problem = e2fsck_ind_block_verify(p,
+                                                                p->block_buf,
+                                                                fs->blocksize);
+               }
+       }
+
        if (problem) {
                p->num_illegal_blocks++;
-               if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
+               if (!p->suppress &&
+                   p->num_illegal_blocks - p->last_illegal_blocks > 12) {
                        if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
                                p->clear = 1;
                                return BLOCK_ABORT;
@@ -1808,9 +2061,12 @@ static int process_block(ext2_filsys fs,
                                set_latch_flags(PR_LATCH_BLOCK,
                                                PRL_SUPPRESS, 0);
                        }
+                       p->last_illegal_blocks = p->num_illegal_blocks;
                }
                pctx->blk = blk;
                pctx->blkcount = blockcnt;
+               if (problem == PR_1_EXTENT_CHANGED)
+                       goto mark_used;
                if (fix_problem(ctx, problem, pctx)) {
                        blk = *block_nr = 0;
                        ret_code = BLOCK_CHANGED;
@@ -1819,6 +2075,7 @@ static int process_block(ext2_filsys fs,
                        return 0;
        }
 
+mark_used:
        if (p->ino == EXT2_RESIZE_INO) {
                /* 
                 * The resize inode has already be sanity checked
index b8fa505caac6550dd22495c872e7b292b961c049..3bb71b8b68bd50512019944bcddac429b83c2105 100644 (file)
@@ -285,7 +285,16 @@ void e2fsck_pass2(e2fsck_t ctx)
                        ext2fs_mark_super_dirty(fs);
                }
        }
-       
+
+       if (!ctx->extent_files &&
+           (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS)) {
+               if (fs->flags & EXT2_FLAG_RW) {
+                       sb->s_feature_incompat &=
+                               ~EXT3_FEATURE_INCOMPAT_EXTENTS;
+                       ext2fs_mark_super_dirty(fs);
+               }
+       }
+
 #ifdef RESOURCE_TRACK
        if (ctx->options & E2F_OPT_TIME2) {
                e2fsck_clear_progbar(ctx);
index 61c2b914f24f762727cea2138544a7242213f665..58c7d0b2cec298e8a9bd035f1512e9a1fafeea6b 100644 (file)
@@ -784,6 +784,46 @@ static struct e2fsck_problem problem_table[] = {
          N_("@i %i is a %It but it looks like it is really a directory.\n"),
          PROMPT_FIX, 0 },
 
+       /* indirect block corrupt */
+       { PR_1_INDIRECT_BAD,
+         N_("@i %i has corrupt indirect block\n"),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* inode has extents, superblock missing INCOMPAT_EXTENTS feature */
+       { PR_1_EXTENT_FEATURE,
+         N_("@i %i is in extent format, but @S is missing EXTENTS feature\n"),
+         PROMPT_FIX, PR_PREEN_OK },
+
+       /* inode has EXTENTS_FL set, but is not an extent inode */
+       { PR_1_SET_EXTENT_FL,
+         N_("@i %i has EXTENT_FL set, but is not in extents format\n"),
+         PROMPT_FIX, PR_PREEN_OK },
+
+       /* inode missing EXTENTS_FL, but is an extent inode */
+       { PR_1_UNSET_EXTENT_FL,
+         N_("@i %i missing EXTENT_FL, but is in extents format\n"),
+         PROMPT_FIX, PR_PREEN_OK },
+
+       /* extent index corrupt */
+       { PR_1_EXTENT_BAD,
+         N_("@i %i has corrupt extent at @b %b (logical %B) length %N\n"),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* extent index corrupt */
+       { PR_1_EXTENT_IDX_BAD,
+         N_("@i %i has corrupt extent index at @b %b (logical %B) entry %N\n"),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* extent has high 16 bits set */
+       { PR_1_EXTENT_HI,
+         N_("High 16 bits of extent/index @b set\n"),
+         PROMPT_CLEAR, PR_LATCH_EXTENT_HI|PR_PREEN_OK|PR_NO_OK|PR_PREEN_NOMSG},
+
+       /* extent has high 16 bits set header */
+       { PR_1_EXTENT_HI_LATCH,
+         N_("@i %i has high 16 bits of extent/index @b set\n"),
+         PROMPT_CLEAR, PR_PREEN_OK | PR_NO_OK | PR_PREEN_NOMSG },
+
        /* Pass 1b errors */
 
        /* Pass 1B: Rescan for duplicate/bad blocks */
@@ -1518,6 +1558,7 @@ static struct latch_descr pr_latch_info[] = {
        { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
        { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
        { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
+       { PR_LATCH_EXTENT_HI, PR_1_EXTENT_HI_LATCH, 0 },
        { -1, 0, 0 },
 };
 
index f5f7212fd61bcba4ae32f631276da9fbf366dd67..9833904059c2dfbbc5c6ceebde6e531268a143f6 100644 (file)
@@ -38,6 +38,7 @@ struct problem_context {
 #define PR_LATCH_LOW_DTIME 0x0070 /* Latch for pass1 orphaned list refugees */
 #define PR_LATCH_TOOBIG        0x0080  /* Latch for file to big errors */
 #define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */
+#define PR_LATCH_EXTENT_HI 0x00A0 /* Latch for extent high bits set */
 
 #define PR_LATCH(x)    ((((x) & PR_LATCH_MASK) >> 4) - 1)
 
@@ -455,6 +456,33 @@ struct problem_context {
 /* inode appears to be a directory */
 #define PR_1_TREAT_AS_DIRECTORY                0x010055
 
+/* indirect block corrupt */
+#define PR_1_INDIRECT_BAD              0x010059
+
+/* wrong EXT3_FEATURE_INCOMPAT_EXTENTS flag */
+#define PR_1_EXTENT_FEATURE            0x010060
+
+/* EXT4_EXTENT_FL flag set on non-extent file */
+#define PR_1_SET_EXTENT_FL             0x010061
+
+/* EXT4_EXTENT_FL flag not set extent file */
+#define PR_1_UNSET_EXTENT_FL           0x010062
+
+/* extent index corrupt */
+#define PR_1_EXTENT_BAD                        0x010063
+
+/* extent index corrupt */
+#define PR_1_EXTENT_IDX_BAD            0x010064
+
+/* extent/index has high 16 bits set - header */
+#define PR_1_EXTENT_HI                 0x010065
+
+/* extent/index has high 16 bits set */
+#define PR_1_EXTENT_HI_LATCH           0x010066
+
+/* extent/index was modified & repaired - not really a problem */
+#define PR_1_EXTENT_CHANGED            0x010067
+
 /*
  * Pass 1b errors
  */
index 199164abc3cf074497ef6b86f243e04f15208261..c8f6af5016abe748ca77915fc9a15ba20cbc0fa2 100644 (file)
@@ -35,6 +35,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \
        dir_iterate.o \
        expanddir.o \
        ext_attr.o \
+       extents.o \
        finddev.o \
        flushb.o \
        freefs.o \
@@ -91,6 +92,7 @@ SRCS= ext2_err.c \
        $(srcdir)/dupfs.c \
        $(srcdir)/expanddir.c \
        $(srcdir)/ext_attr.c \
+       $(srcdir)/extents.c \
        $(srcdir)/fileio.c \
        $(srcdir)/finddev.c \
        $(srcdir)/flushb.c \
@@ -134,7 +136,8 @@ SRCS= ext2_err.c \
        $(srcdir)/tst_bitops.c \
        $(srcdir)/tst_byteswap.c \
        $(srcdir)/tst_getsize.c \
-       $(srcdir)/tst_iscan.c
+       $(srcdir)/tst_iscan.c \
+       $(srcdir)/tst_types.c
 
 HFILES= bitops.h ext2fs.h ext2_io.h ext2_fs.h ext2_ext_attr.h ext3_extents.h \
        tdb.h
@@ -402,6 +405,10 @@ ext_attr.o: $(srcdir)/ext_attr.c $(srcdir)/ext2_fs.h \
  $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
  $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
  $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h
+extents.o: $(srcdir)/extents.c $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext3_extents.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h
 fileio.o: $(srcdir)/fileio.c $(srcdir)/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
  $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
index 07a64157e6f407b80543922d3ef1612a56474338..532ff960b69f73b83aab3acafad75228aefc43f9 100644 (file)
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
+#include "block.h"
 
-struct block_context {
-       ext2_filsys     fs;
-       int (*func)(ext2_filsys fs,
-                   blk_t       *blocknr,
-                   e2_blkcnt_t bcount,
-                   blk_t       ref_blk,
-                   int         ref_offset,
-                   void        *priv_data);
-       e2_blkcnt_t     bcount;
-       int             bsize;
-       int             flags;
-       errcode_t       errcode;
-       char    *ind_buf;
-       char    *dind_buf;
-       char    *tind_buf;
-       void    *priv_data;
-};
+#ifdef EXT_DEBUG
+void ext_show_inode(struct ext2_inode *inode, ext2_ino_t ino)
+{
+       printf("inode: %u blocks: %u\n",
+              ino, inode->i_blocks);
+}
+#else
+#define ext_show_inode(inode, ino) do { } while (0)
+#endif
 
 static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
                             int ref_offset, struct block_context *ctx)
@@ -276,7 +269,6 @@ errcode_t ext2fs_block_iterate2(ext2_filsys fs,
                                void *priv_data)
 {
        int     i;
-       int     got_inode = 0;
        int     ret = 0;
        blk_t   blocks[EXT2_N_BLOCKS];  /* directory data blocks */
        struct ext2_inode inode;
@@ -286,19 +278,20 @@ errcode_t ext2fs_block_iterate2(ext2_filsys fs,
 
        EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
+       ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
+       if (ctx.errcode)
+               return ctx.errcode;
+
        /*
         * Check to see if we need to limit large files
         */
        if (flags & BLOCK_FLAG_NO_LARGE) {
-               ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
-               if (ctx.errcode)
-                       return ctx.errcode;
-               got_inode = 1;
                if (!LINUX_S_ISDIR(inode.i_mode) &&
                    (inode.i_size_high != 0))
                        return EXT2_ET_FILE_TOO_BIG;
        }
 
+       /* The in-memory inode may have been changed by e2fsck */
        retval = ext2fs_get_blocks(fs, ino, blocks);
        if (retval)
                return retval;
@@ -325,10 +318,6 @@ errcode_t ext2fs_block_iterate2(ext2_filsys fs,
         */
        if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
            !(flags & BLOCK_FLAG_DATA_ONLY)) {
-               ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
-               if (ctx.errcode)
-                       goto abort_exit;
-               got_inode = 1;
                if (inode.osd1.hurd1.h_i_translator) {
                        ret |= (*ctx.func)(fs,
                                           &inode.osd1.hurd1.h_i_translator,
@@ -338,7 +327,16 @@ errcode_t ext2fs_block_iterate2(ext2_filsys fs,
                                goto abort_exit;
                }
        }
-       
+
+       /* Iterate over normal data blocks with extents.
+        * We can't do any fixing here because this gets called by other
+        * callers than e2fsck_pass1->check_blocks(). */
+       if (inode.i_flags & EXT4_EXTENTS_FL) {
+               ext_show_inode(&inode, ino);
+               ret |= block_iterate_extents(blocks, sizeof(blocks), 0, 0,&ctx);
+               goto abort_exit;
+       }
+
        /*
         * Iterate over normal data blocks
         */
@@ -373,11 +371,6 @@ errcode_t ext2fs_block_iterate2(ext2_filsys fs,
 
 abort_exit:
        if (ret & BLOCK_CHANGED) {
-               if (!got_inode) {
-                       retval = ext2fs_read_inode(fs, ino, &inode);
-                       if (retval)
-                               return retval;
-               }
                for (i=0; i < EXT2_N_BLOCKS; i++)
                        inode.i_block[i] = blocks[i];
                retval = ext2fs_write_inode(fs, ino, &inode);
diff --git a/lib/ext2fs/block.h b/lib/ext2fs/block.h
new file mode 100644 (file)
index 0000000..ccd6e13
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * block.h --- header for block iteration in block.c, extent.c
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+struct block_context {
+       ext2_filsys     fs;
+       int (*func)(ext2_filsys fs,
+                   blk_t       *blocknr,
+                   e2_blkcnt_t bcount,
+                   blk_t       ref_blk,
+                   int         ref_offset,
+                   void        *priv_data);
+       e2_blkcnt_t     bcount;
+       int             bsize;
+       int             flags;
+       errcode_t       errcode;
+       char    *ind_buf;
+       char    *dind_buf;
+       char    *tind_buf;
+       void    *priv_data;
+};
+
+/* libext2fs nternal function, in extent.c */
+extern int block_iterate_extents(void *eh_buf, unsigned bufsize,blk_t ref_block,
+                                int ref_offset EXT2FS_ATTR((unused)),
+                                struct block_context *ctx);
index 754fc49a1194d6e4a9f7d290caba76c4c971c11d..08199ce04c5fdcc9e1e4f7a698836927432780b1 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
+#include "ext3_extents.h"
 
 #if defined(__GNUC__) && !defined(NO_INLINE_FUNCS)
 #define _BMAP_INLINE_  __inline__
@@ -31,6 +32,65 @@ extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
 
 #define inode_bmap(inode, nr) ((inode)->i_block[(nr)])
 
+/* see also block_iterate_extents() */
+static errcode_t block_bmap_extents(void *eh_buf, unsigned bufsize,
+                                   ext2_filsys fs, blk_t block,blk_t *phys_blk)
+{
+       struct ext3_extent_header *eh = eh_buf;
+       struct ext3_extent *ex;
+       errcode_t ret = 0;
+       int i;
+
+       ret = ext2fs_extent_header_verify(eh, bufsize);
+       if (ret)
+               return ret;
+
+       if (eh->eh_depth == 0) {
+               ex = EXT_FIRST_EXTENT(eh);
+               for (i = 0; i < eh->eh_entries; i++, ex++) {
+                       if (block < ex->ee_block)
+                               continue;
+
+                       if (block < ex->ee_block + ex->ee_len)
+                               /* FIXME: 48-bit support */
+                               *phys_blk = ex->ee_start + block - ex->ee_block;
+
+                       /* only the first extent > block could hold the block
+                        * otherwise the extents would overlap */
+                       break;
+               }
+       } else {
+               struct ext3_extent_idx *ix;
+               char *block_buf;
+
+               ret = ext2fs_get_mem(fs->blocksize, &block_buf);
+               if (ret)
+                       return ret;
+
+               ix = EXT_FIRST_INDEX(eh);
+               for (i = 0; i < eh->eh_entries; i++, ix++) {
+                       if (block < ix->ei_block)
+                               continue;
+
+                       ret = io_channel_read_blk(fs->io, ix->ei_leaf, 1,
+                                                 block_buf);
+                       if (ret)
+                               goto free_buf;
+
+                       ret = block_bmap_extents(block_buf, fs->blocksize,
+                                                fs, block, phys_blk);
+
+                       /* only the first extent > block could hold the block
+                        * otherwise the extents would overlap */
+                       break;
+               }
+
+       free_buf:
+               ext2fs_free_mem(&block_buf);
+       }
+       return ret;
+}
+
 static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, 
                                              blk_t ind, char *block_buf, 
                                              int *blocks_alloc,
@@ -155,6 +215,16 @@ errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
                        return retval;
                inode = &inode_buf;
        }
+
+       if (inode->i_flags & EXT4_EXTENTS_FL) {
+               if (bmap_flags) /* unsupported as yet */
+                       return EXT2_ET_BLOCK_ALLOC_FAIL;
+               retval = block_bmap_extents(inode->i_block,
+                                           sizeof(inode->i_block),
+                                           fs, block, phys_blk);
+               goto done;
+       }
+
        addr_per_block = (blk_t) fs->blocksize >> 2;
 
        if (!block_buf) {
index eda4bb4ef1a000b71a1042f8baa1f93f82e21336..46ed50802c8baec1befd0c172fa62a626f240e19 100644 (file)
@@ -326,5 +326,17 @@ ec EXT2_ET_TDB_ERR_NOEXIST,
 ec     EXT2_ET_TDB_ERR_RDONLY,
        "TDB: Write not permitted"
 
+ec     EXT2_ET_EXTENT_HEADER_BAD,
+       "Corrupt extent header"
+
+ec     EXT2_ET_EXTENT_INDEX_BAD,
+       "Corrupt extent index"
+
+ec     EXT2_ET_EXTENT_LEAF_BAD,
+       "Corrupt extent"
+
+ec     EXT2_ET_EXTENT_NO_SPACE,
+       "No free space in extent map"
+
        end
 
index 4631bc96d07f7f69b897accbc83f628d75fdb354..f616f98057a6457e31c686e447498d5b177adcfd 100644 (file)
@@ -454,11 +454,13 @@ typedef struct ext2_icount *ext2_icount_t;
                                         EXT2_FEATURE_INCOMPAT_COMPRESSION|\
                                         EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
                                         EXT2_FEATURE_INCOMPAT_META_BG|\
+                                        EXT3_FEATURE_INCOMPAT_EXTENTS|\
                                         EXT3_FEATURE_INCOMPAT_RECOVER)
 #else
 #define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\
                                         EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
                                         EXT2_FEATURE_INCOMPAT_META_BG|\
+                                        EXT3_FEATURE_INCOMPAT_EXTENTS|\
                                         EXT3_FEATURE_INCOMPAT_RECOVER)
 #endif
 #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP        (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
@@ -725,6 +727,21 @@ extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
                                           char *block_buf,
                                           int adjust, __u32 *newcount);
 
+/* extent.c */
+errcode_t ext2fs_extent_header_verify(struct ext3_extent_header *eh, int size);
+errcode_t ext2fs_extent_verify(ext2_filsys fs, struct ext3_extent *ex,
+                              struct ext3_extent *ex_prev,
+                              struct ext3_extent_idx *ix, int ix_len);
+errcode_t ext2fs_extent_index_verify(ext2_filsys fs,
+                                    struct ext3_extent_idx *ix,
+                                    struct ext3_extent_idx *ix_prev);
+errcode_t ext2fs_extent_remove(struct ext3_extent_header *eh,
+                              struct ext3_extent *ex);
+errcode_t ext2fs_extent_split(ext2_filsys fs, struct ext3_extent_header **eh,
+                             struct ext3_extent **ex, int count, int *flag);
+errcode_t ext2fs_extent_index_remove(struct ext3_extent_header *eh,
+                                    struct ext3_extent_idx *ix);
+
 /* fileio.c */
 extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
                                   struct ext2_inode *inode,
@@ -779,6 +796,8 @@ extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags);
 /* ind_block.c */
 errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf);
 errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf);
+errcode_t ext2fs_read_ext_block(ext2_filsys fs, blk_t blk, void *buf);
+errcode_t ext2fs_write_ext_block(ext2_filsys fs, blk_t blk, void *buf);
 
 /* initialize.c */
 extern errcode_t ext2fs_initialize(const char *name, int flags,
@@ -964,6 +983,9 @@ extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
                                   int bufsize);
 extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t,
                              struct ext2_inode *f, int hostorder);
+extern void ext2fs_swap_extent_header(struct ext3_extent_header *eh);
+extern void ext2fs_swap_extent_index(struct ext3_extent_idx *ix);
+extern void ext2fs_swap_extent(struct ext3_extent *ex);
 
 /* valid_blk.c */
 extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
diff --git a/lib/ext2fs/extents.c b/lib/ext2fs/extents.c
new file mode 100644 (file)
index 0000000..ce6a2ea
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * extent.c --- iterate over all blocks in an extent-mapped inode
+ *
+ * Copyright (C) 2005 Alex Tomas <alex@clusterfs.com>
+ * Copyright (C) 2006 Andreas Dilger <adilger@clusterfs.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "block.h"
+
+#ifdef EXT_DEBUG
+void ext_show_header(struct ext3_extent_header *eh)
+{
+       printf("header: magic=%x entries=%u max=%u depth=%u generation=%u\n",
+              eh->eh_magic, eh->eh_entries, eh->eh_max, eh->eh_depth,
+              eh->eh_generation);
+}
+
+void ext_show_index(struct ext3_extent_idx *ix)
+{
+       printf("index: block=%u leaf=%u leaf_hi=%u unused=%u\n",
+              ix->ei_block, ix->ei_leaf, ix->ei_leaf_hi, ix->ei_unused);
+}
+
+void ext_show_extent(struct ext3_extent *ex)
+{
+       printf("extent: block=%u-%u len=%u start=%u start_hi=%u\n",
+              ex->ee_block, ex->ee_block + ex->ee_len - 1,
+              ex->ee_len, ex->ee_start, ex->ee_start_hi);
+}
+
+#define ext_printf(fmt, args...) printf(fmt, ## args)
+#else
+#define ext_show_header(eh) do { } while (0)
+#define ext_show_index(ix) do { } while (0)
+#define ext_show_extent(ex) do { } while (0)
+#define ext_printf(fmt, args...) do { } while (0)
+#endif
+
+errcode_t ext2fs_extent_header_verify(struct ext3_extent_header *eh, int size)
+{
+       int eh_max, entry_size;
+
+       ext_show_header(eh);
+       if (eh->eh_magic != EXT3_EXT_MAGIC)
+               return EXT2_ET_EXTENT_HEADER_BAD;
+       if (eh->eh_entries > eh->eh_max)
+               return EXT2_ET_EXTENT_HEADER_BAD;
+       if (eh->eh_depth == 0)
+               entry_size = sizeof(struct ext3_extent);
+       else
+               entry_size = sizeof(struct ext3_extent_idx);
+
+       eh_max = (size - sizeof(*eh)) / entry_size;
+       /* Allow two extent-sized items at the end of the block, for
+        * ext4_extent_tail with checksum in the future. */
+       if (eh->eh_max > eh_max || eh->eh_max < eh_max - 2)
+               return EXT2_ET_EXTENT_HEADER_BAD;
+
+       return 0;
+}
+
+/* Verify that a single extent @ex is valid.  If @ex_prev is passed in,
+ * then this was the previous logical extent in this block and we can
+ * do additional sanity checking (though in case of error we don't know
+ * which of the two extents is bad).  Similarly, if @ix is passed in
+ * we can check that this extent is logically part of the index that
+ * refers to it (though again we can't know which of the two is bad). */
+errcode_t ext2fs_extent_verify(ext2_filsys fs, struct ext3_extent *ex,
+                              struct ext3_extent *ex_prev,
+                              struct ext3_extent_idx *ix, int ix_len)
+{
+       ext_show_extent(ex);
+       /* FIXME: 48-bit support */
+       if (ex->ee_start > fs->super->s_blocks_count)
+               return EXT2_ET_EXTENT_LEAF_BAD;
+
+       if (ex->ee_len == 0)
+               return EXT2_ET_EXTENT_LEAF_BAD;
+
+       if (ex->ee_len >= fs->super->s_blocks_per_group)
+               return EXT2_ET_EXTENT_LEAF_BAD;
+
+       if (ex_prev) {
+               /* We can't have a zero logical block except for first index */
+               if (ex->ee_block == 0)
+                       return EXT2_ET_EXTENT_LEAF_BAD;
+
+               /* FIXME: 48-bit support */
+               /* extents must be in logical offset order */
+               if (ex->ee_block < ex_prev->ee_block + ex_prev->ee_len)
+                       return EXT2_ET_EXTENT_LEAF_BAD;
+
+               /* extents must not overlap physical blocks */
+               if ((ex->ee_start < ex_prev->ee_start + ex_prev->ee_len) &&
+                   (ex->ee_start + ex->ee_len > ex_prev->ee_start))
+                       return EXT2_ET_EXTENT_LEAF_BAD;
+       }
+
+       if (ix) {
+               /* FIXME: 48-bit support */
+               if (ex->ee_block < ix->ei_block)
+                       return EXT2_ET_EXTENT_LEAF_BAD;
+
+               if (ix_len && ex->ee_block + ex->ee_len > ix->ei_block + ix_len)
+                       return EXT2_ET_EXTENT_LEAF_BAD;
+       }
+
+       return 0;
+}
+
+errcode_t ext2fs_extent_index_verify(ext2_filsys fs, struct ext3_extent_idx *ix,
+                                    struct ext3_extent_idx *ix_prev)
+{
+       ext_show_index(ix);
+       /* FIXME: 48-bit support */
+       if (ix->ei_leaf > fs->super->s_blocks_count)
+               return EXT2_ET_EXTENT_INDEX_BAD;
+
+       if (ix_prev == NULL)
+               return 0;
+
+       /* We can't have a zero logical block except for first index */
+       if (ix->ei_block == 0)
+               return EXT2_ET_EXTENT_INDEX_BAD;
+
+       if (ix->ei_block <= ix_prev->ei_block)
+               return EXT2_ET_EXTENT_INDEX_BAD;
+
+       return 0;
+}
+
+errcode_t ext2fs_extent_remove(struct ext3_extent_header *eh,
+                              struct ext3_extent *ex)
+{
+       int offs = ex - EXT_FIRST_EXTENT(eh);
+
+       if (offs < 0 || offs > eh->eh_entries)
+               return EXT2_ET_EXTENT_LEAF_BAD;
+
+       ext_printf("remove extent: offset %u\n", offs);
+
+       memmove(ex, ex + 1, (eh->eh_entries - offs - 1) * sizeof(*ex));
+       --eh->eh_entries;
+
+       return 0;
+}
+
+static errcode_t ext2fs_extent_split_internal(struct ext3_extent_header *eh,
+                                             struct ext3_extent *ex, int offs)
+{
+       int entry = ex - EXT_FIRST_EXTENT(eh);
+       struct ext3_extent *ex_new = ex + 1;
+
+       ext_printf("split: ee_len: %u ee_block: %u ee_start: %u offset: %u\n",
+                  ex->ee_len, ex->ee_block, ex->ee_start, offs);
+       memmove(ex_new, ex, (eh->eh_entries - entry) * sizeof(*ex));
+       ++eh->eh_entries;
+
+       ex->ee_len = offs;
+       /* FIXME: 48-bit support */
+       ex_new->ee_len -= offs;
+       ex_new->ee_block += offs;
+       ex_new->ee_start += offs;
+
+       return 0;
+}
+
+errcode_t ext2fs_extent_split(ext2_filsys fs,
+                             struct ext3_extent_header **eh_orig,
+                             struct ext3_extent **ex_orig, int offs, int *flag)
+{
+       struct ext3_extent_header *eh_parent = *eh_orig;
+       int retval, entry = *ex_orig - EXT_FIRST_EXTENT(eh_parent);
+       blk_t new_block;
+       char *buf;
+       struct ext3_extent_idx *ei = EXT_FIRST_INDEX(eh_parent);
+
+       if (entry < 0 || entry > (*eh_orig)->eh_entries)
+               return EXT2_ET_EXTENT_LEAF_BAD;
+
+       if (offs > (*ex_orig)->ee_len)
+               return EXT2_ET_EXTENT_LEAF_BAD;
+
+       if (eh_parent->eh_entries >= eh_parent->eh_max) {
+               ext_printf("split: eh_entries: %u eh_max: %u\n",
+                          eh_parent->eh_entries, eh_parent->eh_max);
+               if (eh_parent->eh_max == 4) {
+                       struct ext3_extent_header *eh_child;
+                       struct ext3_extent *ex_child;
+
+                       retval = ext2fs_get_mem(fs->blocksize, &buf);
+
+                       if (retval)
+                               return EXT2_ET_EXTENT_NO_SPACE;
+
+                       memset(buf, 0, fs->blocksize);
+                       memcpy(buf, eh_parent, sizeof(*eh_parent) +
+                              eh_parent->eh_entries * sizeof(*ex_child));
+                       eh_child = (struct ext3_extent_header *)buf;
+
+                       eh_child->eh_max = (fs->blocksize -
+                                           sizeof(struct ext3_extent_header)) /
+                                          sizeof(struct ext3_extent);
+                       retval = ext2fs_new_block(fs, (*ex_orig)->ee_block, 0,
+                                                 &new_block);
+                       if (retval)
+                               return EXT2_ET_EXTENT_NO_SPACE;
+
+                       retval = io_channel_write_blk(fs->io, new_block, 1,buf);
+                       if (retval)
+                               return EXT2_ET_EXTENT_NO_SPACE;
+
+                       eh_parent->eh_entries = 1;
+                       eh_parent->eh_depth = 1;
+
+                       ex_child = EXT_FIRST_EXTENT(eh_child);
+                       ei->ei_block = ex_child->ee_block;
+                       /* FIXME: 48-bit support*/
+                       ei->ei_leaf = new_block;
+
+                       *eh_orig = eh_child;
+                       *ex_orig = EXT_FIRST_EXTENT(eh_child) + entry;
+
+                       *flag = BLOCK_CHANGED;
+               } else {
+                       return EXT2_ET_EXTENT_NO_SPACE;
+               }
+       }
+
+       return ext2fs_extent_split_internal(*eh_orig, *ex_orig, offs);
+}
+
+errcode_t ext2fs_extent_index_remove(struct ext3_extent_header *eh,
+                                    struct ext3_extent_idx *ix)
+{
+       struct ext3_extent_idx *first = EXT_FIRST_INDEX(eh);
+       int offs = ix - first;
+
+       ext_printf("remove index: offset %u\n", offs);
+
+       memmove(ix, ix + 1, (eh->eh_entries - offs - 1) * sizeof(*ix));
+       --eh->eh_entries;
+
+       return 0;
+}
+
+/* Internal function for ext2fs_block_iterate2() to recursively walk the
+ * extent tree, with a callback function for each block.  We also call the
+ * callback function on index blocks unless BLOCK_FLAG_DATA_ONLY is given.
+ * We traverse the tree in-order (internal nodes before their children)
+ * unless BLOCK_FLAG_DEPTH_FIRST is given.
+ *
+ * See also block_bmap_extents(). */
+int block_iterate_extents(void *eh_buf, unsigned bufsize, blk_t ref_block,
+                         int ref_offset EXT2FS_ATTR((unused)),
+                         struct block_context *ctx)
+{
+       struct ext3_extent_header *orig_eh, *eh;
+       struct ext3_extent *ex, *ex_prev = NULL;
+       int ret = 0;
+       int item, offs, flags, split_flag = 0;
+       blk_t block_address;
+
+       orig_eh = eh = eh_buf;
+
+       if (ext2fs_extent_header_verify(eh, bufsize))
+               return BLOCK_ERROR;
+
+       if (eh->eh_depth == 0) {
+               ex = EXT_FIRST_EXTENT(eh);
+               for (item = 0; item < eh->eh_entries; item++, ex++) {
+                       ext_show_extent(ex);
+                       for (offs = 0; offs < ex->ee_len; offs++) {
+                               block_address = ex->ee_start + offs;
+                               flags = (*ctx->func)(ctx->fs, &block_address,
+                                                    (ex->ee_block + offs),
+                                                    ref_block, item,
+                                                    ctx->priv_data);
+                               if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+                                       ret |= flags &(BLOCK_ABORT|BLOCK_ERROR);
+                                       return ret;
+                               }
+                               if (!(flags & BLOCK_CHANGED))
+                                       continue;
+
+                               ext_printf("extent leaf changed: "
+                                          "block was %u+%u = %u, now %u\n",
+                                          ex->ee_start, offs,
+                                          ex->ee_start + offs, block_address);
+
+                               /* FIXME: 48-bit support */
+                               if (ex_prev &&
+                                   block_address ==
+                                   ex_prev->ee_start + ex_prev->ee_len &&
+                                   ex->ee_block + offs ==
+                                   ex_prev->ee_block + ex_prev->ee_len) {
+                                       /* can merge block with prev extent */
+                                       ex_prev->ee_len++;
+                                       ex->ee_len--;
+                                       ret |= BLOCK_CHANGED;
+
+                                       if (ex->ee_len == 0) {
+                                               /* no blocks left in this one */
+                                               ext2fs_extent_remove(eh, ex);
+                                               item--; ex--;
+                                               break;
+                                       } else {
+                                               /* FIXME: 48-bit support */
+                                               ex->ee_start++;
+                                               ex->ee_block++;
+                                               offs--;
+                                       }
+
+                               } else if (offs > 0 && /* implies ee_len > 1 */
+                                          (ctx->errcode =
+                                           ext2fs_extent_split(ctx->fs, &eh,
+                                                               &ex, offs,
+                                                               &split_flag)
+                                           /* advance ex past newly split item,
+                                            * comparison is bogus to make sure
+                                            * increment doesn't change logic */
+                                           || (offs > 0 && ex++ == NULL))) {
+                                       /* split before new block failed */
+                                       ret |= BLOCK_ABORT | BLOCK_ERROR;
+                                       return ret;
+
+                               } else if (ex->ee_len > 1 &&
+                                          (ctx->errcode =
+                                           ext2fs_extent_split(ctx->fs, &eh,
+                                                               &ex, 1,
+                                                               &split_flag))) {
+                                       /* split after new block failed */
+                                       ret |= BLOCK_ABORT | BLOCK_ERROR;
+                                       return ret;
+
+                               } else {
+                                       if (ex->ee_len != 1) {
+                                               /* this is an internal error */
+                                               ctx->errcode =
+                                                      EXT2_ET_EXTENT_INDEX_BAD;
+                                               ret |= BLOCK_ABORT |BLOCK_ERROR;
+                                               return ret;
+                                       }
+                                       /* FIXME: 48-bit support */
+                                       ex->ee_start = block_address;
+                                       ret |= BLOCK_CHANGED;
+                               }
+                       }
+                       ex_prev = ex;
+               }
+               /* Multi level split at depth == 0.
+                * ex has been changed to point to  newly allocated block
+                * buffer. And after returning  in this scenario, only inode is
+                * updated with changed i_block. Hence explicitly write to the
+                * block is required. */
+               if (split_flag == BLOCK_CHANGED) {
+                       struct ext3_extent_idx *ix = EXT_FIRST_INDEX(orig_eh);
+                       ctx->errcode = ext2fs_write_ext_block(ctx->fs,
+                                                             ix->ei_leaf, eh);
+               }
+       } else {
+               char *block_buf;
+               struct ext3_extent_idx *ix;
+
+               ret = ext2fs_get_mem(ctx->fs->blocksize, &block_buf);
+               if (ret)
+                       return ret;
+
+               ext_show_header(eh);
+               ix = EXT_FIRST_INDEX(eh);
+               for (item = 0; item < eh->eh_entries; item++, ix++) {
+                       ext_show_index(ix);
+                       /* index is processed first in e2fsck case */
+                       if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+                           !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) {
+                               block_address = ix->ei_leaf;
+                               flags = (*ctx->func)(ctx->fs, &block_address,
+                                                    BLOCK_COUNT_IND, ref_block,
+                                                    item, ctx->priv_data);
+                               if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+                                       ret |= flags &(BLOCK_ABORT|BLOCK_ERROR);
+                                       goto free_buf;
+                               }
+                               if (flags & BLOCK_CHANGED) {
+                                       ret |= BLOCK_CHANGED;
+                                       /* index has no more block, remove it */
+                                       /* FIXME: 48-bit support */
+                                       ix->ei_leaf = block_address;
+                                       if (ix->ei_leaf == 0 &&
+                                           ix->ei_leaf_hi == 0) {
+                                               if(ext2fs_extent_index_remove(eh, ix)) {
+                                                       ret |= BLOCK_ABORT |BLOCK_ERROR;
+                                                       goto free_buf;
+                                               } else {
+                                                       --item; --ix;
+                                                       continue;
+                                               }
+                                       }
+                                       /* remapped? */
+                               }
+                       }
+                       ctx->errcode = ext2fs_read_ext_block(ctx->fs,
+                                                            ix->ei_leaf,
+                                                            block_buf);
+                       if (ctx->errcode) {
+                               ret |= BLOCK_ERROR;
+                               goto free_buf;
+                       }
+                       flags = block_iterate_extents(block_buf,
+                                                     ctx->fs->blocksize,
+                                                     ix->ei_leaf, item, ctx);
+                       if (flags & BLOCK_CHANGED) {
+                               struct ext3_extent_header *nh;
+                               ctx->errcode =
+                                       ext2fs_write_ext_block(ctx->fs,
+                                                              ix->ei_leaf,
+                                                              block_buf);
+
+                               nh = (struct ext3_extent_header *)block_buf;
+                               if (nh->eh_entries == 0)
+                                       ix->ei_leaf = ix->ei_leaf_hi = 0;
+                       }
+                       if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+                               ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+                               goto free_buf;
+                       }
+                       if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+                           !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) {
+                               flags = (*ctx->func)(ctx->fs, &block_address,
+                                                    BLOCK_COUNT_IND, ref_block,
+                                                    item, ctx->priv_data);
+                               if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+                                       ret |= flags &(BLOCK_ABORT|BLOCK_ERROR);
+                                       goto free_buf;
+                               }
+                               if (flags & BLOCK_CHANGED)
+                                       /* FIXME: 48-bit support */
+                                       ix->ei_leaf = block_address;
+                       }
+
+                       if (flags & BLOCK_CHANGED) {
+                               /* index has no more block, remove it */
+                               if (ix->ei_leaf == 0 && ix->ei_leaf_hi == 0 &&
+                                   ext2fs_extent_index_remove(eh, ix)) {
+                                       ret |= BLOCK_ABORT |BLOCK_ERROR;
+                                       goto free_buf;
+                               }
+
+                               ret |= BLOCK_CHANGED;
+                               if (ref_block == 0) {
+                                       --item; --ix;
+                                       continue;
+                               }
+                               /* remapped? */
+                       }
+               }
+
+       free_buf:
+               ext2fs_free_mem(&block_buf);
+       }
+       return ret;
+}
index 3519048311221f59c7946ac26fb88dc21f1521eb..85748525718866ffc326edb15515e93319e3022c 100644 (file)
@@ -22,9 +22,9 @@
 errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf)
 {
        errcode_t       retval;
-       blk_t           *block_nr;
-       int             i;
-       int             limit = fs->blocksize >> 2;
+       int     limit = fs->blocksize >> 2;
+       blk_t   *block_nr = (blk_t *)buf;
+       int     i;
 
        if ((fs->flags & EXT2_FLAG_IMAGE_FILE) &&
            (fs->io != fs->image_io))
@@ -36,7 +36,6 @@ errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf)
        }
 #ifdef EXT2FS_ENABLE_SWAPFS
        if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_READ)) {
-               block_nr = (blk_t *) buf;
                for (i = 0; i < limit; i++, block_nr++)
                        *block_nr = ext2fs_swab32(*block_nr);
        }
@@ -64,3 +63,82 @@ errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf)
 }
 
 
+errcode_t ext2fs_read_ext_block(ext2_filsys fs, blk_t blk, void *buf)
+{
+       errcode_t       retval;
+
+       if ((fs->flags & EXT2_FLAG_IMAGE_FILE) &&
+           (fs->io != fs->image_io))
+               memset(buf, 0, fs->blocksize);
+       else {
+               retval = io_channel_read_blk(fs->io, blk, 1, buf);
+               if (retval)
+                       return retval;
+       }
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_READ)) {
+               struct ext3_extent_header *eh = buf;
+               int i, limit;
+
+               ext2fs_swap_extent_header(eh);
+
+               if (eh->eh_depth == 0) {
+                       struct ext3_extent *ex = EXT_FIRST_EXTENT(eh);
+
+                       limit = (fs->blocksize - sizeof(*eh)) / sizeof(*ex);
+                       if (eh->eh_entries < limit)
+                               limit = eh->eh_entries;
+
+                       for (i = 0; i < limit; i++, ex++)
+                               ext2fs_swap_extent(ex);
+               } else {
+                       struct ext3_extent_idx *ix = EXT_FIRST_INDEX(eh);
+
+                       limit = (fs->blocksize - sizeof(*eh)) / sizeof(*ix);
+                       if (eh->eh_entries < limit)
+                               limit = eh->eh_entries;
+
+                       for (i = 0; i < limit; i++, ix++)
+                               ext2fs_swap_extent_index(ix);
+               }
+       }
+#endif
+       return 0;
+}
+
+errcode_t ext2fs_write_ext_block(ext2_filsys fs, blk_t blk, void *buf)
+{
+       if (fs->flags & EXT2_FLAG_IMAGE_FILE)
+               return 0;
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_WRITE)) {
+               struct ext3_extent_header *eh = buf;
+               int i, limit;
+
+               if (eh->eh_depth == 0) {
+                       struct ext3_extent *ex = EXT_FIRST_EXTENT(eh);
+
+                       limit = (fs->blocksize - sizeof(*eh)) / sizeof(*ex);
+                       if (eh->eh_entries < limit)
+                               limit = eh->eh_entries;
+
+                       for (i = 0; i < limit; i++, ex++)
+                               ext2fs_swap_extent(ex);
+               } else {
+                       struct ext3_extent_idx *ix = EXT_FIRST_INDEX(eh);
+
+                       limit = (fs->blocksize - sizeof(*eh)) / sizeof(*ix);
+                       if (eh->eh_entries < limit)
+                               limit = eh->eh_entries;
+
+                       for (i = 0; i < limit; i++, ix++)
+                               ext2fs_swap_extent_index(ix);
+               }
+
+               ext2fs_swap_extent_header(eh);
+       }
+#endif
+       return io_channel_write_blk(fs->io, blk, 1, buf);
+}
+
index ee863f1e9329e9b76e9a4e42aa678ac39370451d..b38784140fb2974045885cbc500655ae6c9db5e0 100644 (file)
@@ -142,11 +142,33 @@ void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header)
        }
 }
 
+void ext2fs_swap_extent_header(struct ext3_extent_header *eh) {
+       eh->eh_magic = ext2fs_swab16(eh->eh_magic);
+       eh->eh_entries = ext2fs_swab16(eh->eh_entries);
+       eh->eh_max = ext2fs_swab16(eh->eh_max);
+       eh->eh_depth = ext2fs_swab16(eh->eh_depth);
+       eh->eh_generation = ext2fs_swab32(eh->eh_generation);
+}
+
+void ext2fs_swap_extent_index(struct ext3_extent_idx *ix) {
+       ix->ei_block = ext2fs_swab32(ix->ei_block);
+       ix->ei_leaf = ext2fs_swab32(ix->ei_leaf);
+       ix->ei_leaf_hi = ext2fs_swab16(ix->ei_leaf_hi);
+       ix->ei_unused = ext2fs_swab16(ix->ei_unused);
+}
+
+void ext2fs_swap_extent(struct ext3_extent *ex) {
+       ex->ee_block = ext2fs_swab32(ex->ee_block);
+       ex->ee_len = ext2fs_swab16(ex->ee_len);
+       ex->ee_start_hi =ext2fs_swab16(ex->ee_start_hi);
+       ex->ee_start = ext2fs_swab32(ex->ee_start);
+}
+
 void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
                            struct ext2_inode_large *f, int hostorder,
                            int bufsize)
 {
-       unsigned i, has_data_blocks, extra_isize;
+       unsigned i, has_data_blocks, extra_isize, has_extents;
        int islnk = 0;
        __u32 *eaf, *eat;
 
@@ -164,18 +186,46 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
        t->i_gid = ext2fs_swab16(f->i_gid);
        t->i_links_count = ext2fs_swab16(f->i_links_count);
        t->i_file_acl = ext2fs_swab32(f->i_file_acl);
-       if (hostorder)
-               has_data_blocks = ext2fs_inode_data_blocks(fs, 
+       if (hostorder) {
+               has_data_blocks = ext2fs_inode_data_blocks(fs,
                                           (struct ext2_inode *) f);
-       t->i_blocks = ext2fs_swab32(f->i_blocks);
-       if (!hostorder)
-               has_data_blocks = ext2fs_inode_data_blocks(fs, 
+               t->i_blocks = ext2fs_swab32(f->i_blocks);
+               has_extents = (f->i_flags & EXT4_EXTENTS_FL);
+               t->i_flags = ext2fs_swab32(f->i_flags);
+       } else {
+               t->i_blocks = ext2fs_swab32(f->i_blocks);
+               has_data_blocks = ext2fs_inode_data_blocks(fs,
                                           (struct ext2_inode *) t);
+               t->i_flags = ext2fs_swab32(f->i_flags);
+               has_extents = (t->i_flags & EXT4_EXTENTS_FL);
+       }
        t->i_flags = ext2fs_swab32(f->i_flags);
        t->i_dir_acl = ext2fs_swab32(f->i_dir_acl);
-       if (!islnk || has_data_blocks ) {
-               for (i = 0; i < EXT2_N_BLOCKS; i++)
-                       t->i_block[i] = ext2fs_swab32(f->i_block[i]);
+       if (!islnk || has_data_blocks) {
+               if (has_extents) {
+                       struct ext3_extent_header *eh;
+                       int max = EXT2_N_BLOCKS * sizeof(__u32) - sizeof(*eh);
+
+                       memcpy(t->i_block, f->i_block, sizeof(f->i_block));
+                       eh = (struct ext3_extent_header *)t->i_block;
+                       ext2fs_swap_extent_header(eh);
+
+                       if (!eh->eh_depth) {
+                               struct ext3_extent *ex = EXT_FIRST_EXTENT(eh);
+                               max = max / sizeof(struct ext3_extent);
+                               for (i = 0; i < max; i++, ex++)
+                                       ext2fs_swap_extent(ex);
+                       } else {
+                               struct ext3_extent_idx *ix =
+                                       EXT_FIRST_INDEX(eh);
+                               max = max / sizeof(struct ext3_extent_idx);
+                               for (i = 0; i < max; i++, ix++)
+                                       ext2fs_swap_extent_index(ix);
+                       }
+               } else {
+                       for (i = 0; i < EXT2_N_BLOCKS; i++)
+                               t->i_block[i] = ext2fs_swab32(f->i_block[i]);
+               }
        } else if (t != f) {
                for (i = 0; i < EXT2_N_BLOCKS; i++)
                        t->i_block[i] = f->i_block[i];
@@ -227,11 +277,13 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
        if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16)))
                return; /* no i_extra_isize field */
 
-       if (hostorder)
+       if (hostorder) {
                extra_isize = f->i_extra_isize;
-       t->i_extra_isize = ext2fs_swab16(f->i_extra_isize);
-       if (!hostorder)
+               t->i_extra_isize = ext2fs_swab16(f->i_extra_isize);
+       } else {
+               t->i_extra_isize = ext2fs_swab16(f->i_extra_isize);
                extra_isize = t->i_extra_isize;
+       }
        if (extra_isize > EXT2_INODE_SIZE(fs->super) -
                                sizeof(struct ext2_inode)) {
                /* this is error case: i_extra_size is too large */
index 29ff27a7cf67c35c17373a3a0edcb7064fc2a171..caca170bdf26f2b2039d818224cb13304f805a30 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
+#include "ext3_extents.h"
 
 /*
  * This function returns 1 if the inode's block entries actually
@@ -41,12 +42,23 @@ int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode)
        if (LINUX_S_ISLNK (inode->i_mode)) {
                if (inode->i_file_acl == 0) {
                        /* With no EA block, we can rely on i_blocks */
-                       if (inode->i_blocks == 0)
-                               return 0;
+                       if (inode->i_flags & EXT4_EXTENTS_FL) {
+                               struct ext3_extent_header *eh;
+                               eh = (struct ext3_extent_header *)inode->i_block;
+                               if (eh->eh_entries == 0)
+                                       return 0;
+                       } else {
+                               if (inode->i_blocks == 0)
+                                       return 0;
+                       }
                } else {
                        /* With an EA block, life gets more tricky */
                        if (inode->i_size >= EXT2_N_BLOCKS*4)
                                return 1; /* definitely using i_block[] */
+                       /*
+                        * we cant have EA + extents, so assume we aren't
+                        * using extents
+                        */
                        if (inode->i_size > 4 && inode->i_block[1] == 0)
                                return 1; /* definitely using i_block[] */
                        return 0; /* Probably a fast symlink */
index b4851f057cad65cabea3c0b66c59b3cc62196a29..94e90fa4ce645b4a7c0b3e1d0c749adeb0a822d2 100644 (file)
@@ -1,4 +1,10 @@
 Pass 1: Checking inodes, blocks, and sizes
+Inode 15 has EXTENT_FL set, but is not in extents format
+Fix? yes
+
+Inode 16 has EXTENT_FL set, but is not in extents format
+Fix? yes
+
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 /lost+found not found.  Create? yes
index 1d639f6cc05c9164f19c807d144bd08338803ce9..09b9a3a51fdda073fcd49c363247ff381c35c51d 100644 (file)
@@ -3,46 +3,60 @@ Filesystem did not have a UUID; generating one.
 Pass 1: Checking inodes, blocks, and sizes
 Group 0's inode bitmap (4) is bad.  Relocate? yes
 
+Inode 11 has corrupt indirect block
+Clear? yes
+
 Relocating group 0's inode bitmap from 4 to 43...
+Restarting e2fsck from the beginning...
+Pass 1: Checking inodes, blocks, and sizes
 
 Running additional passes to resolve blocks claimed by more than one inode...
 Pass 1B: Rescanning for multiply-claimed blocks
 Multiply-claimed block(s) in inode 2: 21
-Multiply-claimed block(s) in inode 11: 9 10 11 12 13 14 15 16 17 18 19 20
 Multiply-claimed block(s) in inode 12: 25 26
 Pass 1C: Scanning directories for inodes with multiply-claimed blocks
 Pass 1D: Reconciling multiply-claimed blocks
-(There are 3 inodes containing multiply-claimed blocks.)
+(There are 2 inodes containing multiply-claimed blocks.)
 
 File / (inode #2, mod time Sun Jan  2 08:29:13 1994) 
   has 1 multiply-claimed block(s), shared with 1 file(s):
        <The bad blocks inode> (inode #1, mod time Sun Jul 17 00:47:58 1994)
 Clone multiply-claimed blocks? yes
 
-File /lost+found (inode #11, mod time Sun Jan  2 08:28:40 1994) 
-  has 12 multiply-claimed block(s), shared with 1 file(s):
-       <The bad blocks inode> (inode #1, mod time Sun Jul 17 00:47:58 1994)
-Clone multiply-claimed blocks? yes
-
 File /termcap (inode #12, mod time Sun Jan  2 08:29:13 1994) 
   has 2 multiply-claimed block(s), shared with 1 file(s):
        <The bad blocks inode> (inode #1, mod time Sun Jul 17 00:47:58 1994)
 Clone multiply-claimed blocks? yes
 
 Pass 2: Checking directory structure
+Entry 'lost+found' in / (2) has deleted/unused inode 11.  Clear? yes
+
 Pass 3: Checking directory connectivity
+/lost+found not found.  Create? yes
+
 Pass 4: Checking reference counts
+Inode 2 ref count is 4, should be 3.  Fix? yes
+
 Pass 5: Checking group summary information
 Block bitmap differences:  +43
 Fix? yes
 
-Free blocks count wrong for group #0 (57, counted=41).
+Free blocks count wrong for group #0 (56, counted=52).
+Fix? yes
+
+Free blocks count wrong (56, counted=52).
+Fix? yes
+
+Free inodes count wrong for group #0 (19, counted=20).
+Fix? yes
+
+Directories count wrong for group #0 (3, counted=2).
 Fix? yes
 
-Free blocks count wrong (57, counted=41).
+Free inodes count wrong (19, counted=20).
 Fix? yes
 
 
 test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
-test_filesys: 12/32 files (0.0% non-contiguous), 59/100 blocks
+test_filesys: 12/32 files (0.0% non-contiguous), 48/100 blocks
 Exit status is 1
index 92491da814be9cf764930e4de916a5eb43e7ef97..61f7007d93fea9b5ac462c7ad1a6d21e10ffb8e6 100644 (file)
@@ -3,5 +3,5 @@ Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-test_filesys: 12/32 files (8.3% non-contiguous), 59/100 blocks
+test_filesys: 12/32 files (8.3% non-contiguous), 48/100 blocks
 Exit status is 0
index d9e8a245008191d68cb910473d0bb968b2cc720c..6462760a6e56befac9cb36be9586991fbfbf2803 100644 (file)
@@ -8,54 +8,41 @@ Inode 13, i_size is 15360, should be 12288.  Fix? yes
 
 Inode 13, i_blocks is 32, should be 30.  Fix? yes
 
-Inode 12 has illegal block(s).  Clear? yes
-
-Illegal block #12 (778398818) in inode 12.  CLEARED.
-Illegal block #13 (1768444960) in inode 12.  CLEARED.
-Illegal block #14 (1752375411) in inode 12.  CLEARED.
-Illegal block #15 (1684829551) in inode 12.  CLEARED.
-Illegal block #16 (1886349344) in inode 12.  CLEARED.
-Illegal block #17 (1819633253) in inode 12.  CLEARED.
-Illegal block #18 (1663072620) in inode 12.  CLEARED.
-Illegal block #19 (1735287144) in inode 12.  CLEARED.
-Illegal block #20 (1310731877) in inode 12.  CLEARED.
-Illegal block #21 (560297071) in inode 12.  CLEARED.
-Illegal block #22 (543512352) in inode 12.  CLEARED.
-Too many illegal blocks in inode 12.
-Clear inode? yes
-
-Restarting e2fsck from the beginning...
-Pass 1: Checking inodes, blocks, and sizes
+Inode 12 has corrupt indirect block
+Clear? yes
+
+Inode 12, i_blocks is 34, should be 24.  Fix? yes
+
 Pass 2: Checking directory structure
-Entry 'termcap' in / (2) has deleted/unused inode 12.  Clear? yes
+Directory inode 13 has an unallocated block #16580876.  Allocate? yes
 
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Inode 2 ref count is 5, should be 4.  Fix? yes
 
 Pass 5: Checking group summary information
-Block bitmap differences:  -(27--41) -(44--45) -(74--90)
+Block bitmap differences:  -(38--41) -(74--90)
 Fix? yes
 
-Free blocks count wrong for group #0 (9, counted=43).
+Free blocks count wrong for group #0 (9, counted=30).
 Fix? yes
 
-Free blocks count wrong (9, counted=43).
+Free blocks count wrong (9, counted=30).
 Fix? yes
 
-Inode bitmap differences:  -12 -14
+Inode bitmap differences:  -14
 Fix? yes
 
-Free inodes count wrong for group #0 (18, counted=20).
+Free inodes count wrong for group #0 (18, counted=19).
 Fix? yes
 
 Directories count wrong for group #0 (4, counted=3).
 Fix? yes
 
-Free inodes count wrong (18, counted=20).
+Free inodes count wrong (18, counted=19).
 Fix? yes
 
 
 test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
-test_filesys: 12/32 files (0.0% non-contiguous), 57/100 blocks
+test_filesys: 13/32 files (7.7% non-contiguous), 70/100 blocks
 Exit status is 1
index da1208f308ba3b2e421279917dc1776bc514952f..da3642a707dbc063f37bdb66999276cec014bf44 100644 (file)
@@ -1,7 +1,18 @@
 Pass 1: Checking inodes, blocks, and sizes
+Inode 13 is too big.  Truncate? yes
+
+Block #16580876 (37) causes directory to be too big.  CLEARED.
+Inode 13, i_size is 4093916160, should be 12288.  Fix? yes
+
+Inode 13, i_blocks is 32, should be 30.  Fix? yes
+
 Pass 2: Checking directory structure
+Directory inode 13 has an unallocated block #16580876.  Allocate? yes
+
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-test_filesys: 12/32 files (0.0% non-contiguous), 57/100 blocks
-Exit status is 0
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 13/32 files (15.4% non-contiguous), 70/100 blocks
+Exit status is 1
index 708f1da77197aa68bf32a733300d75dfce94175d..94891c8cc9911cf46c6853544ff6332bfb88e7c8 100644 (file)
@@ -1,38 +1,36 @@
 Filesystem did not have a UUID; generating one.
 
 Pass 1: Checking inodes, blocks, and sizes
-Inode 14 has illegal block(s).  Clear? yes
-
-Illegal block #2 (4294901760) in inode 14.  CLEARED.
-Illegal block #3 (4294901760) in inode 14.  CLEARED.
-Illegal block #4 (4294901760) in inode 14.  CLEARED.
-Illegal block #5 (4294901760) in inode 14.  CLEARED.
-Illegal block #6 (4294901760) in inode 14.  CLEARED.
-Illegal block #7 (4294901760) in inode 14.  CLEARED.
-Illegal block #8 (4294901760) in inode 14.  CLEARED.
-Illegal block #9 (4294901760) in inode 14.  CLEARED.
-Illegal block #10 (4294901760) in inode 14.  CLEARED.
-Inode 14, i_size is 18446462598732849291, should be 2048.  Fix? yes
-
-Inode 14, i_blocks is 18, should be 4.  Fix? yes
+Inode 14 has corrupt indirect block
+Clear? yes
 
+Restarting e2fsck from the beginning...
+Pass 1: Checking inodes, blocks, and sizes
 Pass 2: Checking directory structure
-i_file_acl for inode 14 (/MAKEDEV) is 4294901760, should be zero.
-Clear? yes
+Entry 'MAKEDEV' in / (2) has deleted/unused inode 14.  Clear? yes
 
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-Block bitmap differences:  -(43--49)
+Block bitmap differences:  -(41--49)
+Fix? yes
+
+Free blocks count wrong for group #0 (68, counted=77).
+Fix? yes
+
+Free blocks count wrong (68, counted=77).
+Fix? yes
+
+Inode bitmap differences:  -14
 Fix? yes
 
-Free blocks count wrong for group #0 (68, counted=75).
+Free inodes count wrong for group #0 (3, counted=4).
 Fix? yes
 
-Free blocks count wrong (68, counted=75).
+Free inodes count wrong (3, counted=4).
 Fix? yes
 
 
 test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
-test_filesys: 29/32 files (3.4% non-contiguous), 25/100 blocks
+test_filesys: 28/32 files (0.0% non-contiguous), 23/100 blocks
 Exit status is 1
index 1fffb022483108a97fbfa68fde0d0e6482b0ae54..fb4e83ac1b98ba2af80faa925f762fe99061fd3f 100644 (file)
@@ -3,5 +3,5 @@ Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-test_filesys: 29/32 files (0.0% non-contiguous), 25/100 blocks
+test_filesys: 28/32 files (0.0% non-contiguous), 23/100 blocks
 Exit status is 0