--- ./fs/ext3/namei.c.orig 2004-03-09 16:46:43.000000000 -0700 +++ ./fs/ext3/namei.c 2004-04-21 02:30:25.000000000 -0600 @@ -1533,13 +1539,20 @@ static int ext3_delete_entry (handle_t * * if it is needed. */ static inline void ext3_inc_count(handle_t *handle, struct inode *inode) { - inode->i_nlink++; + if (is_dx(inode) && inode->i_nlink > 1) { + inode->i_nlink++; + if (inode->i_nlink >= 65000) /* limit is 16-bit i_links_count */ + inode->i_nlink = 1; + } else { + inode->i_nlink++; + } } static inline void ext3_dec_count(handle_t *handle, struct inode *inode) { - inode->i_nlink--; + if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2) + inode->i_nlink--; } static int ext3_add_nondir(handle_t *handle, @@ -1640,7 +1652,7 @@ static int ext3_mkdir(struct inode * dir struct ext3_dir_entry_2 * de; int err; - if (dir->i_nlink >= EXT3_LINK_MAX) + if (EXT3_DIR_LINK_MAXED(dir)) return -EMLINK; handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + @@ -1662,7 +1674,7 @@ static int ext3_mkdir(struct inode * dir 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? */ + ext3_dec_count(handle, inode); /* is this nlink == 0? */ ext3_mark_inode_dirty(handle, inode); iput (inode); goto out_stop; @@ -1694,7 +1706,7 @@ static int ext3_mkdir(struct inode * dir iput (inode); goto out_stop; } - dir->i_nlink++; + ext3_inc_count(handle, dir); ext3_update_dx_flag(dir); ext3_mark_inode_dirty(handle, dir); d_instantiate(dentry, inode); @@ -1755,10 +1767,11 @@ static int empty_dir (struct inode * ino } 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)) { + /* On error skip the de and offset to the next block. */ + de = (void *)(bh->b_data + sb->s_blocksize); + offset = (offset | (sb->s_blocksize - 1)) + 1; + continue; } if (le32_to_cpu(de->inode)) { brelse (bh); @@ -1951,14 +1964,14 @@ static int ext3_rmdir (struct inode * di retval = ext3_delete_entry(handle, dir, de, bh); if (retval) goto end_rmdir; - if (inode->i_nlink != 2) - ext3_warning (inode->i_sb, "ext3_rmdir", - "empty directory has nlink!=2 (%d)", - inode->i_nlink); + if (!EXT3_DIR_LINK_EMPTY(inode)) + ext3_warning(inode->i_sb, __FUNCTION__, + "empty directory has too many links (%d)", + inode->i_nlink); inode->i_version = ++event; inode->i_nlink = 0; ext3_orphan_add(handle, inode); - dir->i_nlink--; + ext3_dec_count(handle, dir); inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; ext3_mark_inode_dirty(handle, inode); ext3_update_dx_flag(dir); @@ -2044,7 +2053,7 @@ static int ext3_unlink(struct inode * di dir->i_ctime = dir->i_mtime = CURRENT_TIME; ext3_update_dx_flag(dir); ext3_mark_inode_dirty(handle, dir); - inode->i_nlink--; + ext3_dec_count(handle, inode); if (!inode->i_nlink) { ext3_try_to_delay_deletion(inode); ext3_orphan_add(handle, inode); @@ -2121,9 +2147,8 @@ static int ext3_link (struct dentry * ol if (S_ISDIR(inode->i_mode)) return -EPERM; - if (inode->i_nlink >= EXT3_LINK_MAX) { + if (EXT3_DIR_LINK_MAXED(inode)) return -EMLINK; - } handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + EXT3_INDEX_EXTRA_TRANS_BLOCKS); @@ -2138,7 +2163,7 @@ static int ext3_link (struct dentry * ol ext3_inc_count(handle, inode); atomic_inc(&inode->i_count); - err = ext3_add_nondir(handle, dentry, inode); + err = ext3_add_link(handle, dentry, inode); ext3_orphan_del(handle, inode); ext3_journal_stop(handle, dir); return err; @@ -2206,8 +2232,8 @@ static int ext3_rename (struct inode * o if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino) goto end_rename; retval = -EMLINK; - if (!new_inode && new_dir!=old_dir && - new_dir->i_nlink >= EXT3_LINK_MAX) + if (!new_inode && new_dir != old_dir && + EXT3_DIR_LINK_MAXED(new_dir)) goto end_rename; } if (!new_bh) { @@ -2261,7 +2287,7 @@ static int ext3_rename (struct inode * o } if (new_inode) { - new_inode->i_nlink--; + ext3_dec_count(handle, new_inode); new_inode->i_ctime = CURRENT_TIME; } old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; @@ -2272,11 +2298,11 @@ static int ext3_rename (struct inode * o PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino); BUFFER_TRACE(dir_bh, "call ext3_journal_dirty_metadata"); ext3_journal_dirty_metadata(handle, dir_bh); - old_dir->i_nlink--; + ext3_dec_count(handle, old_dir); if (new_inode) { - new_inode->i_nlink--; + ext3_dec_count(handle, new_inode); } else { - new_dir->i_nlink++; + ext3_inc_count(handle, new_dir); ext3_update_dx_flag(new_dir); ext3_mark_inode_dirty(handle, new_dir); } --- ./include/linux/ext3_fs.h.orig 2004-03-16 17:33:35.000000000 -0700 +++ ./include/linux/ext3_fs.h 2004-04-21 01:53:21.000000000 -0600 @@ -573,14 +573,15 @@ struct ext3_dir_entry_2 { */ #ifdef CONFIG_EXT3_INDEX - #define is_dx(dir) (EXT3_HAS_COMPAT_FEATURE(dir->i_sb, \ - EXT3_FEATURE_COMPAT_DIR_INDEX) && \ +#define is_dx(dir) (EXT3_HAS_COMPAT_FEATURE(dir->i_sb, \ + EXT3_FEATURE_COMPAT_DIR_INDEX) && \ (EXT3_I(dir)->i_flags & EXT3_INDEX_FL)) -#define EXT3_DIR_LINK_MAX(dir) (!is_dx(dir) && (dir)->i_nlink >= EXT3_LINK_MAX) -#define EXT3_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2 || (dir)->i_nlink == 1) +#define EXT3_DIR_LINK_MAXED(dir) (!is_dx(dir) && (dir)->i_nlink >=EXT3_LINK_MAX) +#define EXT3_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2 || \ + (is_dx(dir) && (dir)->i_nlink == 1)) #else #define is_dx(dir) 0 -#define EXT3_DIR_LINK_MAX(dir) ((dir)->i_nlink >= EXT3_LINK_MAX) +#define EXT3_DIR_LINK_MAXED(dir) ((dir)->i_nlink >= EXT3_LINK_MAX) #define EXT3_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2) #endif