===================================================================
--- iam.orig/fs/ext3/iam.c
+++ iam/fs/ext3/iam.c
-@@ -0,0 +1,1321 @@
+@@ -0,0 +1,1375 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ return iam_leaf_ops(leaf)->key_cmp(leaf, key);
+}
+
++static int iam_leaf_keyeq(const struct iam_leaf *leaf,
++ const struct iam_key *key)
++{
++ return iam_leaf_ops(leaf)->key_eq(leaf, key);
++}
++
+#if EXT3_INVARIANT_ON
+static int iam_leaf_check(struct iam_leaf *leaf);
+extern int dx_node_check(struct iam_path *p, struct iam_frame *f);
+ return iam_leaf_keycmp(&it->ii_path.ip_leaf, k);
+}
+
++static inline int it_keyeq(const struct iam_iterator *it,
++ const struct iam_key *k)
++{
++ return iam_leaf_keyeq(&it->ii_path.ip_leaf, k);
++}
++
++static int it_ikeycmp(const struct iam_iterator *it, const struct iam_ikey *ik)
++{
++ return iam_ikeycmp(it->ii_path.ip_container,
++ iam_leaf_ikey(&it->ii_path.ip_leaf,
++ iam_path_ikey(&it->ii_path, 0)), ik);
++}
++
+static inline int it_at_rec(const struct iam_iterator *it)
+{
+ return !iam_leaf_at_end(&it->ii_path.ip_leaf);
+
+ result = iam_path_lookup(&it->ii_path, index);
+ if (result >= 0) {
-+ switch (result) {
++ int collision;
++
++ collision = result & IAM_LOOKUP_LAST;
++ switch (result & ~IAM_LOOKUP_LAST) {
+ case IAM_LOOKUP_EXACT:
+ result = +1;
+ it->ii_state = IAM_IT_ATTACHED;
+ default:
+ assert(0);
+ }
++ result |= collision;
+ }
+ /*
+ * See iam_it_get_exact() for explanation.
+}
+
+/*
++ * Correct hash, but not the same key was found, iterate through hash
++ * collision chain, looking for correct record.
++ */
++static int iam_it_collision(struct iam_iterator *it)
++{
++ int result;
++
++ assert(ergo(it_at_rec(it), !it_keyeq(it, it->ii_path.ip_key_target)));
++
++ while ((result = iam_it_next(it)) == 0) {
++ if (it_ikeycmp(it, it->ii_path.ip_ikey_target) != 0)
++ return -ENOENT;
++ if (it_keyeq(it, it->ii_path.ip_key_target))
++ return 0;
++ }
++ return result;
++}
++
++/*
+ * Attach iterator. After successful completion, @it points to record with
+ * least key not larger than @k.
+ *
+
+ result = __iam_it_get(it, 0);
+
++ if (result == IAM_LOOKUP_LAST) {
++ result = iam_it_collision(it);
++ if (result != 0) {
++ iam_it_put(it);
++ iam_it_fini(it);
++ result = __iam_it_get(it, 0);
++ } else
++ result = +1;
++ }
++ if (result > 0)
++ result &= ~IAM_LOOKUP_LAST;
++
+ assert_corr(ergo(result > 0, it_keycmp(it, k) == 0));
+ assert_corr(ergo(result == 0 && it_state(it) == IAM_IT_ATTACHED,
+ it_keycmp(it, k) <= 0));
+ assert_corr(it_state(it) == IAM_IT_DETACHED);
+
+ it->ii_path.ip_ikey_target = k;
-+ return __iam_it_get(it, 1);
++ return __iam_it_get(it, 1) & ~IAM_LOOKUP_LAST;
+}
+
+/*
+ struct iam_path *path;
+ struct iam_leaf *leaf;
+
-+ assert_corr(it->ii_flags&IAM_IT_MOVE);
++ /* assert_corr(it->ii_flags&IAM_IT_MOVE); */
+ assert_corr(it_state(it) == IAM_IT_ATTACHED ||
+ it_state(it) == IAM_IT_SKEWED);
+
===================================================================
--- iam.orig/fs/ext3/iam_htree.c
+++ iam/fs/ext3/iam_htree.c
-@@ -0,0 +1,668 @@
+@@ -0,0 +1,683 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ __u32 hash;
+ int result;
+ int namelen;
++ int last = 1;
+ const char *name;
+
+ c = iam_leaf_container(l);
+ found = scan;
+ result = IAM_LOOKUP_EXACT;
+ break;
-+ } else if (ent_is_live(scan) && gethash(l, scan) <= hash)
-+ found = scan;
++ } else if (ent_is_live(scan)) {
++ if (gethash(l, scan) <= hash)
++ found = scan;
++ else
++ last = 0;
++ }
+ }
+ if (found == NULL) {
+ /*
+ l->il_at = (void *)found;
+ assert_corr(iam_leaf_at_rec(l));
+ }
++ if (last)
++ result |= IAM_LOOKUP_LAST;
+ return result;
+}
+
+ assert(0);
+}
+
-+static int iam_htree_key_cmp(const struct iam_leaf *l,
-+ const struct iam_key *k)
++static int iam_htree_key_cmp(const struct iam_leaf *l, const struct iam_key *k)
+{
+ const char *name;
+ __u32 h0;
+ return h0 < h1 ? -1 : (h0 == h1 ? 0 : +1);
+}
+
++static int iam_htree_key_eq(const struct iam_leaf *l, const struct iam_key *k)
++{
++ const char *name;
++
++ name = (const char *)k;
++ return match(strlen(name), name, getent(l));
++}
++
+static void iam_htree_rec_set(struct iam_leaf *l, const struct iam_rec *r)
+{
+ __u32 *ino;
+ .rec = iam_htree_rec,
+ .key_set = iam_htree_key_set,
+ .key_cmp = iam_htree_key_cmp,
++ .key_eq = iam_htree_key_eq,
+ .key_size = iam_htree_key_size,
+ .rec_set = iam_htree_rec_set,
+ .lookup = iam_htree_lookup,
===================================================================
--- iam.orig/fs/ext3/iam_lfix.c
+++ iam/fs/ext3/iam_lfix.c
-@@ -0,0 +1,675 @@
+@@ -0,0 +1,682 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ return lfix_keycmp(iam_leaf_container(l), iam_leaf_key_at(l->il_at), k);
+}
+
++static int iam_lfix_key_eq(const struct iam_leaf *l, const struct iam_key *k)
++{
++ return !lfix_keycmp(iam_leaf_container(l),
++ iam_leaf_key_at(l->il_at), k);
++}
++
+static void iam_lfix_rec_set(struct iam_leaf *l, const struct iam_rec *r)
+{
+ assert_corr(iam_leaf_at_rec(l));
+ .rec = iam_lfix_rec,
+ .key_set = iam_lfix_key_set,
+ .key_cmp = iam_lfix_key_cmp,
++ .key_eq = iam_lfix_key_eq,
+ .key_size = iam_lfix_key_size,
+ .rec_set = iam_lfix_rec_set,
+ .lookup = iam_lfix_lookup,
===================================================================
--- iam.orig/fs/ext3/iam_lvar.c
+++ iam/fs/ext3/iam_lvar.c
-@@ -0,0 +1,990 @@
+@@ -0,0 +1,1005 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ int namelen;
+ int found_equal;
+ lvar_hash_t hash;
++ int last;
+
+ assert_inv(n_invariant(leaf));
+ end = n_end(leaf);
+ hash = get_hash(iam_leaf_container(leaf), name, namelen);
+ found = NULL;
+ found_equal = 0;
++ last = 1;
+
+ for (scan = n_start(leaf); scan < end; scan = e_next(leaf, scan)) {
+ lvar_hash_t scan_hash;
+ found = scan;
+ found_equal = 1;
+ }
-+ } else
++ } else {
++ last = 0;
+ break;
++ }
+ }
+ if (found == NULL) {
+ /*
+ result = IAM_LOOKUP_OK;
+ assert_corr(n_at_rec(leaf));
+ }
++ if (last)
++ result |= IAM_LOOKUP_LAST;
+ assert_inv(n_invariant(leaf));
+ return result;
+}
+ return e_cmp(l, n_cur(l), hash);
+}
+
++static int lvar_key_eq(const struct iam_leaf *l, const struct iam_key *k)
++{
++ const char *name;
++
++ name = kchar(k);
++ return e_eq(n_cur(l), name, strlen(name));
++}
++
+static void lvar_rec_set(struct iam_leaf *l, const struct iam_rec *r)
+{
+ assert_corr(n_at_rec(l));
+ .rec = lvar_rec,
+ .key_set = lvar_key_set,
+ .key_cmp = lvar_key_cmp,
++ .key_eq = lvar_key_eq,
+ .key_size = lvar_key_size,
+ .rec_set = lvar_rec_set,
+ .lookup = lvar_lookup,
void *il_descr_data;
};
-@@ -473,7 +485,7 @@ struct iam_path_compat {
+@@ -215,19 +227,23 @@ enum iam_lookup_t {
+ /*
+ * lookup found a record with the key requested
+ */
+- IAM_LOOKUP_EXACT,
++ IAM_LOOKUP_EXACT = 0,
+ /*
+ * lookup positioned leaf on some record
+ */
+- IAM_LOOKUP_OK,
++ IAM_LOOKUP_OK = 1,
+ /*
+ * leaf was empty
+ */
+- IAM_LOOKUP_EMPTY,
++ IAM_LOOKUP_EMPTY = 2,
+ /*
+ * lookup positioned leaf before first record
+ */
+- IAM_LOOKUP_BEFORE
++ IAM_LOOKUP_BEFORE = 3,
++ /*
++ * Found hash may have a continuation in the next leaf.
++ */
++ IAM_LOOKUP_LAST = 0x100
+ };
+
+ /*
+@@ -331,6 +347,7 @@ struct iam_leaf_operations {
+ void (*rec_set)(struct iam_leaf *l, const struct iam_rec *r);
+
+ int (*key_cmp)(const struct iam_leaf *l, const struct iam_key *k);
++ int (*key_eq)(const struct iam_leaf *l, const struct iam_key *k);
+
+ int (*key_size)(const struct iam_leaf *l);
+ /*
+@@ -473,7 +490,7 @@ struct iam_path_compat {
struct iam_container ipc_container;
__u32 ipc_scratch[DX_SCRATCH_KEYS];
struct dx_hash_info *ipc_hinfo;
struct iam_path_descr ipc_descr;
struct dx_hash_info ipc_hinfo_area;
};
-@@ -848,7 +860,9 @@ static inline struct iam_ikey *iam_path_
+@@ -848,7 +865,9 @@ static inline struct iam_ikey *iam_path_
return path->ip_data->ipd_key_scratch[nr];
}
void dx_insert_block(struct iam_path *path, struct iam_frame *frame,
u32 hash, u32 block);
int dx_index_is_compat(struct iam_path *path);
-@@ -858,7 +872,8 @@ int ext3_htree_next_block(struct inode *
+@@ -858,7 +877,8 @@ int ext3_htree_next_block(struct inode *
struct buffer_head *ext3_append(handle_t *handle, struct inode *inode,
u32 *block, int *err);
struct ext3_dir_entry_2 *split_entry(struct inode *dir,
struct ext3_dir_entry_2 *de,
unsigned long ino, mode_t mode,
-@@ -874,6 +889,10 @@ struct ext3_dir_entry_2 *move_entries(st
+@@ -874,6 +894,10 @@ struct ext3_dir_entry_2 *move_entries(st
extern struct iam_descr iam_htree_compat_param;