return ret;
 }
 
+static int delete_conflicting_dir_entry(struct btrfs_trans_handle *trans,
+                                       struct btrfs_inode *dir,
+                                       struct btrfs_path *path,
+                                       struct btrfs_dir_item *dst_di,
+                                       const struct btrfs_key *log_key,
+                                       u8 log_type,
+                                       bool exists)
+{
+       struct btrfs_key found_key;
+
+       btrfs_dir_item_key_to_cpu(path->nodes[0], dst_di, &found_key);
+       /* The existing dentry points to the same inode, don't delete it. */
+       if (found_key.objectid == log_key->objectid &&
+           found_key.type == log_key->type &&
+           found_key.offset == log_key->offset &&
+           btrfs_dir_type(path->nodes[0], dst_di) == log_type)
+               return 1;
+
+       /*
+        * Don't drop the conflicting directory entry if the inode for the new
+        * entry doesn't exist.
+        */
+       if (!exists)
+               return 0;
+
+       return drop_one_dir_item(trans, path, dir, dst_di);
+}
+
 /*
  * take a single entry in a log directory item and replay it into
  * the subvolume.
 {
        char *name;
        int name_len;
-       struct btrfs_dir_item *dst_di;
-       struct btrfs_key found_key;
+       struct btrfs_dir_item *dir_dst_di;
+       struct btrfs_dir_item *index_dst_di;
+       bool dir_dst_matches = false;
+       bool index_dst_matches = false;
        struct btrfs_key log_key;
+       struct btrfs_key search_key;
        struct inode *dir;
        u8 log_type;
        bool exists;
        int ret;
-       bool update_size = (key->type == BTRFS_DIR_INDEX_KEY);
+       bool update_size = true;
        bool name_added = false;
 
        dir = read_one_inode(root, key->objectid);
        exists = (ret == 0);
        ret = 0;
 
-       if (key->type == BTRFS_DIR_ITEM_KEY) {
-               dst_di = btrfs_lookup_dir_item(trans, root, path, key->objectid,
-                                      name, name_len, 1);
-       } else if (key->type == BTRFS_DIR_INDEX_KEY) {
-               dst_di = btrfs_lookup_dir_index_item(trans, root, path,
-                                                    key->objectid,
-                                                    key->offset, name,
-                                                    name_len, 1);
-       } else {
-               /* Corruption */
-               ret = -EINVAL;
+       dir_dst_di = btrfs_lookup_dir_item(trans, root, path, key->objectid,
+                                          name, name_len, 1);
+       if (IS_ERR(dir_dst_di)) {
+               ret = PTR_ERR(dir_dst_di);
                goto out;
+       } else if (dir_dst_di) {
+               ret = delete_conflicting_dir_entry(trans, BTRFS_I(dir), path,
+                                                  dir_dst_di, &log_key, log_type,
+                                                  exists);
+               if (ret < 0)
+                       goto out;
+               dir_dst_matches = (ret == 1);
        }
 
-       if (IS_ERR(dst_di)) {
-               ret = PTR_ERR(dst_di);
+       btrfs_release_path(path);
+
+       index_dst_di = btrfs_lookup_dir_index_item(trans, root, path,
+                                                  key->objectid, key->offset,
+                                                  name, name_len, 1);
+       if (IS_ERR(index_dst_di)) {
+               ret = PTR_ERR(index_dst_di);
                goto out;
-       } else if (!dst_di) {
-               /* we need a sequence number to insert, so we only
-                * do inserts for the BTRFS_DIR_INDEX_KEY types
-                */
-               if (key->type != BTRFS_DIR_INDEX_KEY)
+       } else if (index_dst_di) {
+               ret = delete_conflicting_dir_entry(trans, BTRFS_I(dir), path,
+                                                  index_dst_di, &log_key,
+                                                  log_type, exists);
+               if (ret < 0)
                        goto out;
-               goto insert;
+               index_dst_matches = (ret == 1);
        }
 
-       btrfs_dir_item_key_to_cpu(path->nodes[0], dst_di, &found_key);
-       /* the existing item matches the logged item */
-       if (found_key.objectid == log_key.objectid &&
-           found_key.type == log_key.type &&
-           found_key.offset == log_key.offset &&
-           btrfs_dir_type(path->nodes[0], dst_di) == log_type) {
+       btrfs_release_path(path);
+
+       if (dir_dst_matches && index_dst_matches) {
+               ret = 0;
                update_size = false;
                goto out;
        }
 
-       /*
-        * don't drop the conflicting directory entry if the inode
-        * for the new entry doesn't exist
-        */
-       if (!exists)
-               goto out;
-
-       ret = drop_one_dir_item(trans, path, BTRFS_I(dir), dst_di);
-       if (ret)
-               goto out;
-
-       if (key->type == BTRFS_DIR_INDEX_KEY)
-               goto insert;
-out:
-       btrfs_release_path(path);
-       if (!ret && update_size) {
-               btrfs_i_size_write(BTRFS_I(dir), dir->i_size + name_len * 2);
-               ret = btrfs_update_inode(trans, root, BTRFS_I(dir));
-       }
-       kfree(name);
-       iput(dir);
-       if (!ret && name_added)
-               ret = 1;
-       return ret;
-
-insert:
        /*
         * Check if the inode reference exists in the log for the given name,
         * inode and parent inode
         */
-       found_key.objectid = log_key.objectid;
-       found_key.type = BTRFS_INODE_REF_KEY;
-       found_key.offset = key->objectid;
-       ret = backref_in_log(root->log_root, &found_key, 0, name, name_len);
+       search_key.objectid = log_key.objectid;
+       search_key.type = BTRFS_INODE_REF_KEY;
+       search_key.offset = key->objectid;
+       ret = backref_in_log(root->log_root, &search_key, 0, name, name_len);
        if (ret < 0) {
                goto out;
        } else if (ret) {
                goto out;
        }
 
-       found_key.objectid = log_key.objectid;
-       found_key.type = BTRFS_INODE_EXTREF_KEY;
-       found_key.offset = key->objectid;
-       ret = backref_in_log(root->log_root, &found_key, key->objectid, name,
+       search_key.objectid = log_key.objectid;
+       search_key.type = BTRFS_INODE_EXTREF_KEY;
+       search_key.offset = key->objectid;
+       ret = backref_in_log(root->log_root, &search_key, key->objectid, name,
                             name_len);
        if (ret < 0) {
                goto out;
                name_added = true;
        update_size = false;
        ret = 0;
-       goto out;
+
+out:
+       if (!ret && update_size) {
+               btrfs_i_size_write(BTRFS_I(dir), dir->i_size + name_len * 2);
+               ret = btrfs_update_inode(trans, root, BTRFS_I(dir));
+       }
+       kfree(name);
+       iput(dir);
+       if (!ret && name_added)
+               ret = 1;
+       return ret;
 }
 
-/*
- * find all the names in a directory item and reconcile them into
- * the subvolume.  Only BTRFS_DIR_ITEM_KEY types will have more than
- * one name in a directory item, but the same code gets used for
- * both directory index types
- */
+/* Replay one dir item from a BTRFS_DIR_INDEX_KEY key. */
 static noinline int replay_one_dir_item(struct btrfs_trans_handle *trans,
                                        struct btrfs_root *root,
                                        struct btrfs_path *path,
                                        struct extent_buffer *eb, int slot,
                                        struct btrfs_key *key)
 {
-       int ret = 0;
-       u32 item_size = btrfs_item_size_nr(eb, slot);
+       int ret;
        struct btrfs_dir_item *di;
-       int name_len;
-       unsigned long ptr;
-       unsigned long ptr_end;
-       struct btrfs_path *fixup_path = NULL;
 
-       ptr = btrfs_item_ptr_offset(eb, slot);
-       ptr_end = ptr + item_size;
-       while (ptr < ptr_end) {
-               di = (struct btrfs_dir_item *)ptr;
-               name_len = btrfs_dir_name_len(eb, di);
-               ret = replay_one_name(trans, root, path, eb, di, key);
-               if (ret < 0)
-                       break;
-               ptr = (unsigned long)(di + 1);
-               ptr += name_len;
+       /* We only log dir index keys, which only contain a single dir item. */
+       ASSERT(key->type == BTRFS_DIR_INDEX_KEY);
 
-               /*
-                * If this entry refers to a non-directory (directories can not
-                * have a link count > 1) and it was added in the transaction
-                * that was not committed, make sure we fixup the link count of
-                * the inode it the entry points to. Otherwise something like
-                * the following would result in a directory pointing to an
-                * inode with a wrong link that does not account for this dir
-                * entry:
-                *
-                * mkdir testdir
-                * touch testdir/foo
-                * touch testdir/bar
-                * sync
-                *
-                * ln testdir/bar testdir/bar_link
-                * ln testdir/foo testdir/foo_link
-                * xfs_io -c "fsync" testdir/bar
-                *
-                * <power failure>
-                *
-                * mount fs, log replay happens
-                *
-                * File foo would remain with a link count of 1 when it has two
-                * entries pointing to it in the directory testdir. This would
-                * make it impossible to ever delete the parent directory has
-                * it would result in stale dentries that can never be deleted.
-                */
-               if (ret == 1 && btrfs_dir_type(eb, di) != BTRFS_FT_DIR) {
-                       struct btrfs_key di_key;
+       di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
+       ret = replay_one_name(trans, root, path, eb, di, key);
+       if (ret < 0)
+               return ret;
 
-                       if (!fixup_path) {
-                               fixup_path = btrfs_alloc_path();
-                               if (!fixup_path) {
-                                       ret = -ENOMEM;
-                                       break;
-                               }
-                       }
+       /*
+        * If this entry refers to a non-directory (directories can not have a
+        * link count > 1) and it was added in the transaction that was not
+        * committed, make sure we fixup the link count of the inode the entry
+        * points to. Otherwise something like the following would result in a
+        * directory pointing to an inode with a wrong link that does not account
+        * for this dir entry:
+        *
+        * mkdir testdir
+        * touch testdir/foo
+        * touch testdir/bar
+        * sync
+        *
+        * ln testdir/bar testdir/bar_link
+        * ln testdir/foo testdir/foo_link
+        * xfs_io -c "fsync" testdir/bar
+        *
+        * <power failure>
+        *
+        * mount fs, log replay happens
+        *
+        * File foo would remain with a link count of 1 when it has two entries
+        * pointing to it in the directory testdir. This would make it impossible
+        * to ever delete the parent directory has it would result in stale
+        * dentries that can never be deleted.
+        */
+       if (ret == 1 && btrfs_dir_type(eb, di) != BTRFS_FT_DIR) {
+               struct btrfs_path *fixup_path;
+               struct btrfs_key di_key;
 
-                       btrfs_dir_item_key_to_cpu(eb, di, &di_key);
-                       ret = link_to_fixup_dir(trans, root, fixup_path,
-                                               di_key.objectid);
-                       if (ret)
-                               break;
-               }
-               ret = 0;
+               fixup_path = btrfs_alloc_path();
+               if (!fixup_path)
+                       return -ENOMEM;
+
+               btrfs_dir_item_key_to_cpu(eb, di, &di_key);
+               ret = link_to_fixup_dir(trans, root, fixup_path, di_key.objectid);
+               btrfs_free_path(fixup_path);
        }
-       btrfs_free_path(fixup_path);
+
        return ret;
 }
 
                                                eb, i, &key);
                        if (ret)
                                break;
-               } else if (key.type == BTRFS_DIR_ITEM_KEY) {
-                       ret = replay_one_dir_item(wc->trans, root, path,
-                                                 eb, i, &key);
-                       if (ret)
-                               break;
                }
+               /*
+                * We don't log BTRFS_DIR_ITEM_KEY keys anymore, only the
+                * BTRFS_DIR_INDEX_KEY items which we use to derive the
+                * BTRFS_DIR_ITEM_KEY items. If we are replaying a log from an
+                * older kernel with such keys, ignore them.
+                */
        }
        btrfs_free_path(path);
        return ret;
                goto out_unlock;
        }
 
-       di = btrfs_lookup_dir_item(trans, log, path, dir_ino,
-                                  name, name_len, -1);
-       if (IS_ERR(di)) {
-               err = PTR_ERR(di);
-               goto fail;
-       }
-       if (di) {
-               ret = btrfs_delete_one_dir_name(trans, log, path, di);
-               if (ret) {
-                       err = ret;
-                       goto fail;
-               }
-       }
-       btrfs_release_path(path);
+       /*
+        * We only log dir index items of a directory, so we don't need to look
+        * for dir item keys.
+        */
        di = btrfs_lookup_dir_index_item(trans, log, path, dir_ino,
                                         index, name, name_len, -1);
        if (IS_ERR(di)) {
 static noinline int insert_dir_log_key(struct btrfs_trans_handle *trans,
                                       struct btrfs_root *log,
                                       struct btrfs_path *path,
-                                      int key_type, u64 dirid,
+                                      u64 dirid,
                                       u64 first_offset, u64 last_offset)
 {
        int ret;
 
        key.objectid = dirid;
        key.offset = first_offset;
-       if (key_type == BTRFS_DIR_ITEM_KEY)
-               key.type = BTRFS_DIR_LOG_ITEM_KEY;
-       else
-               key.type = BTRFS_DIR_LOG_INDEX_KEY;
+       key.type = BTRFS_DIR_LOG_INDEX_KEY;
        ret = btrfs_insert_empty_item(trans, log, path, &key, sizeof(*item));
        if (ret)
                return ret;
                                  struct btrfs_inode *inode,
                                  struct btrfs_path *path,
                                  struct btrfs_path *dst_path,
-                                 int key_type,
                                  struct btrfs_log_ctx *ctx)
 {
        struct btrfs_root *log = inode->root->log_root;
        const int nritems = btrfs_header_nritems(src);
        const u64 ino = btrfs_ino(inode);
        const bool inode_logged_before = inode_logged(trans, inode);
-       u64 last_logged_key_offset;
        bool last_found = false;
        int batch_start = 0;
        int batch_size = 0;
        int i;
 
-       if (key_type == BTRFS_DIR_ITEM_KEY)
-               last_logged_key_offset = inode->last_dir_item_offset;
-       else
-               last_logged_key_offset = inode->last_dir_index_offset;
-
        for (i = path->slots[0]; i < nritems; i++) {
                struct btrfs_key key;
                int ret;
 
                btrfs_item_key_to_cpu(src, &key, i);
 
-               if (key.objectid != ino || key.type != key_type) {
+               if (key.objectid != ino || key.type != BTRFS_DIR_INDEX_KEY) {
                        last_found = true;
                        break;
                }
                 * we logged is in the log tree, saving time and avoiding adding
                 * contention on the log tree.
                 */
-               if (key.offset > last_logged_key_offset)
+               if (key.offset > inode->last_dir_index_offset)
                        goto add_to_batch;
                /*
                 * Check if the key was already logged before. If not we can add
 static noinline int log_dir_items(struct btrfs_trans_handle *trans,
                          struct btrfs_inode *inode,
                          struct btrfs_path *path,
-                         struct btrfs_path *dst_path, int key_type,
+                         struct btrfs_path *dst_path,
                          struct btrfs_log_ctx *ctx,
                          u64 min_offset, u64 *last_offset_ret)
 {
        u64 ino = btrfs_ino(inode);
 
        min_key.objectid = ino;
-       min_key.type = key_type;
+       min_key.type = BTRFS_DIR_INDEX_KEY;
        min_key.offset = min_offset;
 
        ret = btrfs_search_forward(root, &min_key, path, trans->transid);
         * we didn't find anything from this transaction, see if there
         * is anything at all
         */
-       if (ret != 0 || min_key.objectid != ino || min_key.type != key_type) {
+       if (ret != 0 || min_key.objectid != ino ||
+           min_key.type != BTRFS_DIR_INDEX_KEY) {
                min_key.objectid = ino;
-               min_key.type = key_type;
+               min_key.type = BTRFS_DIR_INDEX_KEY;
                min_key.offset = (u64)-1;
                btrfs_release_path(path);
                ret = btrfs_search_slot(NULL, root, &min_key, path, 0, 0);
                        btrfs_release_path(path);
                        return ret;
                }
-               ret = btrfs_previous_item(root, path, ino, key_type);
+               ret = btrfs_previous_item(root, path, ino, BTRFS_DIR_INDEX_KEY);
 
                /* if ret == 0 there are items for this type,
                 * create a range to tell us the last key of this type.
                        struct btrfs_key tmp;
                        btrfs_item_key_to_cpu(path->nodes[0], &tmp,
                                              path->slots[0]);
-                       if (key_type == tmp.type)
+                       if (tmp.type == BTRFS_DIR_INDEX_KEY)
                                first_offset = max(min_offset, tmp.offset) + 1;
                }
                goto done;
        }
 
        /* go backward to find any previous key */
-       ret = btrfs_previous_item(root, path, ino, key_type);
+       ret = btrfs_previous_item(root, path, ino, BTRFS_DIR_INDEX_KEY);
        if (ret == 0) {
                struct btrfs_key tmp;
                btrfs_item_key_to_cpu(path->nodes[0], &tmp, path->slots[0]);
-               if (key_type == tmp.type) {
+               if (tmp.type == BTRFS_DIR_INDEX_KEY) {
                        first_offset = tmp.offset;
                        ret = overwrite_item(trans, log, dst_path,
                                             path->nodes[0], path->slots[0],
         * from our directory
         */
        while (1) {
-               ret = process_dir_items_leaf(trans, inode, path, dst_path,
-                                            key_type, ctx);
+               ret = process_dir_items_leaf(trans, inode, path, dst_path, ctx);
                if (ret != 0) {
                        if (ret < 0)
                                err = ret;
                        goto done;
                }
                btrfs_item_key_to_cpu(path->nodes[0], &min_key, path->slots[0]);
-               if (min_key.objectid != ino || min_key.type != key_type) {
+               if (min_key.objectid != ino || min_key.type != BTRFS_DIR_INDEX_KEY) {
                        last_offset = (u64)-1;
                        goto done;
                }
                 * insert the log range keys to indicate where the log
                 * is valid
                 */
-               ret = insert_dir_log_key(trans, log, path, key_type,
-                                        ino, first_offset, last_offset);
+               ret = insert_dir_log_key(trans, log, path, ino, first_offset,
+                                        last_offset);
                if (ret)
                        err = ret;
        }
        u64 min_key;
        u64 max_key;
        int ret;
-       int key_type = BTRFS_DIR_ITEM_KEY;
 
        /*
         * If this is the first time we are being logged in the current
         * transaction, or we were logged before but the inode was evicted and
-        * reloaded later, in which case its logged_trans is 0, reset the values
-        * of the last logged key offsets. Note that we don't use the helper
+        * reloaded later, in which case its logged_trans is 0, reset the value
+        * of the last logged key offset. Note that we don't use the helper
         * function inode_logged() here - that is because the function returns
         * true after an inode eviction, assuming the worst case as it can not
         * know for sure if the inode was logged before. So we can not skip key
         * searches in the case the inode was evicted, because it may not have
         * been logged in this transaction and may have been logged in a past
-        * transaction, so we need to reset the last dir item and index offsets
-        * to (u64)-1.
+        * transaction, so we need to reset the last dir index offset to (u64)-1.
         */
-       if (inode->logged_trans != trans->transid) {
-               inode->last_dir_item_offset = (u64)-1;
+       if (inode->logged_trans != trans->transid)
                inode->last_dir_index_offset = (u64)-1;
-       }
-again:
+
        min_key = 0;
        max_key = 0;
-       if (key_type == BTRFS_DIR_ITEM_KEY)
-               ctx->last_dir_item_offset = inode->last_dir_item_offset;
-       else
-               ctx->last_dir_item_offset = inode->last_dir_index_offset;
+       ctx->last_dir_item_offset = inode->last_dir_index_offset;
 
        while (1) {
-               ret = log_dir_items(trans, inode, path, dst_path, key_type,
+               ret = log_dir_items(trans, inode, path, dst_path,
                                ctx, min_key, &max_key);
                if (ret)
                        return ret;
                min_key = max_key + 1;
        }
 
-       if (key_type == BTRFS_DIR_ITEM_KEY) {
-               inode->last_dir_item_offset = ctx->last_dir_item_offset;
-               key_type = BTRFS_DIR_INDEX_KEY;
-               goto again;
-       } else {
-               inode->last_dir_index_offset = ctx->last_dir_item_offset;
-       }
+       inode->last_dir_index_offset = ctx->last_dir_item_offset;
+
        return 0;
 }
 
  *    link_to_fixup_dir());
  *
  * 2) For directories we log with a mode of LOG_INODE_ALL. It's possible that
- *    while logging the inode's items new items with keys BTRFS_DIR_ITEM_KEY and
- *    BTRFS_DIR_INDEX_KEY are added to fs/subvol tree and the logged inode item
+ *    while logging the inode's items new index items (key type
+ *    BTRFS_DIR_INDEX_KEY) are added to fs/subvol tree and the logged inode item
  *    has a size that doesn't match the sum of the lengths of all the logged
- *    names. This does not result in a problem because if a dir_item key is
- *    logged but its matching dir_index key is not logged, at log replay time we
- *    don't use it to replay the respective name (see replay_one_name()). On the
- *    other hand if only the dir_index key ends up being logged, the respective
- *    name is added to the fs/subvol tree with both the dir_item and dir_index
- *    keys created (see replay_one_name()).
- *    The directory's inode item with a wrong i_size is not a problem as well,
- *    since we don't use it at log replay time to set the i_size in the inode
- *    item of the fs/subvol tree (see overwrite_item()).
+ *    names - this is ok, not a problem, because at log replay time we set the
+ *    directory's i_size to the correct value (see replay_one_name() and
+ *    do_overwrite_item()).
  */
 static int log_new_dir_dentries(struct btrfs_trans_handle *trans,
                                struct btrfs_root *root,
                        goto next_dir_inode;
 
                min_key.objectid = dir_elem->ino;
-               min_key.type = BTRFS_DIR_ITEM_KEY;
+               min_key.type = BTRFS_DIR_INDEX_KEY;
                min_key.offset = 0;
 again:
                btrfs_release_path(path);
 
                        btrfs_item_key_to_cpu(leaf, &min_key, i);
                        if (min_key.objectid != dir_elem->ino ||
-                           min_key.type != BTRFS_DIR_ITEM_KEY)
+                           min_key.type != BTRFS_DIR_INDEX_KEY)
                                goto next_dir_inode;
 
                        di = btrfs_item_ptr(leaf, i, struct btrfs_dir_item);
         * was previously logged, make sure the next log attempt on the directory
         * is not skipped and logs the inode again. This is because the log may
         * not currently be authoritative for a range including the old
-        * BTRFS_DIR_ITEM_KEY and BTRFS_DIR_INDEX_KEY keys, so we want to make
-        * sure after a log replay we do not end up with both the new and old
-        * dentries around (in case the inode is a directory we would have a
-        * directory with two hard links and 2 inode references for different
-        * parents). The next log attempt of old_dir will happen at
-        * btrfs_log_all_parents(), called through btrfs_log_inode_parent()
-        * below, because we have previously set inode->last_unlink_trans to the
-        * current transaction ID, either here or at btrfs_record_unlink_dir() in
-        * case inode is a directory.
+        * BTRFS_DIR_INDEX_KEY key, so we want to make sure after a log replay we
+        * do not end up with both the new and old dentries around (in case the
+        * inode is a directory we would have a directory with two hard links and
+        * 2 inode references for different parents). The next log attempt of
+        * old_dir will happen at btrfs_log_all_parents(), called through
+        * btrfs_log_inode_parent() below, because we have previously set
+        * inode->last_unlink_trans to the current transaction ID, either here or
+        * at btrfs_record_unlink_dir() in case the inode is a directory.
         */
        if (old_dir)
                old_dir->logged_trans = 0;