]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
Merge branch 'maint' into next
authorTheodore Ts'o <tytso@mit.edu>
Mon, 16 Oct 2017 03:20:53 +0000 (23:20 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 16 Oct 2017 03:20:53 +0000 (23:20 -0400)
21 files changed:
1  2 
configure.ac
debugfs/Makefile.in
debugfs/htree.c
debugfs/set_fields.c
e2fsck/message.c
e2fsck/pass1.c
e2fsck/pass2.c
e2fsck/pass5.c
e2fsck/problem.c
e2fsck/problem.h
ext2ed/doc/ext2ed-design.sgml
ext2ed/doc/ext2fs-overview.sgml
ext2ed/ext2.descriptors
lib/ext2fs/Makefile.in
lib/ext2fs/blkmap64_rb.c
lib/ext2fs/ext2_fs.h
lib/ext2fs/ext2fs.h
lib/ext2fs/inline_data.c
misc/Makefile.in
misc/create_inode.c
resize/resize2fs.c

diff --combined configure.ac
index 1314c3855eaeeb39325f05a34d764ad62ec7b7f1,dd1668766fe938e611e07e8d0b2e622c8583da00..4ec4617054efe4cfc51bb63bdcf2e6509e82b85c
@@@ -124,8 -124,8 +124,8 @@@ els
  fi
  AC_USE_SYSTEM_EXTENSIONS
  dnl
- dnl Set default values for library extentions.  Will be dealt with after
- dnl parsing configuration opions, which may modify these
+ dnl Set default values for library extensions.  Will be dealt with after
+ dnl parsing configuration options, which may modify these
  dnl
  LIB_EXT=.a
  STATIC_LIB_EXT=.a
@@@ -918,10 -918,8 +918,10 @@@ AC_CHECK_HEADERS(m4_flatten(
        attr/xattr.h
        linux/falloc.h
        linux/fd.h
 +      linux/fsmap.h
        linux/major.h
        linux/loop.h
 +      linux/types.h
        net/if_dl.h
        netinet/in.h
        sys/acl.h
@@@ -977,7 -975,7 +977,7 @@@ AC_CHECK_HEADERS(net/if.h,,
  ]])
  AC_FUNC_VPRINTF
  dnl Check to see if dirent has member d_reclen. On cygwin those d_reclen
- dnl is not decleared.
+ dnl is not declared.
  AC_CHECK_MEMBER(struct dirent.d_reclen,[AC_DEFINE(HAVE_RECLEN_DIRENT, 1,
                       [Define to 1 if dirent has d_reclen])],,
                [#include <dirent.h>])
@@@ -1115,10 -1113,8 +1115,10 @@@ AC_CHECK_FUNCS(m4_flatten(
        fallocate
        fallocate64
        fchown
 +      fcntl
        fdatasync
        fstat64
 +      fsync
        ftruncate64
        futimes
        getcwd
diff --combined debugfs/Makefile.in
index 408a3236694d18201e56492cb4eb0d8b35a5538a,aef4a5a865e8a20d520522fe391cbe6eff52d0cf..57940cdeabe32a61c4b88bf22ad38d47a45deb8a
@@@ -46,7 -46,7 +46,7 @@@ STATIC_DEPLIBS= $(STATIC_LIBEXT2FS) $(D
                $(DEPSTATIC_LIBCOM_ERR) $(DEPSTATIC_LIBUUID) \
                $(DEPSTATIC_LIBE2P)
  
- # This nastyness is needed because of jfs_user.h hackery; when we finally
+ # This nastiness is needed because of jfs_user.h hackery; when we finally
  # clean up this mess, we should be able to drop it
  LOCAL_CFLAGS = -I$(srcdir)/../e2fsck -DDEBUGFS
  DEPEND_CFLAGS = -I$(srcdir)
@@@ -164,7 -164,7 +164,7 @@@ tst_set_fields: set_fields.c util.
        $(Q) $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(SYSLIBS) -DUNITTEST \
                -o tst_set_fields $(srcdir)/set_fields.c $(srcdir)/util.c $(LIBS)
  
 -check:: tst_set_fields
 +fullcheck check:: tst_set_fields
        $(TESTENV) ./tst_set_fields
  
  # +++ Dependency line eater +++
diff --combined debugfs/htree.c
index 8c18666d626f4705fd1a1f0575c81fdefbf90437,ede1224eef729aa65662ceedc6ab99b498c346d5..cf7d78aa67e3d89e38080f5a5889067ee9fc2ce1
@@@ -287,8 -287,7 +287,8 @@@ void do_htree_dump(int argc, char *argv
        fprintf(pager, "\t Indirect levels: %d\n", rootnode->indirect_levels);
        fprintf(pager, "\t Flags: %d\n", rootnode->unused_flags);
  
 -      ent = (struct ext2_dx_entry *) (buf + 24 + rootnode->info_length);
 +      ent = (struct ext2_dx_entry *)
 +              ((char *)rootnode + rootnode->info_length);
  
        htree_dump_int_node(current_fs, ino, &inode, rootnode, ent,
                            buf + current_fs->blocksize,
@@@ -340,7 -339,7 +340,7 @@@ void do_dx_hash(int argc, char *argv[]
        err = ext2fs_dirhash(hash_version, argv[optind], strlen(argv[optind]),
                             hash_seed, &hash, &minor_hash);
        if (err) {
-               com_err(argv[0], err, "while caclulating hash");
+               com_err(argv[0], err, "while calculating hash");
                return;
        }
        printf("Hash of %s is 0x%0x (minor 0x%0x)\n", argv[optind],
diff --combined debugfs/set_fields.c
index ca688622b09eb169d00c4f6456637a2ce68a5f4a,af7a0ab3b99fd8183df16cd1c370b517d8818b86..8dfbba9c4fa40a6c2eaa51d40e9aefd7ddd020fa
@@@ -212,6 -212,7 +212,6 @@@ static struct field_set_info inode_fiel
        /* Special case: i_file_acl_high is 2 bytes */
        { "file_acl", &set_inode.i_file_acl, 
                &set_inode.osd2.linux2.l_i_file_acl_high, 6, parse_uint },
 -      { "dir_acl", &set_inode.i_dir_acl, NULL, 4, parse_uint, FLAG_ALIAS },
        { "faddr", &set_inode.i_faddr, NULL, 4, parse_uint },
        { "frag", &set_inode.osd2.hurd2.h_i_frag, NULL, 1, parse_uint, FLAG_ALIAS },
        { "fsize", &set_inode.osd2.hurd2.h_i_fsize, NULL, 1, parse_uint },
@@@ -429,7 -430,7 +429,7 @@@ static struct field_set_info *find_fiel
  
  /*
   * Note: info->size == 6 is special; this means a base size 4 bytes,
-  * and secondiory (high) size of 2 bytes.  This is needed for the
+  * and secondary (high) size of 2 bytes.  This is needed for the
   * special case of i_blocks_high and i_file_acl_high.
   */
  static errcode_t parse_uint(struct field_set_info *info, char *field,
diff --combined e2fsck/message.c
index 0d47a3a40012a7cd78eb555da0bdd8df8f09c93d,30ce620aa123aecffcc0d73f7febfeaeeb31a5e6..727f71d5f7ad27365d5a725f7000e849f385ef58
@@@ -32,7 -32,7 +32,7 @@@
   *    %IM     <inode> -> i_mtime
   *    %IF     <inode> -> i_faddr
   *    %If     <inode> -> i_file_acl
 - *    %Id     <inode> -> i_dir_acl
 + *    %Id     <inode> -> i_size_high
   *    %Iu     <inode> -> i_uid
   *    %Ig     <inode> -> i_gid
   *    %It     <inode type>
@@@ -224,7 -224,7 +224,7 @@@ static void print_time(FILE *f, time_t 
        static int              do_gmt = -1;
  
  #ifdef __dietlibc__
-               /* The diet libc doesn't respect the TZ environemnt variable */
+               /* The diet libc doesn't respect the TZ environment variable */
                if (do_gmt == -1) {
                        time_str = getenv("TZ");
                        if (!time_str)
@@@ -283,8 -283,17 +283,8 @@@ static _INLINE_ void expand_inode_expre
        case 's':
                if (LINUX_S_ISDIR(inode->i_mode))
                        fprintf(f, "%u", inode->i_size);
 -              else {
 -#ifdef EXT2_NO_64_TYPE
 -                      if (inode->i_size_high)
 -                              fprintf(f, "0x%x%08x", inode->i_size_high,
 -                                      inode->i_size);
 -                      else
 -                              fprintf(f, "%u", inode->i_size);
 -#else
 +              else
                        fprintf(f, "%llu", EXT2_I_SIZE(inode));
 -#endif
 -              }
                break;
        case 'S':
                fprintf(f, "%u", large_inode->i_extra_isize);
                break;
        case 'd':
                fprintf(f, "%u", (LINUX_S_ISDIR(inode->i_mode) ?
 -                                inode->i_dir_acl : 0));
 +                      inode->i_size_high : 0));
                break;
        case 'u':
                fprintf(f, "%d", inode_uid(*inode));
@@@ -405,7 -414,11 +405,7 @@@ static _INLINE_ void expand_percent_exp
                fputc('%', f);
                break;
        case 'b':
 -#ifdef EXT2_NO_64_TYPE
 -              fprintf(f, "%*u", width, (unsigned long) ctx->blk);
 -#else
                fprintf(f, "%*llu", width, (unsigned long long) ctx->blk);
 -#endif
                break;
        case 'B':
                if (ctx->blkcount == BLOCK_COUNT_IND)
                if (*first && islower(m[0]))
                        fputc(toupper(*m++), f);
                fputs(m, f);
 -              if (ctx->blkcount >= 0) {
 -#ifdef EXT2_NO_64_TYPE
 -                      fprintf(f, "%d", ctx->blkcount);
 -#else
 +              if (ctx->blkcount >= 0)
                        fprintf(f, "%lld", (long long) ctx->blkcount);
 -#endif
 -              }
                break;
        case 'c':
 -#ifdef EXT2_NO_64_TYPE
 -              fprintf(f, "%*u", width, (unsigned long) ctx->blk2);
 -#else
                fprintf(f, "%*llu", width, (unsigned long long) ctx->blk2);
 -#endif
                break;
        case 'd':
                fprintf(f, "%*u", width, ctx->dir);
                fprintf(f, "%*s", width, error_message(ctx->errcode));
                break;
        case 'N':
 -#ifdef EXT2_NO_64_TYPE
 -              fprintf(f, "%*u", width, ctx->num);
 -#else
                fprintf(f, "%*llu", width, (long long)ctx->num);
 -#endif
 +              break;
 +      case 'n':
 +              fprintf(f, "%*llu", width, (long long)ctx->num2);
                break;
        case 'p':
                print_pathname(f, fs, ctx->ino, 0);
                print_pathname(f, fs, ctx->dir, ctx->ino);
                break;
        case 'r':
 -#ifdef EXT2_NO_64_TYPE
 -              fprintf(f, "%*d", width, ctx->blkcount);
 -#else
                fprintf(f, "%*lld", width, (long long) ctx->blkcount);
 -#endif
                break;
        case 'S':
                fprintf(f, "%llu", get_backup_sb(NULL, fs, NULL, NULL));
                fprintf(f, "0x%0*x", width, ctx->csum1);
                break;
        case 'X':
 -#ifdef EXT2_NO_64_TYPE
 -              fprintf(f, "0x%0*x", width, ctx->num);
 -#else
                fprintf(f, "0x%0*llx", width, (long long)ctx->num);
 -#endif
                break;
        case 'y':
                fprintf(f, "0x%0*x", width, ctx->csum2);
diff --combined e2fsck/pass1.c
index f7593ff0af73a13aeb962452961a2341298cabc1,7a82536dde57feb7b59e9b6e4c23d6d4279c9125..5015d9382c8b9a460f476aea709c999bd0a1445d
   *    - A bitmap of which blocks are in use.          (block_found_map)
   *    - A bitmap of which blocks are in use by two inodes     (block_dup_map)
   *    - The data blocks of the directory inodes.      (dir_map)
 + *    - Ref counts for ea_inodes.                     (ea_inode_refs)
   *
   * Pass 1 is designed to stash away enough information so that the
   * other passes should not need to read in the inode information
-  * during the normal course of a filesystem check.  (Althogh if an
+  * during the normal course of a filesystem check.  (Although if an
   * inconsistency is detected, other passes may need to read in an
   * inode to fix it.)
   *
  
  #undef DEBUG
  
 +struct ea_quota {
 +      blk64_t blocks;
 +      __u64 inodes;
 +};
 +
  static int process_block(ext2_filsys fs, blk64_t      *blocknr,
                         e2_blkcnt_t blockcnt, blk64_t ref_blk,
                         int ref_offset, void *priv_data);
@@@ -71,8 -65,7 +71,8 @@@ static int process_bad_block(ext2_filsy
                             e2_blkcnt_t blockcnt, blk64_t ref_blk,
                             int ref_offset, void *priv_data);
  static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 -                       char *block_buf);
 +                       char *block_buf,
 +                       const struct ea_quota *ea_ibody_quota);
  static void mark_table_blocks(e2fsck_t ctx);
  static void alloc_bb_map(e2fsck_t ctx);
  static void alloc_imagic_map(e2fsck_t ctx);
@@@ -109,7 -102,6 +109,7 @@@ struct process_block_struct 
  
  struct process_inode_block {
        ext2_ino_t ino;
 +      struct ea_quota ea_ibody_quota;
        struct ext2_inode_large inode;
  };
  
@@@ -185,6 -177,7 +185,6 @@@ int e2fsck_pass1_check_symlink(ext2_fil
  {
        unsigned int len;
        int i;
 -      blk64_t blocks;
        ext2_extent_handle_t    handle;
        struct ext2_extent_info info;
        struct ext2fs_extent    extent;
                return 1;
        }
  
 -      blocks = ext2fs_inode_data_blocks2(fs, inode);
 -      if (blocks) {
 -              if (inode->i_flags & EXT4_INLINE_DATA_FL)
 +      if (ext2fs_is_fast_symlink(inode)) {
 +              if (inode->i_size >= sizeof(inode->i_block))
 +                      return 0;
 +
 +              len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
 +              if (len == sizeof(inode->i_block))
                        return 0;
 +      } else {
                if ((inode->i_size >= fs->blocksize) ||
 -                  (blocks != fs->blocksize >> 9) ||
                    (inode->i_block[0] < fs->super->s_first_data_block) ||
                    (inode->i_block[0] >= ext2fs_blocks_count(fs->super)))
                        return 0;
                }
                if (len == fs->blocksize)
                        return 0;
 -      } else if (inode->i_flags & EXT4_INLINE_DATA_FL) {
 -              char *inline_buf = NULL;
 -              size_t inline_sz = 0;
 -
 -              if (ext2fs_inline_data_size(fs, ino, &inline_sz))
 -                      return 0;
 -              if (inode->i_size != inline_sz)
 -                      return 0;
 -              if (ext2fs_get_mem(inline_sz + 1, &inline_buf))
 -                      return 0;
 -              i = 0;
 -              if (ext2fs_inline_data_get(fs, ino, inode, inline_buf, NULL))
 -                      goto exit_inline;
 -              inline_buf[inline_sz] = 0;
 -              len = strnlen(inline_buf, inline_sz);
 -              if (len != inline_sz)
 -                      goto exit_inline;
 -              i = 1;
 -exit_inline:
 -              ext2fs_free_mem(&inline_buf);
 -              return i;
 -      } else {
 -              if (inode->i_size >= sizeof(inode->i_block))
 -                      return 0;
 -
 -              len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
 -              if (len == sizeof(inode->i_block))
 -                      return 0;
        }
        if (len != inode->i_size)
                if ((inode->i_flags & EXT4_ENCRYPT_FL) == 0)
@@@ -315,127 -333,21 +315,127 @@@ static void check_size(e2fsck_t ctx, st
        e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
  }
  
 -static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
 +/*
 + * For a given size, calculate how many blocks would be charged towards quota.
 + */
 +static blk64_t size_to_quota_blocks(ext2_filsys fs, size_t size)
 +{
 +      blk64_t clusters;
 +
 +      clusters = DIV_ROUND_UP(size, fs->blocksize << fs->cluster_ratio_bits);
 +      return EXT2FS_C2B(fs, clusters);
 +}
 +
 +/*
 + * Check validity of EA inode. Return 0 if EA inode is valid, otherwise return
 + * the problem code.
 + */
 +static problem_t check_large_ea_inode(e2fsck_t ctx,
 +                                    struct ext2_ext_attr_entry *entry,
 +                                    struct problem_context *pctx,
 +                                    blk64_t *quota_blocks)
 +{
 +      struct ext2_inode inode;
 +      __u32 hash;
 +      errcode_t retval;
 +
 +      /* Check if inode is within valid range */
 +      if ((entry->e_value_inum < EXT2_FIRST_INODE(ctx->fs->super)) ||
 +          (entry->e_value_inum > ctx->fs->super->s_inodes_count)) {
 +              pctx->num = entry->e_value_inum;
 +              return PR_1_ATTR_VALUE_EA_INODE;
 +      }
 +
 +      e2fsck_read_inode(ctx, entry->e_value_inum, &inode, "pass1");
 +
 +      retval = ext2fs_ext_attr_hash_entry2(ctx->fs, entry, NULL, &hash);
 +      if (retval) {
 +              com_err("check_large_ea_inode", retval,
 +                      _("while hashing entry with e_value_inum = %u"),
 +                      entry->e_value_inum);
 +              fatal_error(ctx, 0);
 +      }
 +
 +      if (hash == entry->e_hash) {
 +              *quota_blocks = size_to_quota_blocks(ctx->fs,
 +                                                   entry->e_value_size);
 +      } else {
 +              /* This might be an old Lustre-style ea_inode reference. */
 +              if (inode.i_mtime == pctx->ino &&
 +                  inode.i_generation == pctx->inode->i_generation) {
 +                      *quota_blocks = 0;
 +              } else {
 +                      /* If target inode is also missing EA_INODE flag,
 +                       * this is likely to be a bad reference.
 +                       */
 +                      if (!(inode.i_flags & EXT4_EA_INODE_FL)) {
 +                              pctx->num = entry->e_value_inum;
 +                              return PR_1_ATTR_VALUE_EA_INODE;
 +                      } else {
 +                              pctx->num = entry->e_hash;
 +                              return PR_1_ATTR_HASH;
 +                      }
 +              }
 +      }
 +
 +      if (!(inode.i_flags & EXT4_EA_INODE_FL)) {
 +              pctx->num = entry->e_value_inum;
 +              if (fix_problem(ctx, PR_1_ATTR_SET_EA_INODE_FL, pctx)) {
 +                      inode.i_flags |= EXT4_EA_INODE_FL;
 +                      ext2fs_write_inode(ctx->fs, entry->e_value_inum,
 +                                         &inode);
 +              } else {
 +                      return PR_1_ATTR_NO_EA_INODE_FL;
 +              }
 +      }
 +      return 0;
 +}
 +
 +static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx,
 +                            struct ext2_ext_attr_entry *first, void *end)
 +{
 +      struct ext2_ext_attr_entry *entry;
 +
 +      for (entry = first;
 +           (void *)entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry);
 +           entry = EXT2_EXT_ATTR_NEXT(entry)) {
 +              if (!entry->e_value_inum)
 +                      continue;
 +              if (!ctx->ea_inode_refs) {
 +                      pctx->errcode = ea_refcount_create(0,
 +                                                         &ctx->ea_inode_refs);
 +                      if (pctx->errcode) {
 +                              pctx->num = 4;
 +                              fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
 +                              ctx->flags |= E2F_FLAG_ABORT;
 +                              return;
 +                      }
 +              }
 +              ea_refcount_increment(ctx->ea_inode_refs, entry->e_value_inum,
 +                                    0);
 +      }
 +}
 +
 +static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
 +                            struct ea_quota *ea_ibody_quota)
  {
        struct ext2_super_block *sb = ctx->fs->super;
        struct ext2_inode_large *inode;
        struct ext2_ext_attr_entry *entry;
 -      char *start, *header;
 +      char *start, *header, *end;
        unsigned int storage_size, remain;
        problem_t problem = 0;
        region_t region = 0;
  
 +      ea_ibody_quota->blocks = 0;
 +      ea_ibody_quota->inodes = 0;
 +
        inode = (struct ext2_inode_large *) pctx->inode;
        storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
                inode->i_extra_isize;
        header = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
                 inode->i_extra_isize;
 +      end = header + storage_size;
        start = header + sizeof(__u32);
        entry = (struct ext2_ext_attr_entry *) start;
  
                /* attribute len eats this space */
                remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
  
 -              /* check value size */
 -              if (entry->e_value_size > remain) {
 -                      pctx->num = entry->e_value_size;
 -                      problem = PR_1_ATTR_VALUE_SIZE;
 -                      goto fix;
 -              }
 +              if (entry->e_value_inum == 0) {
 +                      /* check value size */
 +                      if (entry->e_value_size > remain) {
 +                              pctx->num = entry->e_value_size;
 +                              problem = PR_1_ATTR_VALUE_SIZE;
 +                              goto fix;
 +                      }
  
 -              /* e_value_block must be 0 in inode's ea */
 -              if (entry->e_value_block != 0) {
 -                      pctx->num = entry->e_value_block;
 -                      problem = PR_1_ATTR_VALUE_BLOCK;
 -                      goto fix;
 -              }
 +                      if (entry->e_value_size &&
 +                          region_allocate(region,
 +                                          sizeof(__u32) + entry->e_value_offs,
 +                                          EXT2_EXT_ATTR_SIZE(
 +                                              entry->e_value_size))) {
 +                              problem = PR_1_INODE_EA_ALLOC_COLLISION;
 +                              goto fix;
 +                      }
  
 -              if (entry->e_value_size &&
 -                  region_allocate(region, sizeof(__u32) + entry->e_value_offs,
 -                                  EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
 -                      problem = PR_1_INODE_EA_ALLOC_COLLISION;
 -                      goto fix;
 -              }
 +                      hash = ext2fs_ext_attr_hash_entry(entry,
 +                                                        start + entry->e_value_offs);
 +
 +                      /* e_hash may be 0 in older inode's ea */
 +                      if (entry->e_hash != 0 && entry->e_hash != hash) {
 +                              pctx->num = entry->e_hash;
 +                              problem = PR_1_ATTR_HASH;
 +                              goto fix;
 +                      }
 +              } else {
 +                      blk64_t quota_blocks;
  
 -              hash = ext2fs_ext_attr_hash_entry(entry,
 -                                                start + entry->e_value_offs);
 +                      problem = check_large_ea_inode(ctx, entry, pctx,
 +                                                     &quota_blocks);
 +                      if (problem != 0)
 +                              goto fix;
  
 -              /* e_hash may be 0 in older inode's ea */
 -              if (entry->e_hash != 0 && entry->e_hash != hash) {
 -                      pctx->num = entry->e_hash;
 -                      problem = PR_1_ATTR_HASH;
 -                      goto fix;
 +                      ea_ibody_quota->blocks += quota_blocks;
 +                      ea_ibody_quota->inodes++;
                }
  
 -              remain -= entry->e_value_size;
 +              /* If EA value is stored in external inode then it does not
 +               * consume space here */
 +              if (entry->e_value_inum == 0)
 +                      remain -= entry->e_value_size;
  
                entry = EXT2_EXT_ATTR_NEXT(entry);
        }
@@@ -537,18 -439,13 +537,18 @@@ fix
         * it seems like a corruption. it's very unlikely we could repair
         * EA(s) in automatic fashion -bzzz
         */
 -      if (problem == 0 || !fix_problem(ctx, problem, pctx))
 +      if (problem == 0 || !fix_problem(ctx, problem, pctx)) {
 +              inc_ea_inode_refs(ctx, pctx,
 +                                (struct ext2_ext_attr_entry *)start, end);
                return;
 +      }
  
        /* simply remove all possible EA(s) */
        *((__u32 *)header) = 0UL;
        e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
                                EXT2_INODE_SIZE(sb), "pass1");
 +      ea_ibody_quota->blocks = 0;
 +      ea_ibody_quota->inodes = 0;
  }
  
  static int check_inode_extra_negative_epoch(__u32 xtime, __u32 extra) {
   */
  #define EXT4_EXTRA_NEGATIVE_DATE_CUTOFF 2 * (1LL << 32)
  
 -static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
 +static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx,
 +                                  struct ea_quota *ea_ibody_quota)
  {
        struct ext2_super_block *sb = ctx->fs->super;
        struct ext2_inode_large *inode;
        __u32 *eamagic;
        int min, max;
  
 +      ea_ibody_quota->blocks = 0;
 +      ea_ibody_quota->inodes = 0;
 +
        inode = (struct ext2_inode_large *) pctx->inode;
        if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
                /* this isn't large inode. so, nothing to check */
                        inode->i_extra_isize = (inode->i_extra_isize + 3) & ~3;
                e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
                                        EXT2_INODE_SIZE(sb), "pass1");
 -              return;
        }
  
        /* check if there is no place for an EA header */
                        inode->i_extra_isize);
        if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
                /* it seems inode has an extended attribute(s) in body */
 -              check_ea_in_inode(ctx, pctx);
 +              check_ea_in_inode(ctx, pctx, ea_ibody_quota);
        }
  
        /*
@@@ -1176,7 -1070,6 +1176,7 @@@ void e2fsck_pass1(e2fsck_t ctx
        int             failed_csum = 0;
        ext2_ino_t      ino_threshold = 0;
        dgrp_t          ra_group = 0;
 +      struct ea_quota ea_ibody_quota;
  
        init_resource_track(&rtrack, ctx->fs->io);
        clear_problem_context(&pctx);
                        /*
                         * Make sure the root inode is a directory; if
                         * not, offer to clear it.  It will be
-                        * regnerated in pass #3.
+                        * regenerated in pass #3.
                         */
                        if (!LINUX_S_ISDIR(inode->i_mode)) {
                                if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx))
                                                           "pass1");
                                        failed_csum = 0;
                                }
 -                              check_blocks(ctx, &pctx, block_buf);
 +                              check_blocks(ctx, &pctx, block_buf, NULL);
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
                                continue;
                        }
                                                        "pass1");
                                        failed_csum = 0;
                                }
 -                              check_blocks(ctx, &pctx, block_buf);
 +                              check_blocks(ctx, &pctx, block_buf, NULL);
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
                                continue;
                        }
                                        failed_csum = 0;
                                }
                        }
 -                      check_blocks(ctx, &pctx, block_buf);
 +                      check_blocks(ctx, &pctx, block_buf, NULL);
                        FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
                        continue;
                }
                }
  
                if (inode->i_faddr || frag || fsize ||
 -                  (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
 +                  (!ext2fs_has_feature_largedir(fs->super) &&
 +                  (LINUX_S_ISDIR(inode->i_mode) && inode->i_size_high)))
                        mark_inode_bad(ctx, ino);
                if ((fs->super->s_creator_os != EXT2_OS_HURD) &&
                    !ext2fs_has_feature_64bit(fs->super) &&
                        }
                }
  
 -              check_inode_extra_space(ctx, &pctx);
 +              check_inode_extra_space(ctx, &pctx, &ea_ibody_quota);
                check_is_really_dir(ctx, &pctx, block_buf);
  
                /*
                        if (inode->i_flags & EXT4_INLINE_DATA_FL) {
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
                                continue;
 -                      } else if (ext2fs_inode_data_blocks(fs, inode) == 0) {
 +                      } else if (ext2fs_is_fast_symlink(inode)) {
                                ctx->fs_fast_symlinks_count++;
 -                              check_blocks(ctx, &pctx, block_buf);
 +                              check_blocks(ctx, &pctx, block_buf,
 +                                           &ea_ibody_quota);
                                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
                                continue;
                        }
                     inode->i_block[EXT2_DIND_BLOCK] ||
                     inode->i_block[EXT2_TIND_BLOCK] ||
                     ext2fs_file_acl_block(fs, inode))) {
 -                      struct ext2_inode_large *ip;
 +                      struct process_inode_block *itp;
  
 -                      inodes_to_process[process_inode_count].ino = ino;
 -                      ip = &inodes_to_process[process_inode_count].inode;
 +                      itp = &inodes_to_process[process_inode_count];
 +                      itp->ino = ino;
 +                      itp->ea_ibody_quota = ea_ibody_quota;
                        if (inode_size < sizeof(struct ext2_inode_large))
 -                              memcpy(ip, inode, inode_size);
 +                              memcpy(&itp->inode, inode, inode_size);
                        else
 -                              memcpy(ip, inode, sizeof(*ip));
 +                              memcpy(&itp->inode, inode, sizeof(itp->inode));
                        process_inode_count++;
                } else
 -                      check_blocks(ctx, &pctx, block_buf);
 +                      check_blocks(ctx, &pctx, block_buf, &ea_ibody_quota);
  
                FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
  
                ctx->refcount_extra = 0;
        }
  
 +      if (ctx->ea_block_quota_blocks) {
 +              ea_refcount_free(ctx->ea_block_quota_blocks);
 +              ctx->ea_block_quota_blocks = 0;
 +      }
 +
 +      if (ctx->ea_block_quota_inodes) {
 +              ea_refcount_free(ctx->ea_block_quota_inodes);
 +              ctx->ea_block_quota_inodes = 0;
 +      }
 +
        if (ctx->invalid_bitmaps)
                handle_fs_bad_blocks(ctx);
  
@@@ -2125,8 -2005,7 +2125,8 @@@ static void process_inodes(e2fsck_t ctx
                sprintf(buf, _("reading indirect blocks of inode %u"),
                        pctx.ino);
                ehandler_operation(buf);
 -              check_blocks(ctx, &pctx, block_buf);
 +              check_blocks(ctx, &pctx, block_buf,
 +                           &inodes_to_process[i].ea_ibody_quota);
                if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
                        break;
        }
@@@ -2278,20 -2157,14 +2278,20 @@@ static _INLINE_ void mark_block_used(e2
        }
  }
  
 +/*
 + * When cluster size is greater than one block, it is caller's responsibility
 + * to make sure block parameter starts at a cluster boundary.
 + */
  static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block,
                                      unsigned int num)
  {
        if (ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num))
                ext2fs_mark_block_bitmap_range2(ctx->block_found_map, block, num);
 -      else
 -              while (num--)
 -                      mark_block_used(ctx, block++);
 +      else {
 +              int i;
 +              for (i = 0; i < num; i += EXT2FS_CLUSTER_RATIO(ctx->fs))
 +                      mark_block_used(ctx, block + i);
 +      }
  }
  
  /*
@@@ -2309,7 -2182,7 +2309,7 @@@ static void adjust_extattr_refcount(e2f
        ext2_filsys                     fs = ctx->fs;
        blk64_t                         blk;
        __u32                           should_be;
 -      int                             count;
 +      ea_value_t                      count;
  
        clear_problem_context(&pctx);
  
                }
                header = (struct ext2_ext_attr_header *) block_buf;
                pctx.blkcount = header->h_refcount;
 -              should_be = header->h_refcount + adjust_sign * count;
 +              should_be = header->h_refcount + adjust_sign * (int)count;
                pctx.num = should_be;
                if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
                        header->h_refcount = should_be;
   * Handle processing the extended attribute blocks
   */
  static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
 -                         char *block_buf)
 +                         char *block_buf, struct ea_quota *ea_block_quota)
  {
        ext2_filsys fs = ctx->fs;
        ext2_ino_t      ino = pctx->ino;
        blk64_t         blk;
        char *          end;
        struct ext2_ext_attr_header *header;
 -      struct ext2_ext_attr_entry *entry;
 -      int             count;
 +      struct ext2_ext_attr_entry *first, *entry;
 +      blk64_t         quota_blocks = EXT2FS_C2B(fs, 1);
 +      __u64           quota_inodes = 0;
        region_t        region = 0;
        int             failed_csum = 0;
  
 +      ea_block_quota->blocks = 0;
 +      ea_block_quota->inodes = 0;
 +
        blk = ext2fs_file_acl_block(fs, inode);
        if (blk == 0)
                return 0;
  
        /* Have we seen this EA block before? */
        if (ext2fs_fast_test_block_bitmap2(ctx->block_ea_map, blk)) {
 +              ea_block_quota->blocks = EXT2FS_C2B(fs, 1);
 +              ea_block_quota->inodes = 0;
 +
 +              if (ctx->ea_block_quota_blocks) {
 +                      ea_refcount_fetch(ctx->ea_block_quota_blocks, blk,
 +                                        &quota_blocks);
 +                      if (quota_blocks)
 +                              ea_block_quota->blocks = quota_blocks;
 +              }
 +
 +              if (ctx->ea_block_quota_inodes)
 +                      ea_refcount_fetch(ctx->ea_block_quota_inodes, blk,
 +                                        &ea_block_quota->inodes);
 +
                if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
                        return 1;
                /* Ooops, this EA was referenced more than it stated */
                        goto clear_extattr;
        }
  
 -      entry = (struct ext2_ext_attr_entry *)(header+1);
 +      first = (struct ext2_ext_attr_entry *)(header+1);
        end = block_buf + fs->blocksize;
 +      entry = first;
        while ((char *)entry < end && *(__u32 *)entry) {
                __u32 hash;
  
                                goto clear_extattr;
                        break;
                }
 -              if (entry->e_value_block != 0) {
 -                      if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
 -                              goto clear_extattr;
 -              }
 -              if (entry->e_value_offs + entry->e_value_size > fs->blocksize) {
 -                      if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
 -                              goto clear_extattr;
 -                      break;
 -              }
 -              if (entry->e_value_size &&
 -                  region_allocate(region, entry->e_value_offs,
 -                                  EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
 -                      if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
 -                              goto clear_extattr;
 -              }
 +              if (entry->e_value_inum == 0) {
 +                      if (entry->e_value_offs + entry->e_value_size >
 +                          fs->blocksize) {
 +                              if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
 +                                      goto clear_extattr;
 +                              break;
 +                      }
 +                      if (entry->e_value_size &&
 +                          region_allocate(region, entry->e_value_offs,
 +                                          EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
 +                              if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION,
 +                                              pctx))
 +                                      goto clear_extattr;
 +                      }
  
 -              hash = ext2fs_ext_attr_hash_entry(entry, block_buf +
 -                                                       entry->e_value_offs);
 +                      hash = ext2fs_ext_attr_hash_entry(entry, block_buf +
 +                                                        entry->e_value_offs);
  
 -              if (entry->e_hash != hash) {
 -                      pctx->num = entry->e_hash;
 -                      if (fix_problem(ctx, PR_1_ATTR_HASH, pctx))
 +                      if (entry->e_hash != hash) {
 +                              pctx->num = entry->e_hash;
 +                              if (fix_problem(ctx, PR_1_ATTR_HASH, pctx))
 +                                      goto clear_extattr;
 +                              entry->e_hash = hash;
 +                      }
 +              } else {
 +                      problem_t problem;
 +                      blk64_t entry_quota_blocks;
 +
 +                      problem = check_large_ea_inode(ctx, entry, pctx,
 +                                                     &entry_quota_blocks);
 +                      if (problem && fix_problem(ctx, problem, pctx))
                                goto clear_extattr;
 -                      entry->e_hash = hash;
 +
 +                      quota_blocks += entry_quota_blocks;
 +                      quota_inodes++;
                }
  
                entry = EXT2_EXT_ATTR_NEXT(entry);
                        return 0;
        }
  
 -      count = header->h_refcount - 1;
 -      if (count)
 -              ea_refcount_store(ctx->refcount, blk, count);
 +      if (quota_blocks != EXT2FS_C2B(fs, 1)) {
 +              if (!ctx->ea_block_quota_blocks) {
 +                      pctx->errcode = ea_refcount_create(0,
 +                                              &ctx->ea_block_quota_blocks);
 +                      if (pctx->errcode) {
 +                              pctx->num = 3;
 +                              goto refcount_fail;
 +                      }
 +              }
 +              ea_refcount_store(ctx->ea_block_quota_blocks, blk,
 +                                quota_blocks);
 +      }
 +
 +      if (quota_inodes) {
 +              if (!ctx->ea_block_quota_inodes) {
 +                      pctx->errcode = ea_refcount_create(0,
 +                                              &ctx->ea_block_quota_inodes);
 +                      if (pctx->errcode) {
 +                              pctx->num = 4;
 +refcount_fail:
 +                              fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
 +                              ctx->flags |= E2F_FLAG_ABORT;
 +                              return 0;
 +                      }
 +              }
 +
 +              ea_refcount_store(ctx->ea_block_quota_inodes, blk,
 +                                quota_inodes);
 +      }
 +      ea_block_quota->blocks = quota_blocks;
 +      ea_block_quota->inodes = quota_inodes;
 +
 +      inc_ea_inode_refs(ctx, pctx, first, end);
 +      ea_refcount_store(ctx->refcount, blk, header->h_refcount - 1);
        mark_block_used(ctx, blk);
        ext2fs_fast_mark_block_bitmap2(ctx->block_ea_map, blk);
        return 1;
@@@ -2664,7 -2476,7 +2664,7 @@@ static int handle_htree(e2fsck_t ctx, s
                return 1;
  
        pctx->num = root->indirect_levels;
 -      if ((root->indirect_levels > 1) &&
 +      if ((root->indirect_levels > ext2_dir_htree_level(fs)) &&
            fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
                return 1;
  
@@@ -2752,7 -2564,8 +2752,7 @@@ static void scan_extent_node(e2fsck_t c
  {
        struct ext2fs_extent    extent;
        blk64_t                 blk, last_lblk;
 -      e2_blkcnt_t             blockcnt;
 -      unsigned int            i;
 +      unsigned int            i, n;
        int                     is_dir, is_leaf;
        problem_t               problem;
        struct ext2_extent_info info;
@@@ -3028,29 -2841,50 +3028,29 @@@ report_problem
                        }
                }
  alloc_later:
 -              while (is_dir && (++pb->last_db_block <
 -                                (e2_blkcnt_t) extent.e_lblk)) {
 -                      pctx->errcode = ext2fs_add_dir_block2(ctx->fs->dblist,
 -                                                            pb->ino, 0,
 -                                                            pb->last_db_block);
 -                      if (pctx->errcode) {
 -                              pctx->blk = 0;
 -                              pctx->num = pb->last_db_block;
 -                              goto failed_add_dir_block;
 -                      }
 -              }
 -              if (!ctx->fs->cluster_ratio_bits) {
 -                      mark_blocks_used(ctx, extent.e_pblk, extent.e_len);
 -                      pb->num_blocks += extent.e_len;
 -              }
 -              for (blk = extent.e_pblk, blockcnt = extent.e_lblk, i = 0;
 -                   i < extent.e_len;
 -                   blk++, blockcnt++, i++) {
 -                      if (ctx->fs->cluster_ratio_bits &&
 -                          !(pb->previous_block &&
 -                            (EXT2FS_B2C(ctx->fs, blk) ==
 -                             EXT2FS_B2C(ctx->fs, pb->previous_block)) &&
 -                            (blk & EXT2FS_CLUSTER_MASK(ctx->fs)) ==
 -                            ((unsigned) blockcnt & EXT2FS_CLUSTER_MASK(ctx->fs)))) {
 -                              mark_block_used(ctx, blk);
 -                              pb->num_blocks++;
 -                      }
 -                      if (has_unaligned_cluster_map(ctx, pb->previous_block,
 -                                                    pb->last_block, blk,
 -                                                    blockcnt)) {
 -                              pctx->blk = blockcnt;
 -                              pctx->blk2 = blk;
 -                              fix_problem(ctx, PR_1_MISALIGNED_CLUSTER, pctx);
 -                              mark_block_used(ctx, blk);
 -                              mark_block_used(ctx, blk);
 +              if (is_dir) {
 +                      while (++pb->last_db_block <
 +                             (e2_blkcnt_t) extent.e_lblk) {
 +                              pctx->errcode = ext2fs_add_dir_block2(
 +                                                      ctx->fs->dblist,
 +                                                      pb->ino, 0,
 +                                                      pb->last_db_block);
 +                              if (pctx->errcode) {
 +                                      pctx->blk = 0;
 +                                      pctx->num = pb->last_db_block;
 +                                      goto failed_add_dir_block;
 +                              }
                        }
 -                      pb->last_block = blockcnt;
 -                      pb->previous_block = blk;
  
 -                      if (is_dir) {
 -                              pctx->errcode = ext2fs_add_dir_block2(ctx->fs->dblist, pctx->ino, blk, blockcnt);
 +                      for (i = 0; i < extent.e_len; i++) {
 +                              pctx->errcode = ext2fs_add_dir_block2(
 +                                                      ctx->fs->dblist,
 +                                                      pctx->ino,
 +                                                      extent.e_pblk + i,
 +                                                      extent.e_lblk + i);
                                if (pctx->errcode) {
 -                                      pctx->blk = blk;
 -                                      pctx->num = blockcnt;
 +                                      pctx->blk = extent.e_pblk + i;
 +                                      pctx->num = extent.e_lblk + i;
                                failed_add_dir_block:
                                        fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
                                        /* Should never get here */
                                        return;
                                }
                        }
 +                      if (extent.e_len > 0)
 +                              pb->last_db_block = extent.e_lblk + extent.e_len - 1;
 +              }
 +              if (has_unaligned_cluster_map(ctx, pb->previous_block,
 +                                            pb->last_block,
 +                                            extent.e_pblk,
 +                                            extent.e_lblk)) {
 +                      for (i = 0; i < extent.e_len; i++) {
 +                              pctx->blk = extent.e_lblk + i;
 +                              pctx->blk2 = extent.e_pblk + i;
 +                              fix_problem(ctx, PR_1_MISALIGNED_CLUSTER, pctx);
 +                              mark_block_used(ctx, extent.e_pblk + i);
 +                              mark_block_used(ctx, extent.e_pblk + i);
 +                      }
 +              }
 +
 +              /*
 +               * Check whether first cluster got marked in previous iteration.
 +               */
 +              if (ctx->fs->cluster_ratio_bits &&
 +                  pb->previous_block &&
 +                  (EXT2FS_B2C(ctx->fs, extent.e_pblk) ==
 +                   EXT2FS_B2C(ctx->fs, pb->previous_block)))
 +                      /* Set blk to the beginning of next cluster. */
 +                      blk = EXT2FS_C2B(
 +                              ctx->fs,
 +                              EXT2FS_B2C(ctx->fs, extent.e_pblk) + 1);
 +              else
 +                      /* Set blk to the beginning of current cluster. */
 +                      blk = EXT2FS_C2B(ctx->fs,
 +                                       EXT2FS_B2C(ctx->fs, extent.e_pblk));
 +
 +              if (blk < extent.e_pblk + extent.e_len) {
 +                      mark_blocks_used(ctx, blk,
 +                                       extent.e_pblk + extent.e_len - blk);
 +                      n = DIV_ROUND_UP(extent.e_pblk + extent.e_len - blk,
 +                                       EXT2FS_CLUSTER_RATIO(ctx->fs));
 +                      pb->num_blocks += n;
                }
 -              if (is_dir && extent.e_len > 0)
 -                      pb->last_db_block = blockcnt - 1;
 +              pb->last_block = extent.e_lblk + extent.e_len - 1;
                pb->previous_block = extent.e_pblk + extent.e_len - 1;
                start_block = pb->last_block = last_lblk;
                if (is_leaf && !is_dir &&
@@@ -3257,7 -3054,7 +3257,7 @@@ err
   * blocks used by that inode.
   */
  static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 -                       char *block_buf)
 +                       char *block_buf, const struct ea_quota *ea_ibody_quota)
  {
        ext2_filsys fs = ctx->fs;
        struct process_block_struct pb;
        int             extent_fs;
        int             inlinedata_fs;
        __u64           size;
 +      struct ea_quota ea_block_quota;
  
        pb.ino = ino;
 -      pb.num_blocks = 0;
 +      pb.num_blocks = EXT2FS_B2C(ctx->fs,
 +                                 ea_ibody_quota ? ea_ibody_quota->blocks : 0);
        pb.last_block = ~0;
        pb.last_init_lblock = -1;
        pb.last_db_block = -1;
        extent_fs = ext2fs_has_feature_extents(ctx->fs->super);
        inlinedata_fs = ext2fs_has_feature_inline_data(ctx->fs->super);
  
 -      if (check_ext_attr(ctx, pctx, block_buf)) {
 +      if (check_ext_attr(ctx, pctx, block_buf, &ea_block_quota)) {
                if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
                        goto out;
 -              pb.num_blocks++;
 +              pb.num_blocks += EXT2FS_B2C(ctx->fs, ea_block_quota.blocks);
        }
  
        if (inlinedata_fs && (inode->i_flags & EXT4_INLINE_DATA_FL))
        }
  
        if (ino != quota_type2inum(PRJQUOTA, fs->super) &&
 -          (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super))) {
 +          (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) &&
 +          !(inode->i_flags & EXT4_EA_INODE_FL)) {
                quota_data_add(ctx->qctx, (struct ext2_inode_large *) inode,
                               ino,
                               pb.num_blocks * EXT2_CLUSTER_SIZE(fs->super));
                quota_data_inodes(ctx->qctx, (struct ext2_inode_large *) inode,
 -                                ino, +1);
 +                                ino, (ea_ibody_quota ?
 +                                      ea_ibody_quota->inodes : 0) +
 +                                              ea_block_quota.inodes + 1);
        }
  
        if (!ext2fs_has_feature_huge_file(fs->super) ||
@@@ -4156,7 -3948,7 +4156,7 @@@ static void mark_table_blocks(e2fsck_t 
  }
  
  /*
-  * Thes subroutines short circuits ext2fs_get_blocks and
+  * These subroutines short circuits ext2fs_get_blocks and
   * ext2fs_check_directory; we use them since we already have the inode
   * structure, so there's no point in letting the ext2fs library read
   * the inode again.
diff --combined e2fsck/pass2.c
index 09b79c3bb42f04ab5ef629a0a3c88c68e71846e0,7190c9772220ebd3e90ad251ffcf7fe5367a40cc..09c16179d48f103442580021abdbc06e21121148
@@@ -85,39 -85,6 +85,39 @@@ struct check_dir_struct 
        unsigned long long next_ra_off;
  };
  
 +static void update_parents(struct dx_dir_info *dx_dir, int type)
 +{
 +      struct dx_dirblock_info *dx_db, *dx_parent, *dx_previous;
 +      int b;
 +
 +      for (b = 0, dx_db = dx_dir->dx_block;
 +           b < dx_dir->numblocks;
 +           b++, dx_db++) {
 +              dx_parent = &dx_dir->dx_block[dx_db->parent];
 +              if (dx_db->type != type)
 +                      continue;
 +
 +              /*
 +               * XXX Make sure dx_parent->min_hash > dx_db->min_hash
 +              */
 +              if (dx_db->flags & DX_FLAG_FIRST) {
 +                      dx_parent->min_hash = dx_db->min_hash;
 +                      if (dx_parent->previous) {
 +                              dx_previous =
 +                                      &dx_dir->dx_block[dx_parent->previous];
 +                              dx_previous->node_max_hash =
 +                                      dx_parent->min_hash;
 +                      }
 +              }
 +              /*
 +               * XXX Make sure dx_parent->max_hash < dx_db->max_hash
 +               */
 +              if (dx_db->flags & DX_FLAG_LAST) {
 +                      dx_parent->max_hash = dx_db->max_hash;
 +              }
 +      }
 +}
 +
  void e2fsck_pass2(e2fsck_t ctx)
  {
        struct ext2_super_block *sb = ctx->fs->super;
                 * Find all of the first and last leaf blocks, and
                 * update their parent's min and max hash values
                 */
 -              for (b=0, dx_db = dx_dir->dx_block;
 -                   b < dx_dir->numblocks;
 -                   b++, dx_db++) {
 -                      if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
 -                          !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
 -                              continue;
 -                      dx_parent = &dx_dir->dx_block[dx_db->parent];
 -                      /*
 -                       * XXX Make sure dx_parent->min_hash > dx_db->min_hash
 -                       */
 -                      if (dx_db->flags & DX_FLAG_FIRST)
 -                              dx_parent->min_hash = dx_db->min_hash;
 -                      /*
 -                       * XXX Make sure dx_parent->max_hash < dx_db->max_hash
 -                       */
 -                      if (dx_db->flags & DX_FLAG_LAST)
 -                              dx_parent->max_hash = dx_db->max_hash;
 -              }
 +              update_parents(dx_dir, DX_DIRBLOCK_LEAF);
 +
 +              /* for 3 level htree: update 2 level parent's min
 +               * and max hash values */
 +              update_parents(dx_dir, DX_DIRBLOCK_NODE);
  
                for (b=0, dx_db = dx_dir->dx_block;
                     b < dx_dir->numblocks;
@@@ -662,10 -642,6 +662,10 @@@ static void parse_int_node(ext2_filsys 
                        dx_db->flags |= DX_FLAG_REFERENCED;
                        dx_db->parent = db->blockcnt;
                }
 +
 +              dx_db->previous =
 +                      i ? ext2fs_le32_to_cpu(ent[i-1].block & 0x0ffffff) : 0;
 +
                if (hash < min_hash)
                        min_hash = hash;
                if (hash > max_hash)
@@@ -973,14 -949,6 +973,14 @@@ static int check_dir_block(ext2_filsys 
                        return DIRENT_ABORT;
        }
  
 +      /* This will allow (at some point in the future) to punch out empty
 +       * directory blocks and reduce the space used by a directory that grows
 +       * very large and then the files are deleted. For now, all that is
 +       * needed is to avoid e2fsck filling in these holes as part of
 +       * feature flag. */
 +      if (db->blk == 0 && ext2fs_has_feature_largedir(fs->super))
 +              return 0;
 +
        if (db->blk == 0 && !inline_data_size) {
                if (allocate_dir_block(ctx, db, buf, &cd->pctx))
                        return 0;
@@@ -1090,8 -1058,7 +1090,8 @@@ inline_read_fail
                        dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
                        if ((root->reserved_zero ||
                             root->info_length < 8 ||
 -                           root->indirect_levels > 1) &&
 +                           root->indirect_levels >=
 +                           ext2_dir_htree_level(fs)) &&
                            fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
                                clear_htree(ctx, ino);
                                dx_dir->numblocks = 0;
@@@ -1635,7 -1602,7 +1635,7 @@@ static int deallocate_inode_block(ext2_
  }
  
  /*
-  * This fuction deallocates an inode
+  * This function deallocates an inode
   */
  static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
  {
@@@ -1705,7 -1672,7 +1705,7 @@@ clear_inode
  }
  
  /*
-  * This fuction clears the htree flag on an inode
+  * This function clears the htree flag on an inode
   */
  static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
  {
@@@ -1844,10 -1811,10 +1844,10 @@@ int e2fsck_process_bad_inode(e2fsck_t c
                } else
                        not_fixed++;
        }
 -      if (inode.i_dir_acl &&
 +      if (inode.i_size_high && !ext2fs_has_feature_largedir(fs->super) &&
            LINUX_S_ISDIR(inode.i_mode)) {
 -              if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
 -                      inode.i_dir_acl = 0;
 +              if (fix_problem(ctx, PR_2_DIR_SIZE_HIGH_ZERO, &pctx)) {
 +                      inode.i_size_high = 0;
                        inode_modified++;
                } else
                        not_fixed++;
diff --combined e2fsck/pass5.c
index ab736496052196fd55970c7bbe1ba53c6bf5390d,d9e2858f714de81c20e14658b1b55ee25a15f136..7803e8b80178031e2e874f7209659b6395a8d79a
@@@ -21,6 -21,8 +21,6 @@@
  #include "e2fsck.h"
  #include "problem.h"
  
 -#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
 -
  static void check_block_bitmaps(e2fsck_t ctx);
  static void check_inode_bitmaps(e2fsck_t ctx);
  static void check_inode_end(e2fsck_t ctx);
@@@ -300,7 -302,7 +300,7 @@@ static void print_bitmap_problem(e2fsck
        pctx->ino = pctx->ino2 = 0;
  }
  
- /* Just to be more succint */
+ /* Just to be more succinct */
  #define B2C(x)        EXT2FS_B2C(fs, (x))
  #define EQ_CLSTR(x, y) (B2C(x) == B2C(y))
  #define LE_CLSTR(x, y) (B2C(x) <= B2C(y))
diff --combined e2fsck/problem.c
index 2bb8d4e208c469631e9ca8d65c8237ede4c1f120,7dbd0b2783a01a3f0107c44b98e3685a54535247..edc9d51fcf31e12f52de0a7636ce078a5623cec5
@@@ -174,7 -174,7 +174,7 @@@ static struct e2fsck_problem problem_ta
          N_("Corruption found in @S.  (%s = %N).\n"),
          PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
  
-       /* Error determing physical device size of filesystem */
+       /* Error determining physical device size of filesystem */
        { PR_0_GETSIZE_ERROR,
          N_("Error determining size of the physical @v: %m\n"),
          PROMPT_NONE, PR_FATAL },
          N_("Timestamp(s) on @i %i beyond 2310-04-04 are likely pre-1970.\n"),
          PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
  
 +      /* Inode has illegal extended attribute value inode */
 +      { PR_1_ATTR_VALUE_EA_INODE,
 +        N_("@i %i has @I @a value @i %N.\n"),
 +        PROMPT_CLEAR, PR_PREEN_OK },
 +
 +      /* Inode has invalid extended attribute. EA inode missing
 +       * EA_INODE flag. */
 +      { PR_1_ATTR_NO_EA_INODE_FL,
 +        N_("@i %i has @n @a. EA @i %N missing EA_INODE flag.\n"),
 +        PROMPT_CLEAR, PR_PREEN_OK },
 +
 +      /* EA inode for parent inode missing EA_INODE flag. */
 +      { PR_1_ATTR_SET_EA_INODE_FL,
 +        N_("EA @i %N for parent @i %i missing EA_INODE flag.\n "),
 +        PROMPT_FIX, PR_PREEN_OK },
 +
 +
        /* Pass 1b errors */
  
        /* Pass 1B: Rescan for duplicate/bad blocks */
          N_("@E has @D/unused @i %Di.  "),
          PROMPT_CLEAR, PR_PREEN_OK },
  
-       /* Directry entry is link to '.' */
+       /* Directory entry is link to '.' */
        { PR_2_LINK_DOT,
          N_("@E @L to '.'  "),
          PROMPT_CLEAR, 0 },
          N_("@E @L to @d %P (%Di).\n"),
          PROMPT_CLEAR, 0 },
  
-       /* Directory entry contains a link to the root directry */
+       /* Directory entry contains a link to the root directory */
        { PR_2_LINK_ROOT,
          N_("@E @L to the @r.\n"),
          PROMPT_CLEAR, 0 },
          N_("i_file_acl @F %If, @s zero.\n"),
          PROMPT_CLEAR, 0 },
  
 -      /* i_dir_acl should be zero */
 -      { PR_2_DIR_ACL_ZERO,
 -        N_("i_dir_acl @F %Id, @s zero.\n"),
 +      /* i_size_high should be zero */
 +      { PR_2_DIR_SIZE_HIGH_ZERO,
 +        N_("i_size_high @F %Id, @s zero.\n"),
          PROMPT_CLEAR, 0 },
  
        /* i_frag should be zero */
          "They @s the same!\n"),
          PROMPT_NONE, 0 },
  
 +      { PR_4_EA_INODE_REF_COUNT,
 +        N_("@a @i %i ref count is %N, @s %n. "),
 +        PROMPT_FIX, PR_PREEN_OK },
 +
        /* Pass 5 errors */
  
        /* Pass 5: Checking group summary information */
diff --combined e2fsck/problem.h
index 49a8c3ad61f603359f79f13f49cb9d4da699e9fa,7eb20b76ec3ab311c5d2c3e40cf8a80b2374dc72..482d111a0f2ceccf094e1541efe7d364bbed9f8e
@@@ -20,7 -20,7 +20,7 @@@ struct problem_context 
        e2_blkcnt_t     blkcount;
        dgrp_t          group;
        __u32           csum1, csum2;
 -      __u64   num;
 +      __u64           num, num2;
        const char *str;
  };
  
@@@ -91,7 -91,7 +91,7 @@@
  /* Miscellaneous superblock corruption */
  #define PR_0_MISC_CORRUPT_SUPER                       0x00000B
  
- /* Error determing physical device size of filesystem */
+ /* Error determining physical device size of filesystem */
  #define PR_0_GETSIZE_ERROR                    0x00000C
  
  /* Inode count in the superblock incorrect */
  /* Timestamp(s) on inode beyond 2310-04-04 are likely pre-1970. */
  #define PR_1_EA_TIME_OUT_OF_RANGE             0x010082
  
 +/* Inode has illegal EA value inode */
 +#define PR_1_ATTR_VALUE_EA_INODE              0x010083
 +
 +/* Parent inode has invalid EA entry. EA inode does not have
 + * EXT4_EA_INODE_FL flag. Delete EA entry? */
 +#define PR_1_ATTR_NO_EA_INODE_FL              0x010085
 +
 +/* EA inode for parent inode does not have EXT4_EA_INODE_FL flag */
 +#define PR_1_ATTR_SET_EA_INODE_FL             0x010086
 +
 +
  /*
   * Pass 1b errors
   */
  /* Directory entry has deleted or unused inode */
  #define PR_2_UNUSED_INODE     0x020003
  
- /* Directry entry is link to '.' */
+ /* Directory entry is link to '.' */
  #define PR_2_LINK_DOT         0x020004
  
  /* Directory entry points to inode now located in a bad block */
  /* Directory entry contains a link to a directory */
  #define PR_2_LINK_DIR         0x020006
  
- /* Directory entry contains a link to the root directry */
+ /* Directory entry contains a link to the root directory */
  #define PR_2_LINK_ROOT                0x020007
  
  /* Directory entry has illegal characters in its name */
  /* i_file_acl should be zero */
  #define PR_2_FILE_ACL_ZERO    0x02000E
  
 -/* i_dir_acl should be zero */
 -#define PR_2_DIR_ACL_ZERO     0x02000F
 +/* i_size_high should be zero */
 +#define PR_2_DIR_SIZE_HIGH_ZERO       0x02000F
  
  /* i_frag should be zero */
  #define PR_2_FRAG_ZERO                0x020010
  #define PR_3_LPF_ENCRYPTED            0x03001B
  
  /*
-  * Pass 3a --- rehashing diretories
+  * Pass 3a --- rehashing directories
   */
  /* Pass 3a: Reindexing directories */
  #define PR_3A_PASS_HEADER             0x031000
  /* Inconsistent inode count information cached */
  #define PR_4_INCONSISTENT_COUNT               0x040004
  
 +/* Extended attribute inode ref count wrong */
 +#define PR_4_EA_INODE_REF_COUNT               0x040005
 +
  /*
   * Pass 5 errors
   */
  /* Inode range not used, but marked in bitmap */
  #define PR_5_INODE_RANGE_UNUSED               0x050016
  
- /* Inode rangeused, but not marked used in bitmap */
+ /* Inode range used, but not marked used in bitmap */
  #define PR_5_INODE_RANGE_USED         0x050017
  
  /* Block in use but group is marked BLOCK_UNINIT */
index 7841358a19e225e21b8f6538c135a5f985d608af,7ea668b52eabd38e5ee71c9d549ecf6de44db88f..e8052a909a63672336ca620375c95372ce7e69f2
@@@ -83,7 -83,7 +83,7 @@@ subjects. I can think of two ways in wh
  <Para>
        The "Engineer" way
  
- Learn the subject throughly before I get to the programming itself.
+ Learn the subject thoroughly before I get to the programming itself.
  Then, I could easily see the entire picture and select the best
  course of action, taking all the factors into account.
  </Para>
@@@ -94,7 -94,7 +94,7 @@@
        The "Explorer - Progressive" way.
  
  Jump immediately into the cold water - Start programming and
- learning the material parallelly.
+ learning the material in parallel.
  </Para>
  </ListItem>
  
@@@ -418,7 -418,7 +418,7 @@@ superblock was set exactly to the abov
  <Para>
  It seems that starting with the <Literal remap="tt">superblock</Literal> was a good bet - Just from
  the list of variables, one can learn a lot. I didn't understand all of them
- at the time, but it seemed that the following keywords were repeating themself
+ at the time, but it seemed that the following keywords were repeating themselves
  in various variables:
  
  <ItemizedList>
@@@ -710,8 -710,8 +710,8 @@@ int dispatch (char *command_line
  <Title>Source files in EXT2ED</Title>
  
  <Para>
- The project was getting large enough to be splitted into several source
- files. I splitted the source as much as I could into self-contained
+ The project was getting large enough to be split into several source
+ files. I split the source as much as I could into self-contained
  source files. The source files consist of the following blocks:
  
  <ItemizedList>
@@@ -1156,7 -1156,7 +1156,7 @@@ according to the source division outlin
  store the necessary information about the inode in a specific structure
  of type struct_file_info which will be available for use by the file_com.c
  functions. Only then it will set the type to file. This is also the reason
- that a direct asynchronic set of the object type to a file through a settype
+ that a direct asynchronous set of the object type to a file through a settype
  command will fail - The above data structure will not be initialized
  properly because the user never was at the inode of the file. 
  
@@@ -2726,7 -2726,7 +2726,7 @@@ struct ext2_inode 
        __u32   i_block[EXT2_N_BLOCKS]; /* Pointers to blocks */
        __u32   i_version;              /* File version (for NFS) */
        __u32   i_file_acl;             /* File ACL */
 -      __u32   i_dir_acl;              /* Directory ACL */
 +      __u32   i_size_high;            /* High 32bits of size */
        __u32   i_faddr;                /* Fragment address */
        union {
                struct {
index 900c3930f57cee6f5c88eb5e939aa1e2dff8875b,1bf8c5f633ba4b787c2bfaca05f616123558148a..0d54f07c1268444eab1be6648af2c4aaf798cf28
@@@ -487,7 -487,7 +487,7 @@@ struct ext2_inode 
        __u32   i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
        __u32   i_version;      /* File version (for NFS) */
        __u32   i_file_acl;     /* File ACL */
 -      __u32   i_dir_acl;      /* Directory ACL */
 +      __u32   i_size_high;    /* High 32bits of size */
        __u32   i_faddr;        /* Fragment address */
        union {
                struct {
@@@ -533,7 -533,7 +533,7 @@@ allocated blocks
  </Para>
  
  <Para>
- It was found experimently that many of the files in the filesystem are
+ It was found experimentally that many of the files in the filesystem are
  actually quite small. To take advantage of this effect, the kernel provides
  storage of up to 12 block numbers in the inode itself. Those blocks are
  called <Literal remap="tt">direct blocks</Literal>. The advantage is that once the kernel has the
@@@ -692,7 -692,7 +692,7 @@@ options. They determine the type of th
  <Title>Time and date</Title>
  
  <Para>
- Linux records the last time in which various operations occured with the
+ Linux records the last time in which various operations occurred with the
  file. The time and date are saved in the standard C library format - The
  number of seconds which passed since 00:00:00 GMT, January 1, 1970. The
  following times are recorded:
@@@ -1087,7 -1087,7 +1087,7 @@@ the length of the file name
  <Para>
  The variable <Literal remap="tt">rec&lowbar;len</Literal> is provided because the directory entries are
  padded with zeroes so that the next entry will be in an offset which is
- a multiplition of 4. The resulting directory entry size is stored in
+ a multiplication of 4. The resulting directory entry size is stored in
  <Literal remap="tt">rec&lowbar;len</Literal>. If the directory entry is the last in the block, it is
  padded with zeroes till the end of the block, and rec&lowbar;len is updated
  accordingly.
@@@ -1402,7 -1402,7 +1402,7 @@@ then zero s&lowbar;mnt&lowbar;count. <L
  E2fsck also records the last time in which the file system was checked in
  the <Literal remap="tt">s&lowbar;lastcheck</Literal> variable. The user tunable parameter
  <Literal remap="tt">s&lowbar;checkinterval</Literal> will contain the number of seconds which are allowed
- to pass since <Literal remap="tt">s&lowbar;lastcheck</Literal> until a check is reforced. A value of
+ to pass since <Literal remap="tt">s&lowbar;lastcheck</Literal> until a check is forced. A value of
  <Literal remap="tt">0</Literal> disables time-based check.
  </Para>
  
diff --combined ext2ed/ext2.descriptors
index b1ac4c4bb70d31f9bdd7cd9af80ca83d5f0c6981,4b9111bc2a1391f856fa732d87efbcb17d211856..e356f4cdce9314adc4ef442a160e0c6051461b39
@@@ -5,7 -5,7 +5,7 @@@ Most of this file is just copied from t
  My parser is very primitive - It only searches for the struct keywords,
  and uses the variables in there. The rest of the file is just ignored.
  
- You will find at the end a few additional types which are not aviable in
+ You will find at the end a few additional types which are not available in
  the original include file, such as the types "file" and "dir". They have
  no variables, but are necessary due to the way ext2ed binds C commands
  to specific types.
@@@ -102,7 -102,7 +102,7 @@@ struct ext2_inode 
        __u32  i_block[14]; /* Pointers to blocks */
        __u32  i_version;       /* File version (for NFS) */
        __u32  i_file_acl;      /* File ACL */
 -      __u32  i_dir_acl;       /* Directory ACL */
 +      __u32  i_size_high;     /* High 32bits of size */
        __u32  i_faddr;         /* Fragment address */
        __u8   l_i_frag;        /* Fragment number */
        __u8   l_i_fsize;       /* Fragment size */
diff --combined lib/ext2fs/Makefile.in
index b729f619d8bc47db46268c1b350ef4647eb2890e,f84ea83ee77af6d803f673af6741224c7d105365..946cae16f1013da9a74cbaac391c4cd07b7c4170
@@@ -5,7 -5,7 +5,7 @@@ top_builddir = ../.
  my_dir = lib/ext2fs
  INSTALL = @INSTALL@
  DEPEND_CFLAGS = -I$(top_srcdir)/debugfs -I$(srcdir)/../../e2fsck -DDEBUGFS
- # This nastyness is needed because of jfs_user.h hackery; when we finally
+ # This nastiness is needed because of jfs_user.h hackery; when we finally
  # clean up this mess, we should be able to drop it
  DEBUGFS_CFLAGS = -I$(srcdir)/../../e2fsck $(ALL_CFLAGS) -DDEBUGFS
  
@@@ -125,7 -125,6 +125,7 @@@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_
        $(TDB_OBJ) \
        undo_io.o \
        unix_io.o \
 +      sparse_io.o \
        unlink.o \
        valid_blk.o \
        version.o \
@@@ -213,7 -212,6 +213,7 @@@ SRCS= ext2_err.c 
        $(srcdir)/tst_iscan.c \
        $(srcdir)/undo_io.c \
        $(srcdir)/unix_io.c \
 +      $(srcdir)/sparse_io.c \
        $(srcdir)/unlink.c \
        $(srcdir)/valid_blk.c \
        $(srcdir)/version.c \
@@@ -523,7 -521,7 +523,7 @@@ mkjournal: mkjournal.c $(STATIC_LIBEXT2
        $(Q) $(CC) -o mkjournal $(srcdir)/mkjournal.c -DDEBUG \
                $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) $(ALL_CFLAGS) $(SYSLIBS)
  
 -check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
 +fullcheck check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
      tst_super_size tst_types tst_inode_size tst_csum tst_crc32c tst_bitmaps \
      tst_inline tst_inline_data tst_libext2fs tst_sha256 tst_sha512 \
      tst_digest_encode tst_getsize tst_getsectsize
@@@ -1111,12 -1109,6 +1111,12 @@@ unix_io.o: $(srcdir)/unix_io.c $(top_bu
   $(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)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/ext2fsP.h
 +sparse_io.o: $(srcdir)/sparse_io.c $(top_builddir)/lib/config.h \
 + $(top_builddir)/lib/dirpaths.h $(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 \
 + $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
 + $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h
  unlink.o: $(srcdir)/unlink.c $(top_builddir)/lib/config.h \
   $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
   $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
diff --combined lib/ext2fs/blkmap64_rb.c
index 8d5ddd3fcedc0dbf627f68523c11319697b9d095,6fc245fda08eb5711aa3ae52e20aeda8a3ce5262..557047749d55a76cb8ca69146864945efa10c7a6
@@@ -23,9 -23,6 +23,9 @@@
  #if HAVE_SYS_TYPES_H
  #include <sys/types.h>
  #endif
 +#if HAVE_LINUX_TYPES_H
 +#include <linux/types.h>
 +#endif
  
  #include "ext2_fs.h"
  #include "ext2fsP.h"
@@@ -555,7 -552,7 +555,7 @@@ static int rb_remove_extent(__u64 start
                        retval = 1;
                        continue;
                } else {
-                       /* modify the last extent in reigon to be removed */
+                       /* modify the last extent in region to be removed */
                        ext->count -= ((start + count) - ext->start);
                        ext->start = start + count;
                        retval = 1;
diff --combined lib/ext2fs/ext2_fs.h
index 3b55000e3a905482f70fd193f657fce17e635d9e,637fab20fe5166bcb135e7e55dd026e1f26af774..2496d16dac55009a1ef51959e83edc83513df30f
@@@ -398,7 -398,7 +398,7 @@@ struct ext2_inode 
        __u32   i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
        __u32   i_generation;   /* File version (for NFS) */
        __u32   i_file_acl;     /* File ACL */
 -      __u32   i_size_high;    /* Formerly i_dir_acl, directory ACL */
 +      __u32   i_size_high;
        __u32   i_faddr;        /* Fragment address */
        union {
                struct {
@@@ -446,7 -446,7 +446,7 @@@ struct ext2_inode_large 
        __u32   i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
        __u32   i_generation;   /* File version (for NFS) */
        __u32   i_file_acl;     /* File ACL */
 -      __u32   i_size_high;    /* Formerly i_dir_acl, directory ACL */
 +      __u32   i_size_high;
        __u32   i_faddr;        /* Fragment address */
        union {
                struct {
  #define EXT4_EPOCH_BITS 2
  #define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
  
 -#define i_dir_acl     i_size_high
 -
  #define i_checksum_lo osd2.linux2.l_i_checksum_lo
  
  #define inode_includes(size, field)                   \
@@@ -921,9 -923,7 +921,9 @@@ EXT4_FEATURE_INCOMPAT_FUNCS(encrypt,               4
  
  #define EXT2_FEATURE_COMPAT_SUPP      0
  #define EXT2_FEATURE_INCOMPAT_SUPP    (EXT2_FEATURE_INCOMPAT_FILETYPE| \
 -                                     EXT4_FEATURE_INCOMPAT_MMP)
 +                                     EXT4_FEATURE_INCOMPAT_MMP| \
 +                                     EXT4_FEATURE_INCOMPAT_LARGEDIR| \
 +                                     EXT4_FEATURE_INCOMPAT_EA_INODE)
  #define EXT2_FEATURE_RO_COMPAT_SUPP   (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
                                         EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
                                         EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \
@@@ -970,7 -970,7 +970,7 @@@ struct ext2_dir_entry 
   * bigger than 255 chars, it's safe to reclaim the extra byte for the
   * file_type field.
   *
-  * This structure is deprecated due to endianity issues. Please use struct
+  * This structure is deprecated due to endian issues. Please use struct
   * ext2_dir_entry and accessor functions
   *   ext2fs_dirent_name_len
   *   ext2fs_dirent_set_name_len
@@@ -1048,7 -1048,7 +1048,7 @@@ struct ext2_dir_entry_tail 
   * regardless of how old the timestamp is.
   *
   * The timestamp in the MMP structure will be updated by e2fsck at some
-  * arbitary intervals (start of passes, after every few groups of inodes
+  * arbitrary intervals (start of passes, after every few groups of inodes
   * in pass1 and pass1b).  There is no guarantee that e2fsck is updating
   * the MMP block in a timely manner, and the updates it does are purely
   * for the convenience of the sysadmin and not for automatic validation.
diff --combined lib/ext2fs/ext2fs.h
index a4feacccbb38d004b727218628d73f3e578f0c20,6c6287b69e48f181dd344e98953636792174c628..6774e32c91c26a0fcb0865f8eff6ca3e00980604
@@@ -209,7 -209,6 +209,7 @@@ typedef struct ext2_file *ext2_file_t
  #define EXT2_MKJOURNAL_LAZYINIT       0x0000002 /* don't zero journal inode before use*/
  #define EXT2_MKJOURNAL_NO_MNT_CHECK 0x0000004 /* don't check mount status */
  
 +struct blk_alloc_ctx;
  struct opaque_ext2_group_desc;
  
  struct struct_ext2_filsys {
         */
        errcode_t (*get_alloc_block)(ext2_filsys fs, blk64_t goal,
                                     blk64_t *ret);
 +      errcode_t (*get_alloc_block2)(ext2_filsys fs, blk64_t goal,
 +                                    blk64_t *ret, struct blk_alloc_ctx *ctx);
        void (*block_alloc_stats)(ext2_filsys fs, blk64_t blk, int inuse);
  
        /*
   *
   * BLOCK_FLAG_DEPTH_TRAVERSE indicates that the iterator function for
   * the indirect, doubly indirect, etc. blocks should be called after
-  * all of the blocks containined in the indirect blocks are processed.
+  * all of the blocks contained in the indirect blocks are processed.
   * This is useful if you are going to be deallocating blocks from an
   * inode.
   *
  #define BLOCK_COUNT_TIND      (-3)
  #define BLOCK_COUNT_TRANSLATOR        (-4)
  
 +#define BLOCK_ALLOC_UNKNOWN   0
 +#define BLOCK_ALLOC_DATA      1
 +#define BLOCK_ALLOC_METADATA  2
 +
 +struct blk_alloc_ctx {
 +      ext2_ino_t              ino;
 +      struct ext2_inode       *inode;
 +      blk64_t                 lblk;
 +      int                     flags;
 +};
 +
  #if 0
  /*
   * Flags for ext2fs_move_blocks
@@@ -423,7 -409,7 +423,7 @@@ typedef struct ext2_extent_path *ext2_e
  /*
   * Flags used by ext2fs_extent_delete()
   */
- #define EXT2_EXTENT_DELETE_KEEP_EMPTY 0x001 /* keep node if last extnt gone */
+ #define EXT2_EXTENT_DELETE_KEEP_EMPTY 0x001 /* keep node if last extent gone */
  
  /*
   * Flags used by ext2fs_extent_set_bmap()
@@@ -599,13 -585,11 +599,13 @@@ typedef struct ext2_icount *ext2_icount
                                         EXT3_FEATURE_INCOMPAT_RECOVER|\
                                         EXT3_FEATURE_INCOMPAT_EXTENTS|\
                                         EXT4_FEATURE_INCOMPAT_FLEX_BG|\
 +                                       EXT4_FEATURE_INCOMPAT_EA_INODE|\
                                         EXT4_LIB_INCOMPAT_MMP|\
                                         EXT4_FEATURE_INCOMPAT_64BIT|\
                                         EXT4_FEATURE_INCOMPAT_INLINE_DATA|\
                                         EXT4_FEATURE_INCOMPAT_ENCRYPT|\
 -                                       EXT4_FEATURE_INCOMPAT_CSUM_SEED)
 +                                       EXT4_FEATURE_INCOMPAT_CSUM_SEED|\
 +                                       EXT4_FEATURE_INCOMPAT_LARGEDIR)
  
  #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP       (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
                                         EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
@@@ -679,9 -663,6 +679,9 @@@ extern errcode_t ext2fs_new_block(ext2_
                                  ext2fs_block_bitmap map, blk_t *ret);
  extern errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal,
                                   ext2fs_block_bitmap map, blk64_t *ret);
 +extern errcode_t ext2fs_new_block3(ext2_filsys fs, blk64_t goal,
 +                                 ext2fs_block_bitmap map, blk64_t *ret,
 +                                 struct blk_alloc_ctx *ctx);
  extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start,
                                        blk_t finish, int num,
                                        ext2fs_block_bitmap map,
@@@ -694,10 -675,6 +694,10 @@@ extern errcode_t ext2fs_alloc_block(ext
                                    char *block_buf, blk_t *ret);
  extern errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal,
                                     char *block_buf, blk64_t *ret);
 +extern errcode_t ext2fs_alloc_block3(ext2_filsys fs, blk64_t goal,
 +                                   char *block_buf, blk64_t *ret,
 +                                   struct blk_alloc_ctx *ctx);
 +
  extern void ext2fs_set_alloc_block_callback(ext2_filsys fs,
                                            errcode_t (*func)(ext2_filsys fs,
                                                              blk64_t goal,
@@@ -1199,9 -1176,6 +1199,9 @@@ extern errcode_t ext2fs_expand_dir(ext2
  /* ext_attr.c */
  extern __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry,
                                        void *data);
 +extern errcode_t ext2fs_ext_attr_hash_entry2(ext2_filsys fs,
 +                                           struct ext2_ext_attr_entry *entry,
 +                                           void *data, __u32 *hash);
  extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf);
  extern errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block,
                                       void *buf);
@@@ -1248,12 -1222,6 +1248,12 @@@ errcode_t ext2fs_xattr_inode_max_size(e
  #define XATTR_HANDLE_FLAG_RAW 0x0001
  errcode_t ext2fs_xattrs_flags(struct ext2_xattr_handle *handle,
                              unsigned int *new_flags, unsigned int *old_flags);
 +extern void ext2fs_ext_attr_block_rehash(struct ext2_ext_attr_header *header,
 +                                       struct ext2_ext_attr_entry *end);
 +extern __u32 ext2fs_get_ea_inode_hash(struct ext2_inode *inode);
 +extern void ext2fs_set_ea_inode_hash(struct ext2_inode *inode, __u32 hash);
 +extern __u64 ext2fs_get_ea_inode_ref(struct ext2_inode *inode);
 +extern void ext2fs_set_ea_inode_ref(struct ext2_inode *inode, __u64 ref_count);
  
  /* extent.c */
  extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
@@@ -1614,7 -1582,6 +1614,7 @@@ errcode_t ext2fs_unlink(ext2_filsys fs
  /* symlink.c */
  errcode_t ext2fs_symlink(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t ino,
                         const char *name, const char *target);
 +int ext2fs_is_fast_symlink(struct ext2_inode *inode);
  
  /* mmp.c */
  errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf);
@@@ -1961,23 -1928,6 +1961,23 @@@ _INLINE_ blk_t ext2fs_inode_data_blocks
        return (blk_t) ext2fs_inode_data_blocks2(fs, inode);
  }
  
 +/* htree levels for ext4 */
 +#define EXT4_HTREE_LEVEL_COMPAT 2
 +#define EXT4_HTREE_LEVEL      3
 +
 +static inline unsigned int ext2_dir_htree_level(ext2_filsys fs)
 +{
 +      if (ext2fs_has_feature_largedir(fs->super))
 +              return EXT4_HTREE_LEVEL;
 +
 +      return EXT4_HTREE_LEVEL_COMPAT;
 +}
 +
 +_INLINE_ int ext2fs_htree_intnode_maxrecs(ext2_filsys fs, int blocks)
 +{
 +      return blocks * ((fs->blocksize - 8) / sizeof(struct ext2_dx_entry));
 +}
 +
  /*
   * This is an efficient, overflow safe way of calculating ceil((1.0 * a) / b)
   */
diff --combined lib/ext2fs/inline_data.c
index 0c1eef6c73d22ca260623d193fd7fc5fc4a47596,e3ae2e84ec502d6dc30db8bf16c42c5b7dba3362..7215c5173fe99614e46723a927050bc4bd7e0ad8
@@@ -42,6 -42,11 +42,6 @@@ static errcode_t ext2fs_inline_data_ea_
  
        retval = ext2fs_xattr_set(handle, "system.data",
                                  data->ea_data, data->ea_size);
 -      if (retval)
 -              goto err;
 -
 -      retval = ext2fs_xattrs_write(handle);
 -
  err:
        (void) ext2fs_xattrs_close(&handle);
        return retval;
@@@ -265,6 -270,11 +265,6 @@@ errcode_t ext2fs_inline_data_ea_remove(
                goto err;
  
        retval = ext2fs_xattr_remove(handle, "system.data");
 -      if (retval)
 -              goto err;
 -
 -      retval = ext2fs_xattrs_write(handle);
 -
  err:
        (void) ext2fs_xattrs_close(&handle);
        return retval;
@@@ -311,7 -321,7 +311,7 @@@ static errcode_t ext2fs_inline_data_con
        dir->name[1] = '.';
  
        /*
-        * Ajust the last rec_len
+        * Adjust the last rec_len
         */
        offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2);
        dir = (struct ext2_dir_entry *) (bbuf + offset);
@@@ -603,7 -613,7 +603,7 @@@ static errcode_t file_test(ext2_filsys 
        /* create a new file */
        retval = ext2fs_new_inode(fs, 2, 010755, 0, &newfile);
        if (retval) {
-               com_err("file_test", retval, "while allocaing a new inode");
+               com_err("file_test", retval, "while allocating a new inode");
                return 1;
        }
  
        inode.i_mode = LINUX_S_IFREG;
        retval = ext2fs_write_new_inode(fs, newfile, &inode);
        if (retval) {
-               com_err("file_test", retval, "while writting a new inode");
+               com_err("file_test", retval, "while writing a new inode");
                return 1;
        }
  
@@@ -801,7 -811,7 +801,7 @@@ int main(int argc, char *argv[]
        retval = ext2fs_allocate_tables(fs);
        if (retval) {
                com_err("setup", retval,
-                       "while allocating tables for test filesysmte");
+                       "while allocating tables for test filesystem");
                exit(1);
        }
  
diff --combined misc/Makefile.in
index f696d13a8b58518b167fbbdb66d00debca780233,5a0f16f5c3485c846a15e8540688d70bef1da9d3..4bb92ace298edbb5a38a9a544c0abb90341e05b3
@@@ -118,7 -118,7 +118,7 @@@ DEPLIBS_E2P= $(LIBE2P) $(DEPLIBCOM_ERR
  
  COMPILE_ET=   _ET_DIR_OVERRIDE=$(srcdir)/../lib/et/et ../lib/et/compile_et
  
- # This nastyness is needed because of jfs_user.h hackery; when we finally
+ # This nastiness is needed because of jfs_user.h hackery; when we finally
  # clean up this mess, we should be able to drop it
  JOURNAL_CFLAGS = -I$(srcdir)/../e2fsck $(ALL_CFLAGS) -DDEBUGFS
  DEPEND_CFLAGS = -I$(top_srcdir)/e2fsck
@@@ -252,7 -252,7 +252,7 @@@ base_device: base_device.
        $(Q) $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(srcdir)/base_device.c \
                -DDEBUG -o base_device $(SYSLIBS)
  
 -check:: base_device
 +fullcheck check:: base_device
        ./base_device < $(srcdir)/base_device.tst > base_device.out
        cmp $(srcdir)/base_device.tst base_device.out
  
diff --combined misc/create_inode.c
index 8f7445d027349c0449dc08f48fcac8c031337a77,73dd2f215f89e38e2dce08a72814bf947df9f095..2dbb433c3f443b66d76395d01f71130467c2028e
@@@ -33,7 -33,7 +33,7 @@@
  #include "create_inode.h"
  #include "support/nls-enable.h"
  
- /* 64KiB is the minimium blksize to best minimize system call overhead. */
+ /* 64KiB is the minimum blksize to best minimize system call overhead. */
  #define COPY_FILE_BUFLEN      65536
  
  static int ext2_file_type(unsigned int mode)
@@@ -683,31 -683,10 +683,31 @@@ out
        return retval;
  }
  
 +struct file_info {
 +      char *path;
 +      size_t path_len;
 +      size_t path_max_len;
 +};
 +
 +static errcode_t path_append(struct file_info *target, const char *file)
 +{
 +      if (strlen(file) + target->path_len + 1 > target->path_max_len) {
 +              target->path_max_len *= 2;
 +              target->path = realloc(target->path, target->path_max_len);
 +              if (!target->path)
 +                      return EXT2_ET_NO_MEMORY;
 +      }
 +      target->path_len += sprintf(target->path + target->path_len, "/%s",
 +                                  file);
 +      return 0;
 +}
 +
  /* Copy files from source_dir to fs */
  static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
                               const char *source_dir, ext2_ino_t root,
 -                             struct hdlinks_s *hdlinks)
 +                             struct hdlinks_s *hdlinks,
 +                             struct file_info *target,
 +                             struct fs_ops_callbacks *fs_callbacks)
  {
        const char      *name;
        DIR             *dh;
        errcode_t       retval = 0;
        int             read_cnt;
        int             hdlink;
 +      size_t          cur_dir_path_len;
  
        if (chdir(source_dir) < 0) {
                retval = errno;
                                save_inode = 1;
                }
  
 +              cur_dir_path_len = target->path_len;
 +              retval = path_append(target, name);
 +              if (retval)
 +                      return retval;
 +
 +              if (fs_callbacks && fs_callbacks->create_new_inode) {
 +                      retval = fs_callbacks->create_new_inode(fs,
 +                              target->path, name, parent_ino, root,
 +                              st.st_mode & S_IFMT);
 +                      if (retval)
 +                              goto out;
 +              }
 +
                switch(st.st_mode & S_IFMT) {
                case S_IFCHR:
                case S_IFBLK:
@@@ -857,8 -822,7 +857,8 @@@ find_lnf
                                        goto out;
                        }
                        /* Populate the dir recursively*/
 -                      retval = __populate_fs(fs, ino, name, root, hdlinks);
 +                      retval = __populate_fs(fs, ino, name, root, hdlinks,
 +                                             target, fs_callbacks);
                        if (retval)
                                goto out;
                        if (chdir("..")) {
                        goto out;
                }
  
 +              if (fs_callbacks && fs_callbacks->end_create_new_inode) {
 +                      retval = fs_callbacks->end_create_new_inode(fs,
 +                              target->path, name, parent_ino, root,
 +                              st.st_mode & S_IFMT);
 +                      if (retval)
 +                              goto out;
 +              }
 +
                /* Save the hardlink ino */
                if (save_inode) {
                        /*
                        hdlinks->hdl[hdlinks->count].dst_ino = ino;
                        hdlinks->count++;
                }
 +              target->path_len = cur_dir_path_len;
 +              target->path[target->path_len] = 0;
        }
  
  out:
        return retval;
  }
  
 -errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
 -                    const char *source_dir, ext2_ino_t root)
 +errcode_t populate_fs2(ext2_filsys fs, ext2_ino_t parent_ino,
 +                     const char *source_dir, ext2_ino_t root,
 +                     struct fs_ops_callbacks *fs_callbacks)
  {
 +      struct file_info file_info;
        struct hdlinks_s hdlinks;
        errcode_t retval;
  
                return retval;
        }
  
 -      retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks);
 +      file_info.path_len = 0;
 +      file_info.path_max_len = 255;
 +      file_info.path = calloc(file_info.path_max_len, 1);
  
 +      retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks,
 +                             &file_info, fs_callbacks);
 +
 +      free(file_info.path);
        free(hdlinks.hdl);
        return retval;
  }
 +
 +errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
 +                    const char *source_dir, ext2_ino_t root)
 +{
 +      return populate_fs2(fs, parent_ino, source_dir, root, NULL);
 +}
diff --combined resize/resize2fs.c
index 20a0c463e411c20754e2b3a9a91b87731ae41e8e,bedeb102393a7fc81c0a989184bc31e85de2233c..0bd325bae81d04a133529e805ca7ecca3a9b5ad4
@@@ -4,7 -4,7 +4,7 @@@
   * Copyright (C) 1997, 1998 by Theodore Ts'o and
   *    PowerQuest, Inc.
   *
-  * Copyright (C) 1999, 2000 by Theosore Ts'o
+  * Copyright (C) 1999, 2000 by Theodore Ts'o
   *
   * %Begin-Header%
   * This file may be redistributed under the terms of the GNU Public
@@@ -80,7 -80,7 +80,7 @@@ static int is_inode_tb(ext2_filsys fs, 
                      fs->inode_blocks_per_group);
  }
  
- /* Some bigalloc helper macros which are more succint... */
+ /* Some bigalloc helper macros which are more succinct... */
  #define B2C(x)        EXT2FS_B2C(fs, (x))
  #define C2B(x)        EXT2FS_C2B(fs, (x))
  #define EQ_CLSTR(x, y) (B2C(x) == B2C(y))
@@@ -578,7 -578,7 +578,7 @@@ out
  }
  
  /*
-  * Clean up the bitmaps for unitialized bitmaps
+  * Clean up the bitmaps for uninitialized bitmaps
   */
  static void fix_uninit_block_bitmaps(ext2_filsys fs)
  {
@@@ -932,7 -932,7 +932,7 @@@ retry
        /*
         * If we changed the number of block_group descriptor blocks,
         * we need to make sure they are all marked as reserved in the
-        * file systems's block allocation map.
+        * filesystem's block allocation map.
         */
        for (i = 0; i < old_fs->group_desc_count; i++)
                ext2fs_reserve_super_and_bgd(fs, i, fs->block_map);
@@@ -1503,7 -1503,7 +1503,7 @@@ static errcode_t blocks_to_move(ext2_re
  
                /*
                 * For those structures that have changed, we need to
-                * do bookkeepping.
+                * do bookkeeping.
                 */
                if (ext2fs_block_bitmap_loc(old_fs, i) !=
                    (blk = ext2fs_block_bitmap_loc(fs, i))) {
@@@ -1986,145 -1986,6 +1986,145 @@@ static void quiet_com_err_proc(const ch
  {
  }
  
 +static int fix_ea_entries(ext2_extent imap, struct ext2_ext_attr_entry *entry,
 +                        struct ext2_ext_attr_entry *end, ext2_ino_t last_ino)
 +{
 +      int modified = 0;
 +      ext2_ino_t new_ino;
 +      errcode_t retval;
 +
 +      while (entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry)) {
 +              if (entry->e_value_inum > last_ino) {
 +                      new_ino = ext2fs_extent_translate(imap,
 +                                                        entry->e_value_inum);
 +                      entry->e_value_inum = new_ino;
 +                      modified = 1;
 +              }
 +              entry = EXT2_EXT_ATTR_NEXT(entry);
 +      }
 +      return modified;
 +}
 +
 +static int fix_ea_ibody_entries(ext2_extent imap,
 +                              struct ext2_inode_large *inode, int inode_size,
 +                              ext2_ino_t last_ino)
 +{
 +      struct ext2_ext_attr_entry *start, *end;
 +      __u32 *ea_magic;
 +
 +      if (inode->i_extra_isize == 0)
 +              return 0;
 +
 +      ea_magic = (__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
 +                              inode->i_extra_isize);
 +      if (*ea_magic != EXT2_EXT_ATTR_MAGIC)
 +              return 0;
 +
 +      start = (struct ext2_ext_attr_entry *)(ea_magic + 1);
 +      end = (struct ext2_ext_attr_entry *)((char *)inode + inode_size);
 +
 +      return fix_ea_entries(imap, start, end, last_ino);
 +}
 +
 +static int fix_ea_block_entries(ext2_extent imap, char *block_buf,
 +                              unsigned int blocksize, ext2_ino_t last_ino)
 +{
 +      struct ext2_ext_attr_header *header;
 +      struct ext2_ext_attr_entry *start, *end;
 +
 +      header = (struct ext2_ext_attr_header *)block_buf;
 +      start = (struct ext2_ext_attr_entry *)(header+1);
 +      end = (struct ext2_ext_attr_entry *)(block_buf + blocksize);
 +
 +      return fix_ea_entries(imap, start, end, last_ino);
 +}
 +
 +/* A simple LRU cache to check recently processed blocks. */
 +struct blk_cache {
 +      int cursor;
 +      blk64_t blks[4];
 +};
 +
 +#define BLK_IN_CACHE(b,c) ((b) == (c).blks[0] || (b) == (c).blks[1] || \
 +                         (b) == (c).blks[2] || (b) == (c).blks[3])
 +#define BLK_ADD_CACHE(b,c) {                  \
 +      (c).blks[(c).cursor] = (b);             \
 +      (c).cursor = ((c).cursor + 1) % 4;      \
 +}
 +
 +static errcode_t fix_ea_inode_refs(ext2_resize_t rfs, struct ext2_inode *inode,
 +                                 char *block_buf, ext2_ino_t last_ino)
 +{
 +      ext2_filsys     fs = rfs->new_fs;
 +      ext2_inode_scan scan = NULL;
 +      ext2_ino_t      ino;
 +      int             inode_size = EXT2_INODE_SIZE(fs->super);
 +      blk64_t         blk;
 +      int             modified;
 +      struct blk_cache blk_cache = { 0 };
 +      struct ext2_ext_attr_header *header;
 +      errcode_t               retval;
 +
 +      header = (struct ext2_ext_attr_header *)block_buf;
 +
 +      retval = ext2fs_open_inode_scan(fs, 0, &scan);
 +      if (retval)
 +              goto out;
 +
 +      while (1) {
 +              retval = ext2fs_get_next_inode_full(scan, &ino, inode,
 +                                                  inode_size);
 +              if (retval)
 +                      goto out;
 +              if (!ino)
 +                      break;
 +
 +              if (inode->i_links_count == 0 && ino != EXT2_RESIZE_INO)
 +                      continue; /* inode not in use */
 +
 +              if (inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
 +                      modified = fix_ea_ibody_entries(rfs->imap,
 +                                      (struct ext2_inode_large *)inode,
 +                                      inode_size, last_ino);
 +                      if (modified) {
 +                              retval = ext2fs_write_inode_full(fs, ino, inode,
 +                                                               inode_size);
 +                              if (retval)
 +                                      goto out;
 +                      }
 +              }
 +
 +              blk = ext2fs_file_acl_block(fs, inode);
 +              if (blk && !BLK_IN_CACHE(blk, blk_cache)) {
 +                      retval = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
 +                      if (retval)
 +                              goto out;
 +
 +                      modified = fix_ea_block_entries(rfs->imap, block_buf,
 +                                                      fs->blocksize,
 +                                                      last_ino);
 +                      if (modified) {
 +                              retval = ext2fs_write_ext_attr3(fs, blk,
 +                                                              block_buf, ino);
 +                              if (retval)
 +                                      goto out;
 +                              /*
 +                               * If refcount is greater than 1, we might see
 +                               * the same block referenced by other inodes
 +                               * later.
 +                               */
 +                              if (header->h_refcount > 1)
 +                                      BLK_ADD_CACHE(blk, blk_cache);
 +                      }
 +              }
 +      }
 +      retval = 0;
 +out:
 +      if (scan)
 +              ext2fs_close_inode_scan(scan);
 +      return retval;
 +
 +}
  static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
  {
        struct process_block_struct     pb;
        char                    *block_buf = 0;
        ext2_ino_t              start_to_move;
        int                     inode_size;
 +      int                     update_ea_inode_refs = 0;
  
        if ((rfs->old_fs->group_desc_count <=
             rfs->new_fs->group_desc_count) &&
  
                ext2fs_inode_alloc_stats2(rfs->new_fs, new_inode, +1,
                                          pb.is_dir);
 -              inode->i_ctime = time(0);
 +              /*
 +               * i_ctime field in xattr inodes contain a portion of the ref
 +               * count, do not overwrite.
 +               */
 +              if (inode->i_flags & EXT4_EA_INODE_FL)
 +                      update_ea_inode_refs = 1;
 +              else
 +                      inode->i_ctime = time(0);
 +
                retval = ext2fs_write_inode_full(rfs->old_fs, new_inode,
                                                inode, inode_size);
                if (retval)
@@@ -2282,14 -2134,6 +2282,14 @@@ remap_blocks
                                goto errout;
                }
        }
 +
 +      if (update_ea_inode_refs &&
 +          ext2fs_has_feature_ea_inode(rfs->new_fs->super)) {
 +              retval = fix_ea_inode_refs(rfs, inode, block_buf,
 +                                         start_to_move);
 +              if (retval)
 +                      goto errout;
 +      }
        io_channel_flush(rfs->old_fs->io);
  
  errout:
@@@ -2939,7 -2783,7 +2939,7 @@@ static int calc_group_overhead(ext2_fil
  
  
  /*
-  * calcluate the minimum number of blocks the given fs can be resized to
+  * calculate the minimum number of blocks the given fs can be resized to
   */
  blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags)
  {
  #endif
  
        /*
-        * if we need more group descriptors in order to accomodate our data
+        * if we need more group descriptors in order to accommodate our data
         * then we need to add them here
         */
        blks_needed = data_needed;