jbd2: Avoid possible NULL dereference in jbd2_journal_begin_ordered_truncate()
[safe/jmp/linux-2.6] / fs / ext4 / namei.c
index a1f72d2..ba702bd 100644 (file)
@@ -74,10 +74,6 @@ static struct buffer_head *ext4_append(handle_t *handle,
 #define assert(test) J_ASSERT(test)
 #endif
 
-#ifndef swap
-#define swap(x, y) do { typeof(x) z = x; x = y; y = z; } while (0)
-#endif
-
 #ifdef DX_DEBUG
 #define dxtrace(command) command
 #else
@@ -159,7 +155,7 @@ static void dx_set_count(struct dx_entry *entries, unsigned value);
 static void dx_set_limit(struct dx_entry *entries, unsigned value);
 static unsigned dx_root_limit(struct inode *dir, unsigned infosize);
 static unsigned dx_node_limit(struct inode *dir);
-static struct dx_frame *dx_probe(struct dentry *dentry,
+static struct dx_frame *dx_probe(const struct qstr *d_name,
                                 struct inode *dir,
                                 struct dx_hash_info *hinfo,
                                 struct dx_frame *frame,
@@ -177,8 +173,10 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
                                 struct dx_frame *frame,
                                 struct dx_frame *frames,
                                 __u32 *start_hash);
-static struct buffer_head * ext4_dx_find_entry(struct dentry *dentry,
-                      struct ext4_dir_entry_2 **res_dir, int *err);
+static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
+               const struct qstr *d_name,
+               struct ext4_dir_entry_2 **res_dir,
+               int *err);
 static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
                             struct inode *inode);
 
@@ -345,7 +343,7 @@ struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir,
  * back to userspace.
  */
 static struct dx_frame *
-dx_probe(struct dentry *dentry, struct inode *dir,
+dx_probe(const struct qstr *d_name, struct inode *dir,
         struct dx_hash_info *hinfo, struct dx_frame *frame_in, int *err)
 {
        unsigned count, indirect;
@@ -356,8 +354,6 @@ dx_probe(struct dentry *dentry, struct inode *dir,
        u32 hash;
 
        frame->bh = NULL;
-       if (dentry)
-               dir = dentry->d_parent->d_inode;
        if (!(bh = ext4_bread (NULL,dir, 0, 0, err)))
                goto fail;
        root = (struct dx_root *) bh->b_data;
@@ -372,9 +368,11 @@ dx_probe(struct dentry *dentry, struct inode *dir,
                goto fail;
        }
        hinfo->hash_version = root->info.hash_version;
+       if (hinfo->hash_version <= DX_HASH_TEA)
+               hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
        hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed;
-       if (dentry)
-               ext4fs_dirhash(dentry->d_name.name, dentry->d_name.len, hinfo);
+       if (d_name)
+               ext4fs_dirhash(d_name->name, d_name->len, hinfo);
        hash = hinfo->hash;
 
        if (root->info.unused_flags & 1) {
@@ -641,6 +639,9 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
        dir = dir_file->f_path.dentry->d_inode;
        if (!(EXT4_I(dir)->i_flags & EXT4_INDEX_FL)) {
                hinfo.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version;
+               if (hinfo.hash_version <= DX_HASH_TEA)
+                       hinfo.hash_version +=
+                               EXT4_SB(dir->i_sb)->s_hash_unsigned;
                hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
                count = htree_dirblock_to_tree(dir_file, dir, 0, &hinfo,
                                               start_hash, start_minor_hash);
@@ -649,7 +650,7 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
        }
        hinfo.hash = start_hash;
        hinfo.minor_hash = 0;
-       frame = dx_probe(NULL, dir_file->f_path.dentry->d_inode, &hinfo, frames, &err);
+       frame = dx_probe(NULL, dir, &hinfo, frames, &err);
        if (!frame)
                return err;
 
@@ -805,15 +806,15 @@ static inline int ext4_match (int len, const char * const name,
  */
 static inline int search_dirblock(struct buffer_head *bh,
                                  struct inode *dir,
-                                 struct dentry *dentry,
-                                 unsigned long offset,
+                                 const struct qstr *d_name,
+                                 unsigned int offset,
                                  struct ext4_dir_entry_2 ** res_dir)
 {
        struct ext4_dir_entry_2 * de;
        char * dlimit;
        int de_len;
-       const char *name = dentry->d_name.name;
-       int namelen = dentry->d_name.len;
+       const char *name = d_name->name;
+       int namelen = d_name->len;
 
        de = (struct ext4_dir_entry_2 *) bh->b_data;
        dlimit = bh->b_data + dir->i_sb->s_blocksize;
@@ -852,7 +853,8 @@ static inline int search_dirblock(struct buffer_head *bh,
  * The returned buffer_head has ->b_count elevated.  The caller is expected
  * to brelse() it when appropriate.
  */
-static struct buffer_head * ext4_find_entry (struct dentry *dentry,
+static struct buffer_head * ext4_find_entry (struct inode *dir,
+                                       const struct qstr *d_name,
                                        struct ext4_dir_entry_2 ** res_dir)
 {
        struct super_block *sb;
@@ -866,16 +868,15 @@ static struct buffer_head * ext4_find_entry (struct dentry *dentry,
        int num = 0;
        ext4_lblk_t  nblocks;
        int i, err;
-       struct inode *dir = dentry->d_parent->d_inode;
        int namelen;
 
        *res_dir = NULL;
        sb = dir->i_sb;
-       namelen = dentry->d_name.len;
+       namelen = d_name->len;
        if (namelen > EXT4_NAME_LEN)
                return NULL;
        if (is_dx(dir)) {
-               bh = ext4_dx_find_entry(dentry, res_dir, &err);
+               bh = ext4_dx_find_entry(dir, d_name, res_dir, &err);
                /*
                 * On success, or if the error was file not found,
                 * return.  Otherwise, fall back to doing a search the
@@ -928,7 +929,7 @@ restart:
                        brelse(bh);
                        goto next;
                }
-               i = search_dirblock(bh, dir, dentry,
+               i = search_dirblock(bh, dir, d_name,
                            block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);
                if (i == 1) {
                        EXT4_I(dir)->i_dir_start_lookup = block;
@@ -962,7 +963,7 @@ cleanup_and_exit:
        return ret;
 }
 
-static struct buffer_head * ext4_dx_find_entry(struct dentry *dentry,
+static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct qstr *d_name,
                       struct ext4_dir_entry_2 **res_dir, int *err)
 {
        struct super_block * sb;
@@ -973,14 +974,13 @@ static struct buffer_head * ext4_dx_find_entry(struct dentry *dentry,
        struct buffer_head *bh;
        ext4_lblk_t block;
        int retval;
-       int namelen = dentry->d_name.len;
-       const u8 *name = dentry->d_name.name;
-       struct inode *dir = dentry->d_parent->d_inode;
+       int namelen = d_name->len;
+       const u8 *name = d_name->name;
 
        sb = dir->i_sb;
        /* NFS may look up ".." - look at dx_root directory block */
        if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){
-               if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err)))
+               if (!(frame = dx_probe(d_name, dir, &hinfo, frames, err)))
                        return NULL;
        } else {
                frame = frames;
@@ -1041,14 +1041,14 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, stru
        if (dentry->d_name.len > EXT4_NAME_LEN)
                return ERR_PTR(-ENAMETOOLONG);
 
-       bh = ext4_find_entry(dentry, &de);
+       bh = ext4_find_entry(dir, &dentry->d_name, &de);
        inode = NULL;
        if (bh) {
-               unsigned long ino = le32_to_cpu(de->inode);
+               __u32 ino = le32_to_cpu(de->inode);
                brelse(bh);
                if (!ext4_valid_inum(dir->i_sb, ino)) {
                        ext4_error(dir->i_sb, "ext4_lookup",
-                                  "bad inode number: %lu", ino);
+                                  "bad inode number: %u", ino);
                        return ERR_PTR(-EIO);
                }
                inode = ext4_iget(dir->i_sb, ino);
@@ -1061,18 +1061,16 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, stru
 
 struct dentry *ext4_get_parent(struct dentry *child)
 {
-       unsigned long ino;
-       struct dentry *parent;
+       __u32 ino;
        struct inode *inode;
-       struct dentry dotdot;
+       static const struct qstr dotdot = {
+               .name = "..",
+               .len = 2,
+       };
        struct ext4_dir_entry_2 * de;
        struct buffer_head *bh;
 
-       dotdot.d_name.name = "..";
-       dotdot.d_name.len = 2;
-       dotdot.d_parent = child; /* confusing, isn't it! */
-
-       bh = ext4_find_entry(&dotdot, &de);
+       bh = ext4_find_entry(child->d_inode, &dotdot, &de);
        inode = NULL;
        if (!bh)
                return ERR_PTR(-ENOENT);
@@ -1081,20 +1079,11 @@ struct dentry *ext4_get_parent(struct dentry *child)
 
        if (!ext4_valid_inum(child->d_inode->i_sb, ino)) {
                ext4_error(child->d_inode->i_sb, "ext4_get_parent",
-                          "bad inode number: %lu", ino);
+                          "bad inode number: %u", ino);
                return ERR_PTR(-EIO);
        }
 
-       inode = ext4_iget(child->d_inode->i_sb, ino);
-       if (IS_ERR(inode))
-               return ERR_CAST(inode);
-
-       parent = d_alloc_anon(inode);
-       if (!parent) {
-               iput(inode);
-               parent = ERR_PTR(-ENOMEM);
-       }
-       return parent;
+       return d_obtain_alias(ext4_iget(child->d_inode->i_sb, ino));
 }
 
 #define S_SHIFT 12
@@ -1178,9 +1167,9 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
        u32 hash2;
        struct dx_map_entry *map;
        char *data1 = (*bh)->b_data, *data2;
-       unsigned split, move, size, i;
+       unsigned split, move, size;
        struct ext4_dir_entry_2 *de = NULL, *de2;
-       int     err = 0;
+       int     err = 0, i;
 
        bh2 = ext4_append (handle, dir, &newblock, &err);
        if (!(bh2)) {
@@ -1240,10 +1229,10 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
                de = de2;
        }
        dx_insert_block(frame, hash2 + continued, newblock);
-       err = ext4_journal_dirty_metadata(handle, bh2);
+       err = ext4_handle_dirty_metadata(handle, dir, bh2);
        if (err)
                goto journal_error;
-       err = ext4_journal_dirty_metadata(handle, frame->bh);
+       err = ext4_handle_dirty_metadata(handle, dir, frame->bh);
        if (err)
                goto journal_error;
        brelse(bh2);
@@ -1278,7 +1267,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
        struct inode    *dir = dentry->d_parent->d_inode;
        const char      *name = dentry->d_name.name;
        int             namelen = dentry->d_name.len;
-       unsigned long   offset = 0;
+       unsigned int    offset = 0;
        unsigned short  reclen;
        int             nlen, rlen, err;
        char            *top;
@@ -1347,8 +1336,8 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
        ext4_update_dx_flag(dir);
        dir->i_version++;
        ext4_mark_inode_dirty(handle, dir);
-       BUFFER_TRACE(bh, "call ext4_journal_dirty_metadata");
-       err = ext4_journal_dirty_metadata(handle, bh);
+       BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
+       err = ext4_handle_dirty_metadata(handle, dir, bh);
        if (err)
                ext4_std_error(dir->i_sb, err);
        brelse(bh);
@@ -1379,7 +1368,7 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
        struct fake_dirent *fde;
 
        blocksize =  dir->i_sb->s_blocksize;
-       dxtrace(printk(KERN_DEBUG "Creating index\n"));
+       dxtrace(printk(KERN_DEBUG "Creating index: inode %lu\n", dir->i_ino));
        retval = ext4_journal_get_write_access(handle, bh);
        if (retval) {
                ext4_std_error(dir->i_sb, retval);
@@ -1388,6 +1377,20 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
        }
        root = (struct dx_root *) bh->b_data;
 
+       /* The 0th block becomes the root, move the dirents out */
+       fde = &root->dotdot;
+       de = (struct ext4_dir_entry_2 *)((char *)fde +
+               ext4_rec_len_from_disk(fde->rec_len));
+       if ((char *) de >= (((char *) root) + blocksize)) {
+               ext4_error(dir->i_sb, __func__,
+                          "invalid rec_len for '..' in inode %lu",
+                          dir->i_ino);
+               brelse(bh);
+               return -EIO;
+       }
+       len = ((char *) root) + blocksize - (char *) de;
+
+       /* Allocate new block for the 0th block's dirents */
        bh2 = ext4_append(handle, dir, &block, &retval);
        if (!(bh2)) {
                brelse(bh);
@@ -1396,11 +1399,6 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
        EXT4_I(dir)->i_flags |= EXT4_INDEX_FL;
        data1 = bh2->b_data;
 
-       /* The 0th block becomes the root, move the dirents out */
-       fde = &root->dotdot;
-       de = (struct ext4_dir_entry_2 *)((char *)fde +
-               ext4_rec_len_from_disk(fde->rec_len));
-       len = ((char *) root) + blocksize - (char *) de;
        memcpy (data1, de, len);
        de = (struct ext4_dir_entry_2 *) data1;
        top = data1 + len;
@@ -1420,6 +1418,8 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
 
        /* Initialize as for dx_probe */
        hinfo.hash_version = root->info.hash_version;
+       if (hinfo.hash_version <= DX_HASH_TEA)
+               hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
        hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
        ext4fs_dirhash(name, namelen, &hinfo);
        frame = frames;
@@ -1449,7 +1449,6 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
                          struct inode *inode)
 {
        struct inode *dir = dentry->d_parent->d_inode;
-       unsigned long offset;
        struct buffer_head *bh;
        struct ext4_dir_entry_2 *de;
        struct super_block *sb;
@@ -1471,7 +1470,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
                ext4_mark_inode_dirty(handle, dir);
        }
        blocks = dir->i_size >> sb->s_blocksize_bits;
-       for (block = 0, offset = 0; block < blocks; block++) {
+       for (block = 0; block < blocks; block++) {
                bh = ext4_bread(handle, dir, block, 0, &retval);
                if(!bh)
                        return retval;
@@ -1508,7 +1507,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
        struct ext4_dir_entry_2 *de;
        int err;
 
-       frame = dx_probe(dentry, NULL, &hinfo, frames, &err);
+       frame = dx_probe(&dentry->d_name, dir, &hinfo, frames, &err);
        if (!frame)
                return err;
        entries = frame->entries;
@@ -1586,7 +1585,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
                        dxtrace(dx_show_index("node", frames[1].entries));
                        dxtrace(dx_show_index("node",
                               ((struct dx_node *) bh2->b_data)->entries));
-                       err = ext4_journal_dirty_metadata(handle, bh2);
+                       err = ext4_handle_dirty_metadata(handle, inode, bh2);
                        if (err)
                                goto journal_error;
                        brelse (bh2);
@@ -1612,7 +1611,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
                        if (err)
                                goto journal_error;
                }
-               ext4_journal_dirty_metadata(handle, frames[0].bh);
+               ext4_handle_dirty_metadata(handle, inode, frames[0].bh);
        }
        de = do_split(handle, dir, &bh, frame, &hinfo, &err);
        if (!de)
@@ -1658,8 +1657,8 @@ static int ext4_delete_entry(handle_t *handle,
                        else
                                de->inode = 0;
                        dir->i_version++;
-                       BUFFER_TRACE(bh, "call ext4_journal_dirty_metadata");
-                       ext4_journal_dirty_metadata(handle, bh);
+                       BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
+                       ext4_handle_dirty_metadata(handle, dir, bh);
                        return 0;
                }
                i += ext4_rec_len_from_disk(de->rec_len);
@@ -1705,9 +1704,11 @@ static int ext4_add_nondir(handle_t *handle,
        if (!err) {
                ext4_mark_inode_dirty(handle, inode);
                d_instantiate(dentry, inode);
+               unlock_new_inode(inode);
                return 0;
        }
        drop_nlink(inode);
+       unlock_new_inode(inode);
        iput(inode);
        return err;
 }
@@ -1735,7 +1736,7 @@ retry:
                return PTR_ERR(handle);
 
        if (IS_DIRSYNC(dir))
-               handle->h_sync = 1;
+               ext4_handle_sync(handle);
 
        inode = ext4_new_inode (handle, dir, mode);
        err = PTR_ERR(inode);
@@ -1769,13 +1770,13 @@ retry:
                return PTR_ERR(handle);
 
        if (IS_DIRSYNC(dir))
-               handle->h_sync = 1;
+               ext4_handle_sync(handle);
 
        inode = ext4_new_inode(handle, dir, mode);
        err = PTR_ERR(inode);
        if (!IS_ERR(inode)) {
                init_special_inode(inode, inode->i_mode, rdev);
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
                inode->i_op = &ext4_special_inode_operations;
 #endif
                err = ext4_add_nondir(handle, dentry, inode);
@@ -1805,7 +1806,7 @@ retry:
                return PTR_ERR(handle);
 
        if (IS_DIRSYNC(dir))
-               handle->h_sync = 1;
+               ext4_handle_sync(handle);
 
        inode = ext4_new_inode(handle, dir, S_IFDIR | mode);
        err = PTR_ERR(inode);
@@ -1834,14 +1835,15 @@ retry:
        strcpy(de->name, "..");
        ext4_set_de_type(dir->i_sb, de, S_IFDIR);
        inode->i_nlink = 2;
-       BUFFER_TRACE(dir_block, "call ext4_journal_dirty_metadata");
-       ext4_journal_dirty_metadata(handle, dir_block);
+       BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata");
+       ext4_handle_dirty_metadata(handle, dir, dir_block);
        brelse(dir_block);
        ext4_mark_inode_dirty(handle, inode);
        err = ext4_add_entry(handle, dentry, inode);
        if (err) {
 out_clear_inode:
                clear_nlink(inode);
+               unlock_new_inode(inode);
                ext4_mark_inode_dirty(handle, inode);
                iput(inode);
                goto out_stop;
@@ -1850,6 +1852,7 @@ out_clear_inode:
        ext4_update_dx_flag(dir);
        ext4_mark_inode_dirty(handle, dir);
        d_instantiate(dentry, inode);
+       unlock_new_inode(inode);
 out_stop:
        ext4_journal_stop(handle);
        if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
@@ -1862,7 +1865,7 @@ out_stop:
  */
 static int empty_dir(struct inode *inode)
 {
-       unsigned long offset;
+       unsigned int offset;
        struct buffer_head *bh;
        struct ext4_dir_entry_2 *de, *de1;
        struct super_block *sb;
@@ -1907,7 +1910,7 @@ static int empty_dir(struct inode *inode)
                                if (err)
                                        ext4_error(sb, __func__,
                                                   "error %d reading directory"
-                                                  " #%lu offset %lu",
+                                                  " #%lu offset %u",
                                                   err, inode->i_ino, offset);
                                offset += sb->s_blocksize;
                                continue;
@@ -1945,6 +1948,9 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
        struct ext4_iloc iloc;
        int err = 0, rc;
 
+       if (!ext4_handle_valid(handle))
+               return 0;
+
        lock_super(sb);
        if (!list_empty(&EXT4_I(inode)->i_orphan))
                goto out_unlock;
@@ -1973,7 +1979,7 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
        /* Insert this inode at the head of the on-disk orphan list... */
        NEXT_ORPHAN(inode) = le32_to_cpu(EXT4_SB(sb)->s_es->s_last_orphan);
        EXT4_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
-       err = ext4_journal_dirty_metadata(handle, EXT4_SB(sb)->s_sbh);
+       err = ext4_handle_dirty_metadata(handle, inode, EXT4_SB(sb)->s_sbh);
        rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
        if (!err)
                err = rc;
@@ -2007,10 +2013,13 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
        struct list_head *prev;
        struct ext4_inode_info *ei = EXT4_I(inode);
        struct ext4_sb_info *sbi;
-       unsigned long ino_next;
+       __u32 ino_next;
        struct ext4_iloc iloc;
        int err = 0;
 
+       if (!ext4_handle_valid(handle))
+               return 0;
+
        lock_super(inode->i_sb);
        if (list_empty(&ei->i_orphan)) {
                unlock_super(inode->i_sb);
@@ -2029,7 +2038,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
         * transaction handle with which to update the orphan list on
         * disk, but we still need to remove the inode from the linked
         * list in memory. */
-       if (!handle)
+       if (sbi->s_journal && !handle)
                goto out;
 
        err = ext4_reserve_inode_write(handle, inode, &iloc);
@@ -2037,19 +2046,19 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
                goto out_err;
 
        if (prev == &sbi->s_orphan) {
-               jbd_debug(4, "superblock will point to %lu\n", ino_next);
+               jbd_debug(4, "superblock will point to %u\n", ino_next);
                BUFFER_TRACE(sbi->s_sbh, "get_write_access");
                err = ext4_journal_get_write_access(handle, sbi->s_sbh);
                if (err)
                        goto out_brelse;
                sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
-               err = ext4_journal_dirty_metadata(handle, sbi->s_sbh);
+               err = ext4_handle_dirty_metadata(handle, inode, sbi->s_sbh);
        } else {
                struct ext4_iloc iloc2;
                struct inode *i_prev =
                        &list_entry(prev, struct ext4_inode_info, i_orphan)->vfs_inode;
 
-               jbd_debug(4, "orphan inode %lu will point to %lu\n",
+               jbd_debug(4, "orphan inode %lu will point to %u\n",
                          i_prev->i_ino, ino_next);
                err = ext4_reserve_inode_write(handle, i_prev, &iloc2);
                if (err)
@@ -2089,12 +2098,12 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
                return PTR_ERR(handle);
 
        retval = -ENOENT;
-       bh = ext4_find_entry(dentry, &de);
+       bh = ext4_find_entry(dir, &dentry->d_name, &de);
        if (!bh)
                goto end_rmdir;
 
        if (IS_DIRSYNC(dir))
-               handle->h_sync = 1;
+               ext4_handle_sync(handle);
 
        inode = dentry->d_inode;
 
@@ -2148,10 +2157,10 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
                return PTR_ERR(handle);
 
        if (IS_DIRSYNC(dir))
-               handle->h_sync = 1;
+               ext4_handle_sync(handle);
 
        retval = -ENOENT;
-       bh = ext4_find_entry(dentry, &de);
+       bh = ext4_find_entry(dir, &dentry->d_name, &de);
        if (!bh)
                goto end_unlink;
 
@@ -2205,7 +2214,7 @@ retry:
                return PTR_ERR(handle);
 
        if (IS_DIRSYNC(dir))
-               handle->h_sync = 1;
+               ext4_handle_sync(handle);
 
        inode = ext4_new_inode(handle, dir, S_IFLNK|S_IRWXUGO);
        err = PTR_ERR(inode);
@@ -2220,10 +2229,10 @@ retry:
                 * We have a transaction open.  All is sweetness.  It also sets
                 * i_size in generic_commit_write().
                 */
-               err = __page_symlink(inode, symname, l,
-                               mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS);
+               err = __page_symlink(inode, symname, l, 1);
                if (err) {
                        clear_nlink(inode);
+                       unlock_new_inode(inode);
                        ext4_mark_inode_dirty(handle, inode);
                        iput(inode);
                        goto out_stop;
@@ -2268,13 +2277,20 @@ retry:
                return PTR_ERR(handle);
 
        if (IS_DIRSYNC(dir))
-               handle->h_sync = 1;
+               ext4_handle_sync(handle);
 
        inode->i_ctime = ext4_current_time(inode);
        ext4_inc_count(handle, inode);
        atomic_inc(&inode->i_count);
 
-       err = ext4_add_nondir(handle, dentry, inode);
+       err = ext4_add_entry(handle, dentry, inode);
+       if (!err) {
+               ext4_mark_inode_dirty(handle, inode);
+               d_instantiate(dentry, inode);
+       } else {
+               drop_nlink(inode);
+               iput(inode);
+       }
        ext4_journal_stop(handle);
        if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
                goto retry;
@@ -2310,9 +2326,9 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
                return PTR_ERR(handle);
 
        if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
-               handle->h_sync = 1;
+               ext4_handle_sync(handle);
 
-       old_bh = ext4_find_entry(old_dentry, &old_de);
+       old_bh = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de);
        /*
         *  Check for inode number is _not_ due to possible IO errors.
         *  We might rmdir the source, keep it as pwd of some process
@@ -2325,7 +2341,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
                goto end_rename;
 
        new_inode = new_dentry->d_inode;
-       new_bh = ext4_find_entry(new_dentry, &new_de);
+       new_bh = ext4_find_entry(new_dir, &new_dentry->d_name, &new_de);
        if (new_bh) {
                if (!new_inode) {
                        brelse(new_bh);
@@ -2364,8 +2380,8 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
                new_dir->i_ctime = new_dir->i_mtime =
                                        ext4_current_time(new_dir);
                ext4_mark_inode_dirty(handle, new_dir);
-               BUFFER_TRACE(new_bh, "call ext4_journal_dirty_metadata");
-               ext4_journal_dirty_metadata(handle, new_bh);
+               BUFFER_TRACE(new_bh, "call ext4_handle_dirty_metadata");
+               ext4_handle_dirty_metadata(handle, new_dir, new_bh);
                brelse(new_bh);
                new_bh = NULL;
        }
@@ -2392,7 +2408,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
                struct buffer_head *old_bh2;
                struct ext4_dir_entry_2 *old_de2;
 
-               old_bh2 = ext4_find_entry(old_dentry, &old_de2);
+               old_bh2 = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de2);
                if (old_bh2) {
                        retval = ext4_delete_entry(handle, old_dir,
                                                   old_de2, old_bh2);
@@ -2415,8 +2431,8 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
                BUFFER_TRACE(dir_bh, "get_write_access");
                ext4_journal_get_write_access(handle, dir_bh);
                PARENT_INO(dir_bh->b_data) = cpu_to_le32(new_dir->i_ino);
-               BUFFER_TRACE(dir_bh, "call ext4_journal_dirty_metadata");
-               ext4_journal_dirty_metadata(handle, dir_bh);
+               BUFFER_TRACE(dir_bh, "call ext4_handle_dirty_metadata");
+               ext4_handle_dirty_metadata(handle, old_dir, dir_bh);
                ext4_dec_count(handle, old_dir);
                if (new_inode) {
                        /* checked empty_dir above, can't have another parent,
@@ -2458,7 +2474,7 @@ const struct inode_operations ext4_dir_inode_operations = {
        .mknod          = ext4_mknod,
        .rename         = ext4_rename,
        .setattr        = ext4_setattr,
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
        .setxattr       = generic_setxattr,
        .getxattr       = generic_getxattr,
        .listxattr      = ext4_listxattr,
@@ -2469,7 +2485,7 @@ const struct inode_operations ext4_dir_inode_operations = {
 
 const struct inode_operations ext4_special_inode_operations = {
        .setattr        = ext4_setattr,
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
        .setxattr       = generic_setxattr,
        .getxattr       = generic_getxattr,
        .listxattr      = ext4_listxattr,