fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / ext3 / namei.c
index 6ed3d4f..2a3e771 100644 (file)
  *  Big-endian to little-endian byte-swapping/bitmaps by
  *        David S. Miller (davem@caip.rutgers.edu), 1995
  *  Directory entry file type support and forward compatibility hooks
- *     for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998
+ *     for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998
  *  Hash Tree Directory indexing (c)
- *     Daniel Phillips, 2001
+ *     Daniel Phillips, 2001
  *  Hash Tree Directory indexing porting
- *     Christopher Li, 2002
+ *     Christopher Li, 2002
  *  Hash Tree Directory indexing cleanup
- *     Theodore Ts'o, 2002
+ *     Theodore Ts'o, 2002
  */
 
 #include <linux/fs.h>
 #include <linux/string.h>
 #include <linux/quotaops.h>
 #include <linux/buffer_head.h>
+#include <linux/bio.h>
 #include <linux/smp_lock.h>
+#include <linux/vs_tag.h>
+
+#include "namei.h"
 #include "xattr.h"
 #include "acl.h"
 
@@ -71,33 +75,30 @@ static struct buffer_head *ext3_append(handle_t *handle,
 #define swap(x, y) do { typeof(x) z = x; x = y; y = z; } while (0)
 #endif
 
-typedef struct { u32 v; } le_u32;
-typedef struct { u16 v; } le_u16;
-
 #ifdef DX_DEBUG
 #define dxtrace(command) command
 #else
-#define dxtrace(command) 
+#define dxtrace(command)
 #endif
 
 struct fake_dirent
 {
-       /*le*/u32 inode;
-       /*le*/u16 rec_len;
+       __le32 inode;
+       __le16 rec_len;
        u8 name_len;
        u8 file_type;
 };
 
 struct dx_countlimit
 {
-       le_u16 limit;
-       le_u16 count;
+       __le16 limit;
+       __le16 count;
 };
 
 struct dx_entry
 {
-       le_u32 hash;
-       le_u32 block;
+       __le32 hash;
+       __le32 block;
 };
 
 /*
@@ -114,7 +115,7 @@ struct dx_root
        char dotdot_name[4];
        struct dx_root_info
        {
-               le_u32 reserved_zero;
+               __le32 reserved_zero;
                u8 hash_version;
                u8 info_length; /* 8 */
                u8 indirect_levels;
@@ -170,7 +171,7 @@ static struct ext3_dir_entry_2* dx_pack_dirents (char *base, int size);
 static void dx_insert_block (struct dx_frame *frame, u32 hash, u32 block);
 static int ext3_htree_next_block(struct inode *dir, __u32 hash,
                                 struct dx_frame *frame,
-                                struct dx_frame *frames, 
+                                struct dx_frame *frames,
                                 __u32 *start_hash);
 static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry,
                       struct ext3_dir_entry_2 **res_dir, int *err);
@@ -184,42 +185,42 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,
 
 static inline unsigned dx_get_block (struct dx_entry *entry)
 {
-       return le32_to_cpu(entry->block.v) & 0x00ffffff;
+       return le32_to_cpu(entry->block) & 0x00ffffff;
 }
 
 static inline void dx_set_block (struct dx_entry *entry, unsigned value)
 {
-       entry->block.v = cpu_to_le32(value);
+       entry->block = cpu_to_le32(value);
 }
 
 static inline unsigned dx_get_hash (struct dx_entry *entry)
 {
-       return le32_to_cpu(entry->hash.v);
+       return le32_to_cpu(entry->hash);
 }
 
 static inline void dx_set_hash (struct dx_entry *entry, unsigned value)
 {
-       entry->hash.v = cpu_to_le32(value);
+       entry->hash = cpu_to_le32(value);
 }
 
 static inline unsigned dx_get_count (struct dx_entry *entries)
 {
-       return le16_to_cpu(((struct dx_countlimit *) entries)->count.v);
+       return le16_to_cpu(((struct dx_countlimit *) entries)->count);
 }
 
 static inline unsigned dx_get_limit (struct dx_entry *entries)
 {
-       return le16_to_cpu(((struct dx_countlimit *) entries)->limit.v);
+       return le16_to_cpu(((struct dx_countlimit *) entries)->limit);
 }
 
 static inline void dx_set_count (struct dx_entry *entries, unsigned value)
 {
-       ((struct dx_countlimit *) entries)->count.v = cpu_to_le16(value);
+       ((struct dx_countlimit *) entries)->count = cpu_to_le16(value);
 }
 
 static inline void dx_set_limit (struct dx_entry *entries, unsigned value)
 {
-       ((struct dx_countlimit *) entries)->limit.v = cpu_to_le16(value);
+       ((struct dx_countlimit *) entries)->limit = cpu_to_le16(value);
 }
 
 static inline unsigned dx_root_limit (struct inode *dir, unsigned infosize)
@@ -251,7 +252,7 @@ static void dx_show_index (char * label, struct dx_entry *entries)
 }
 
 struct stats
-{ 
+{
        unsigned names;
        unsigned space;
        unsigned bcount;
@@ -279,7 +280,7 @@ static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext3_dir_ent
                                       ((char *) de - base));
                        }
                        space += EXT3_DIR_REC_LEN(de->name_len);
-                       names++;
+                       names++;
                }
                de = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
        }
@@ -465,7 +466,7 @@ static void dx_release (struct dx_frame *frames)
  */
 static int ext3_htree_next_block(struct inode *dir, __u32 hash,
                                 struct dx_frame *frame,
-                                struct dx_frame *frames, 
+                                struct dx_frame *frames,
                                 __u32 *start_hash)
 {
        struct dx_frame *p;
@@ -552,11 +553,22 @@ static int htree_dirblock_to_tree(struct file *dir_file,
                                           dir->i_sb->s_blocksize -
                                           EXT3_DIR_REC_LEN(0));
        for (; de < top; de = ext3_next_entry(de)) {
+               if (!ext3_check_dir_entry("htree_dirblock_to_tree", dir, de, bh,
+                                       (block<<EXT3_BLOCK_SIZE_BITS(dir->i_sb))
+                                               +((char *)de - bh->b_data))) {
+                       /* On error, skip the f_pos to the next block. */
+                       dir_file->f_pos = (dir_file->f_pos |
+                                       (dir->i_sb->s_blocksize - 1)) + 1;
+                       brelse (bh);
+                       return count;
+               }
                ext3fs_dirhash(de->name, de->name_len, hinfo);
                if ((hinfo->hash < start_hash) ||
                    ((hinfo->hash == start_hash) &&
                     (hinfo->minor_hash < start_minor_hash)))
                        continue;
+               if (de->inode == 0)
+                       continue;
                if ((err = ext3_htree_store_dirent(dir_file,
                                   hinfo->hash, hinfo->minor_hash, de)) != 0) {
                        brelse(bh);
@@ -591,7 +603,7 @@ int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash,
 
        dxtrace(printk("In htree_fill_tree, start hash: %x:%x\n", start_hash,
                       start_minor_hash));
-       dir = dir_file->f_dentry->d_inode;
+       dir = dir_file->f_path.dentry->d_inode;
        if (!(EXT3_I(dir)->i_flags & EXT3_INDEX_FL)) {
                hinfo.hash_version = EXT3_SB(dir->i_sb)->s_def_hash_version;
                hinfo.seed = EXT3_SB(dir->i_sb)->s_hash_seed;
@@ -602,7 +614,7 @@ int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash,
        }
        hinfo.hash = start_hash;
        hinfo.minor_hash = 0;
-       frame = dx_probe(0, dir_file->f_dentry->d_inode, &hinfo, frames, &err);
+       frame = dx_probe(NULL, dir_file->f_path.dentry->d_inode, &hinfo, frames, &err);
        if (!frame)
                return err;
 
@@ -611,10 +623,14 @@ int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash,
                de = (struct ext3_dir_entry_2 *) frames[0].bh->b_data;
                if ((err = ext3_htree_store_dirent(dir_file, 0, 0, de)) != 0)
                        goto errout;
+               count++;
+       }
+       if (start_hash < 2 || (start_hash ==2 && start_minor_hash==0)) {
+               de = (struct ext3_dir_entry_2 *) frames[0].bh->b_data;
                de = ext3_next_entry(de);
-               if ((err = ext3_htree_store_dirent(dir_file, 0, 0, de)) != 0)
+               if ((err = ext3_htree_store_dirent(dir_file, 2, 0, de)) != 0)
                        goto errout;
-               count += 2;
+               count++;
        }
 
        while (1) {
@@ -627,7 +643,7 @@ int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash,
                }
                count += ret;
                hashval = ~0;
-               ret = ext3_htree_next_block(dir, HASH_NB_ALWAYS, 
+               ret = ext3_htree_next_block(dir, HASH_NB_ALWAYS,
                                            frame, frames, &hashval);
                *next_hash = hashval;
                if (ret < 0) {
@@ -644,7 +660,7 @@ int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash,
                        break;
        }
        dx_release(frames);
-       dxtrace(printk("Fill tree: returned %d entries, next hash: %x\n", 
+       dxtrace(printk("Fill tree: returned %d entries, next hash: %x\n",
                       count, *next_hash));
        return count;
 errout:
@@ -672,6 +688,7 @@ static int dx_make_map (struct ext3_dir_entry_2 *de, int size,
                        map_tail->hash = h.hash;
                        map_tail->offs = (u32) ((char *) de - base);
                        count++;
+                       cond_resched();
                }
                /* XXX: do we need to check rec_len == 0 case? -Chris */
                de = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
@@ -864,7 +881,7 @@ restart:
                                bh = ext3_getblk(NULL, dir, b++, 0, &err);
                                bh_use[ra_max] = bh;
                                if (bh)
-                                       ll_rw_block(READ, 1, &bh);
+                                       ll_rw_block(READ_META, 1, &bh);
                        }
                }
                if ((bh = bh_use[ra_ptr++]) == NULL)
@@ -872,6 +889,8 @@ restart:
                wait_on_buffer(bh);
                if (!buffer_uptodate(bh)) {
                        /* read error, skip block & hope for the best */
+                       ext3_error(sb, __FUNCTION__, "reading directory #%lu "
+                                  "offset %lu", dir->i_ino, block);
                        brelse(bh);
                        goto next;
                }
@@ -926,8 +945,16 @@ static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry,
        struct inode *dir = dentry->d_parent->d_inode;
 
        sb = dir->i_sb;
-       if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err)))
-               return NULL;
+       /* 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)))
+                       return NULL;
+       } else {
+               frame = frames;
+               frame->bh = NULL;                       /* for dx_release() */
+               frame->at = (struct dx_entry *)frames;  /* hack for zero entry*/
+               dx_set_block(frame->at, 0);             /* dx_root block is 0 */
+       }
        hash = hinfo.hash;
        do {
                block = dx_get_block(frame->at);
@@ -952,7 +979,7 @@ static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry,
                brelse (bh);
                /* Check to see if we should continue to search */
                retval = ext3_htree_next_block(dir, hash, frame,
-                                              frames, 0);
+                                              frames, NULL);
                if (retval < 0) {
                        ext3_warning(sb, __FUNCTION__,
                             "error reading index page in directory #%lu",
@@ -984,15 +1011,18 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str
        if (bh) {
                unsigned long ino = le32_to_cpu(de->inode);
                brelse (bh);
-               inode = iget(dir->i_sb, ino);
+               if (!ext3_valid_inum(dir->i_sb, ino)) {
+                       ext3_error(dir->i_sb, "ext3_lookup",
+                                  "bad inode number: %lu", ino);
+                       inode = NULL;
+               } else
+                       inode = iget(dir->i_sb, ino);
 
                if (!inode)
                        return ERR_PTR(-EACCES);
+               dx_propagate_tag(nd, inode);
        }
-       if (inode)
-               return d_splice_alias(inode, dentry);
-       d_add(dentry, inode);
-       return NULL;
+       return d_splice_alias(inode, dentry);
 }
 
 
@@ -1015,7 +1045,13 @@ struct dentry *ext3_get_parent(struct dentry *child)
                return ERR_PTR(-ENOENT);
        ino = le32_to_cpu(de->inode);
        brelse(bh);
-       inode = iget(child->d_inode->i_sb, ino);
+
+       if (!ext3_valid_inum(child->d_inode->i_sb, ino)) {
+               ext3_error(child->d_inode->i_sb, "ext3_get_parent",
+                          "bad inode number: %lu", ino);
+               inode = NULL;
+       } else
+               inode = iget(child->d_inode->i_sb, ino);
 
        if (!inode)
                return ERR_PTR(-EACCES);
@@ -1026,7 +1062,7 @@ struct dentry *ext3_get_parent(struct dentry *child)
                parent = ERR_PTR(-ENOMEM);
        }
        return parent;
-} 
+}
 
 #define S_SHIFT 12
 static unsigned char ext3_type_by_mode[S_IFMT >> S_SHIFT] = {
@@ -1174,7 +1210,7 @@ errout:
  * add_dirent_to_buf will attempt search the directory block for
  * space.  It will return -ENOSPC if no space is available, and -EIO
  * and -EEXIST if directory entry already exists.
- * 
+ *
  * NOTE!  bh is NOT released in the case where ENOSPC is returned.  In
  * all other cases bh is released.
  */
@@ -1250,7 +1286,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
         * happen is that the times are slightly out of date
         * and/or different from the directory change time.
         */
-       dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+       dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
        ext3_update_dx_flag(dir);
        dir->i_version++;
        ext3_mark_inode_dirty(handle, dir);
@@ -1366,7 +1402,6 @@ static int ext3_add_entry (handle_t *handle, struct dentry *dentry,
        int     dx_fallback=0;
 #endif
        unsigned blocksize;
-       unsigned nlen, rlen;
        u32 block, blocks;
 
        sb = dir->i_sb;
@@ -1388,7 +1423,7 @@ static int ext3_add_entry (handle_t *handle, struct dentry *dentry,
                bh = ext3_bread(handle, dir, block, 0, &retval);
                if(!bh)
                        return retval;
-               retval = add_dirent_to_buf(handle, dentry, inode, 0, bh);
+               retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh);
                if (retval != -ENOSPC)
                        return retval;
 
@@ -1404,8 +1439,7 @@ static int ext3_add_entry (handle_t *handle, struct dentry *dentry,
                return retval;
        de = (struct ext3_dir_entry_2 *) bh->b_data;
        de->inode = 0;
-       de->rec_len = cpu_to_le16(rlen = blocksize);
-       nlen = 0;
+       de->rec_len = cpu_to_le16(blocksize);
        return add_dirent_to_buf(handle, dentry, inode, de, bh);
 }
 
@@ -1425,7 +1459,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,
        struct ext3_dir_entry_2 *de;
        int err;
 
-       frame = dx_probe(dentry, 0, &hinfo, frames, &err);
+       frame = dx_probe(dentry, NULL, &hinfo, frames, &err);
        if (!frame)
                return err;
        entries = frame->entries;
@@ -1439,9 +1473,9 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,
        if (err)
                goto journal_error;
 
-       err = add_dirent_to_buf(handle, dentry, inode, 0, bh);
+       err = add_dirent_to_buf(handle, dentry, inode, NULL, bh);
        if (err != -ENOSPC) {
-               bh = 0;
+               bh = NULL;
                goto cleanup;
        }
 
@@ -1460,7 +1494,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,
                if (levels && (dx_get_count(frames->entries) ==
                               dx_get_limit(frames->entries))) {
                        ext3_warning(sb, __FUNCTION__,
-                                    "Directory index full!\n");
+                                    "Directory index full!");
                        err = -ENOSPC;
                        goto cleanup;
                }
@@ -1533,7 +1567,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,
        if (!de)
                goto cleanup;
        err = add_dirent_to_buf(handle, dentry, inode, de, bh);
-       bh = 0;
+       bh = NULL;
        goto cleanup;
 
 journal_error:
@@ -1550,7 +1584,7 @@ cleanup:
  * ext3_delete_entry deletes a directory entry by merging it with the
  * previous entry
  */
-static int ext3_delete_entry (handle_t *handle, 
+static int ext3_delete_entry (handle_t *handle,
                              struct inode * dir,
                              struct ext3_dir_entry_2 * de_del,
                              struct buffer_head * bh)
@@ -1593,12 +1627,12 @@ static int ext3_delete_entry (handle_t *handle,
  */
 static inline void ext3_inc_count(handle_t *handle, struct inode *inode)
 {
-       inode->i_nlink++;
+       inc_nlink(inode);
 }
 
 static inline void ext3_dec_count(handle_t *handle, struct inode *inode)
 {
-       inode->i_nlink--;
+       drop_nlink(inode);
 }
 
 static int ext3_add_nondir(handle_t *handle,
@@ -1621,18 +1655,19 @@ static int ext3_add_nondir(handle_t *handle,
  * is so far negative - it has no inode.
  *
  * If the create succeeds, we fill in the inode information
- * with d_instantiate(). 
+ * with d_instantiate().
  */
 static int ext3_create (struct inode * dir, struct dentry * dentry, int mode,
                struct nameidata *nd)
 {
-       handle_t *handle; 
+       handle_t *handle;
        struct inode * inode;
-       int err;
+       int err, retries = 0;
 
-       handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
+retry:
+       handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
-                                       2*EXT3_QUOTA_INIT_BLOCKS);
+                                       2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
@@ -1648,6 +1683,8 @@ static int ext3_create (struct inode * dir, struct dentry * dentry, int mode,
                err = ext3_add_nondir(handle, dentry, inode);
        }
        ext3_journal_stop(handle);
+       if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
+               goto retry;
        return err;
 }
 
@@ -1656,14 +1693,15 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry,
 {
        handle_t *handle;
        struct inode *inode;
-       int err;
+       int err, retries = 0;
 
        if (!new_valid_dev(rdev))
                return -EINVAL;
 
-       handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
-                                       EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
-                                       2*EXT3_QUOTA_INIT_BLOCKS);
+retry:
+       handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
+                                       EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
+                                       2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
@@ -1680,6 +1718,8 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry,
                err = ext3_add_nondir(handle, dentry, inode);
        }
        ext3_journal_stop(handle);
+       if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
+               goto retry;
        return err;
 }
 
@@ -1689,14 +1729,15 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode)
        struct inode * inode;
        struct buffer_head * dir_block;
        struct ext3_dir_entry_2 * de;
-       int err;
+       int err, retries = 0;
 
        if (dir->i_nlink >= EXT3_LINK_MAX)
                return -EMLINK;
 
-       handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
+retry:
+       handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
-                                       2*EXT3_QUOTA_INIT_BLOCKS);
+                                       2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
@@ -1713,7 +1754,7 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode)
        inode->i_size = EXT3_I(inode)->i_disksize = inode->i_sb->s_blocksize;
        dir_block = ext3_bread (handle, inode, 0, 1, &err);
        if (!dir_block) {
-               inode->i_nlink--; /* is this nlink == 0? */
+               drop_nlink(inode); /* is this nlink == 0? */
                ext3_mark_inode_dirty(handle, inode);
                iput (inode);
                goto out_stop;
@@ -1745,12 +1786,14 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode)
                iput (inode);
                goto out_stop;
        }
-       dir->i_nlink++;
+       inc_nlink(dir);
        ext3_update_dx_flag(dir);
        ext3_mark_inode_dirty(handle, dir);
        d_instantiate(dentry, inode);
 out_stop:
        ext3_journal_stop(handle);
+       if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
+               goto retry;
        return err;
 }
 
@@ -1763,24 +1806,29 @@ static int empty_dir (struct inode * inode)
        struct buffer_head * bh;
        struct ext3_dir_entry_2 * de, * de1;
        struct super_block * sb;
-       int err;
+       int err = 0;
 
        sb = inode->i_sb;
        if (inode->i_size < EXT3_DIR_REC_LEN(1) + EXT3_DIR_REC_LEN(2) ||
            !(bh = ext3_bread (NULL, inode, 0, 0, &err))) {
-               ext3_warning (inode->i_sb, "empty_dir",
-                             "bad directory (dir #%lu) - no data block",
-                             inode->i_ino);
+               if (err)
+                       ext3_error(inode->i_sb, __FUNCTION__,
+                                  "error %d reading directory #%lu offset 0",
+                                  err, inode->i_ino);
+               else
+                       ext3_warning(inode->i_sb, __FUNCTION__,
+                                    "bad directory (dir #%lu) - no data block",
+                                    inode->i_ino);
                return 1;
        }
        de = (struct ext3_dir_entry_2 *) bh->b_data;
        de1 = (struct ext3_dir_entry_2 *)
                        ((char *) de + le16_to_cpu(de->rec_len));
        if (le32_to_cpu(de->inode) != inode->i_ino ||
-                       !le32_to_cpu(de1->inode) || 
+                       !le32_to_cpu(de1->inode) ||
                        strcmp (".", de->name) ||
                        strcmp ("..", de1->name)) {
-               ext3_warning (inode->i_sb, "empty_dir",
+               ext3_warning (inode->i_sb, "empty_dir",
                              "bad directory (dir #%lu) - no `.' or `..'",
                              inode->i_ino);
                brelse (bh);
@@ -1792,24 +1840,26 @@ static int empty_dir (struct inode * inode)
        while (offset < inode->i_size ) {
                if (!bh ||
                        (void *) de >= (void *) (bh->b_data+sb->s_blocksize)) {
+                       err = 0;
                        brelse (bh);
                        bh = ext3_bread (NULL, inode,
                                offset >> EXT3_BLOCK_SIZE_BITS(sb), 0, &err);
                        if (!bh) {
-#if 0
-                               ext3_error (sb, "empty_dir",
-                               "directory #%lu contains a hole at offset %lu",
-                                       inode->i_ino, offset);
-#endif
+                               if (err)
+                                       ext3_error(sb, __FUNCTION__,
+                                                  "error %d reading directory"
+                                                  " #%lu offset %lu",
+                                                  err, inode->i_ino, offset);
                                offset += sb->s_blocksize;
                                continue;
                        }
                        de = (struct ext3_dir_entry_2 *) bh->b_data;
                }
-               if (!ext3_check_dir_entry ("empty_dir", inode, de, bh,
-                                          offset)) {
-                       brelse (bh);
-                       return 1;
+               if (!ext3_check_dir_entry("empty_dir", inode, de, bh, offset)) {
+                       de = (struct ext3_dir_entry_2 *)(bh->b_data +
+                                                        sb->s_blocksize);
+                       offset = (offset | (sb->s_blocksize - 1)) + 1;
+                       continue;
                }
                if (le32_to_cpu(de->inode)) {
                        brelse (bh);
@@ -1845,7 +1895,7 @@ int ext3_orphan_add(handle_t *handle, struct inode *inode)
         * being truncated, or files being unlinked. */
 
        /* @@@ FIXME: Observation from aviro:
-        * I think I can trigger J_ASSERT in ext3_orphan_add().  We block 
+        * I think I can trigger J_ASSERT in ext3_orphan_add().  We block
         * here (on lock_super()), so race with ext3_link() which might bump
         * ->i_nlink. For, say it, character device. Not a regular file,
         * not a directory, not a symlink and ->i_nlink > 0.
@@ -1881,8 +1931,8 @@ int ext3_orphan_add(handle_t *handle, struct inode *inode)
        if (!err)
                list_add(&EXT3_I(inode)->i_orphan, &EXT3_SB(sb)->s_orphan);
 
-       jbd_debug(4, "superblock will point to %ld\n", inode->i_ino);
-       jbd_debug(4, "orphan inode %ld will point to %d\n",
+       jbd_debug(4, "superblock will point to %lu\n", inode->i_ino);
+       jbd_debug(4, "orphan inode %lu will point to %d\n",
                        inode->i_ino, NEXT_ORPHAN(inode));
 out_unlock:
        unlock_super(sb);
@@ -1953,8 +2003,6 @@ int ext3_orphan_del(handle_t *handle, struct inode *inode)
                goto out_brelse;
        NEXT_ORPHAN(inode) = 0;
        err = ext3_mark_iloc_dirty(handle, inode, &iloc);
-       if (err)
-               goto out_brelse;
 
 out_err:
        ext3_std_error(inode->i_sb, err);
@@ -1978,7 +2026,7 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry)
        /* Initialize quotas before so that eventual writes go in
         * separate transaction */
        DQUOT_INIT(dentry->d_inode);
-       handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS);
+       handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
@@ -2008,15 +2056,15 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry)
                              "empty directory has nlink!=2 (%d)",
                              inode->i_nlink);
        inode->i_version++;
-       inode->i_nlink = 0;
+       clear_nlink(inode);
        /* There's no need to set i_disksize: the fact that i_nlink is
         * zero will ensure that the right thing happens during any
         * recovery. */
        inode->i_size = 0;
        ext3_orphan_add(handle, inode);
-       inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+       inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
        ext3_mark_inode_dirty(handle, inode);
-       dir->i_nlink--;
+       drop_nlink(dir);
        ext3_update_dx_flag(dir);
        ext3_mark_inode_dirty(handle, dir);
 
@@ -2037,7 +2085,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry)
        /* Initialize quotas before so that eventual writes go
         * in separate transaction */
        DQUOT_INIT(dentry->d_inode);
-       handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS);
+       handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
@@ -2064,10 +2112,10 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry)
        retval = ext3_delete_entry(handle, dir, de, bh);
        if (retval)
                goto end_unlink;
-       dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+       dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
        ext3_update_dx_flag(dir);
        ext3_mark_inode_dirty(handle, dir);
-       inode->i_nlink--;
+       drop_nlink(inode);
        if (!inode->i_nlink)
                ext3_orphan_add(handle, inode);
        inode->i_ctime = dir->i_ctime;
@@ -2085,15 +2133,16 @@ static int ext3_symlink (struct inode * dir,
 {
        handle_t *handle;
        struct inode * inode;
-       int l, err;
+       int l, err, retries = 0;
 
        l = strlen(symname)+1;
        if (l > dir->i_sb->s_blocksize)
                return -ENAMETOOLONG;
 
-       handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
-                                       EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5 +
-                                       2*EXT3_QUOTA_INIT_BLOCKS);
+retry:
+       handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
+                                       EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5 +
+                                       2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
@@ -2113,7 +2162,8 @@ static int ext3_symlink (struct inode * dir,
                 * We have a transaction open.  All is sweetness.  It also sets
                 * i_size in generic_commit_write().
                 */
-               err = page_symlink(inode, symname, l);
+               err = __page_symlink(inode, symname, l,
+                               mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS);
                if (err) {
                        ext3_dec_count(handle, inode);
                        ext3_mark_inode_dirty(handle, inode);
@@ -2129,6 +2179,8 @@ static int ext3_symlink (struct inode * dir,
        err = ext3_add_nondir(handle, dentry, inode);
 out_stop:
        ext3_journal_stop(handle);
+       if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
+               goto retry;
        return err;
 }
 
@@ -2137,12 +2189,13 @@ static int ext3_link (struct dentry * old_dentry,
 {
        handle_t *handle;
        struct inode *inode = old_dentry->d_inode;
-       int err;
+       int err, retries = 0;
 
        if (inode->i_nlink >= EXT3_LINK_MAX)
                return -EMLINK;
 
-       handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
+retry:
+       handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT3_INDEX_EXTRA_TRANS_BLOCKS);
        if (IS_ERR(handle))
                return PTR_ERR(handle);
@@ -2150,12 +2203,14 @@ static int ext3_link (struct dentry * old_dentry,
        if (IS_DIRSYNC(dir))
                handle->h_sync = 1;
 
-       inode->i_ctime = CURRENT_TIME;
+       inode->i_ctime = CURRENT_TIME_SEC;
        ext3_inc_count(handle, inode);
        atomic_inc(&inode->i_count);
 
        err = ext3_add_nondir(handle, dentry, inode);
        ext3_journal_stop(handle);
+       if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
+               goto retry;
        return err;
 }
 
@@ -2182,8 +2237,9 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
         * in separate transaction */
        if (new_dentry->d_inode)
                DQUOT_INIT(new_dentry->d_inode);
-       handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS +
-                                       EXT3_INDEX_EXTRA_TRANS_BLOCKS + 2);
+       handle = ext3_journal_start(old_dir, 2 *
+                                       EXT3_DATA_TRANS_BLOCKS(old_dir->i_sb) +
+                                       EXT3_INDEX_EXTRA_TRANS_BLOCKS + 2);
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
@@ -2234,7 +2290,7 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
        } else {
                BUFFER_TRACE(new_bh, "get write access");
                ext3_journal_get_write_access(handle, new_bh);
-               new_de->inode = le32_to_cpu(old_inode->i_ino);
+               new_de->inode = cpu_to_le32(old_inode->i_ino);
                if (EXT3_HAS_INCOMPAT_FEATURE(new_dir->i_sb,
                                              EXT3_FEATURE_INCOMPAT_FILETYPE))
                        new_de->file_type = old_de->file_type;
@@ -2249,17 +2305,21 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
         * Like most other Unix systems, set the ctime for inodes on a
         * rename.
         */
-       old_inode->i_ctime = CURRENT_TIME;
+       old_inode->i_ctime = CURRENT_TIME_SEC;
        ext3_mark_inode_dirty(handle, old_inode);
 
        /*
         * ok, that's it
         */
-       retval = ext3_delete_entry(handle, old_dir, old_de, old_bh);
-       if (retval == -ENOENT) {
-               /*
-                * old_de could have moved out from under us.
-                */
+       if (le32_to_cpu(old_de->inode) != old_inode->i_ino ||
+           old_de->name_len != old_dentry->d_name.len ||
+           strncmp(old_de->name, old_dentry->d_name.name, old_de->name_len) ||
+           (retval = ext3_delete_entry(handle, old_dir,
+                                       old_de, old_bh)) == -ENOENT) {
+               /* old_de could have moved from under us during htree split, so
+                * make sure that we are deleting the right entry.  We might
+                * also be pointing to a stale entry in the unused part of
+                * old_bh so just checking inum and the name isn't enough. */
                struct buffer_head *old_bh2;
                struct ext3_dir_entry_2 *old_de2;
 
@@ -2277,22 +2337,22 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
        }
 
        if (new_inode) {
-               new_inode->i_nlink--;
-               new_inode->i_ctime = CURRENT_TIME;
+               drop_nlink(new_inode);
+               new_inode->i_ctime = CURRENT_TIME_SEC;
        }
-       old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+       old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
        ext3_update_dx_flag(old_dir);
        if (dir_bh) {
                BUFFER_TRACE(dir_bh, "get_write_access");
                ext3_journal_get_write_access(handle, dir_bh);
-               PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino);
+               PARENT_INO(dir_bh->b_data) = cpu_to_le32(new_dir->i_ino);
                BUFFER_TRACE(dir_bh, "call ext3_journal_dirty_metadata");
                ext3_journal_dirty_metadata(handle, dir_bh);
-               old_dir->i_nlink--;
+               drop_nlink(old_dir);
                if (new_inode) {
-                       new_inode->i_nlink--;
+                       drop_nlink(new_inode);
                } else {
-                       new_dir->i_nlink++;
+                       inc_nlink(new_dir);
                        ext3_update_dx_flag(new_dir);
                        ext3_mark_inode_dirty(handle, new_dir);
                }
@@ -2327,18 +2387,24 @@ struct inode_operations ext3_dir_inode_operations = {
        .mknod          = ext3_mknod,
        .rename         = ext3_rename,
        .setattr        = ext3_setattr,
-       .setxattr       = ext3_setxattr,
-       .getxattr       = ext3_getxattr,
+#ifdef CONFIG_EXT3_FS_XATTR
+       .setxattr       = generic_setxattr,
+       .getxattr       = generic_getxattr,
        .listxattr      = ext3_listxattr,
-       .removexattr    = ext3_removexattr,
+       .removexattr    = generic_removexattr,
+#endif
        .permission     = ext3_permission,
+       .sync_flags     = ext3_sync_flags,
 };
 
 struct inode_operations ext3_special_inode_operations = {
        .setattr        = ext3_setattr,
-       .setxattr       = ext3_setxattr,
-       .getxattr       = ext3_getxattr,
+#ifdef CONFIG_EXT3_FS_XATTR
+       .setxattr       = generic_setxattr,
+       .getxattr       = generic_getxattr,
        .listxattr      = ext3_listxattr,
-       .removexattr    = ext3_removexattr,
+       .removexattr    = generic_removexattr,
+#endif
        .permission     = ext3_permission,
-}; 
+       .sync_flags     = ext3_sync_flags,
+};