===================================================================
--- linux-2.4.19.orig/fs/ext3/iopen.c 2003-01-30 05:24:37.000000000 -0500
+++ linux-2.4.19/fs/ext3/iopen.c 2004-04-23 22:37:48.000000000 -0400
-@@ -0,0 +1,258 @@
+@@ -0,0 +1,282 @@
+/*
+ * linux/fs/ext3/iopen.c
+ *
+
+/* This function is spliced into ext3_lookup and does the move of a
+ * disconnected dentry (if it exists) to a connected dentry.
-+ * Caller must hold dcache_lock.
+ */
-+struct dentry *iopen_connect_dentry(struct dentry *de, struct inode *inode)
++struct dentry *iopen_connect_dentry(struct dentry *dentry, struct inode *inode,
++ int rehash)
+{
+ struct dentry *tmp, *goal = NULL;
+ struct list_head *lp;
+
++ /* verify this dentry is really new */
++ assert(dentry->d_inode == NULL);
++ assert(list_empty(&dentry->d_alias)); /* d_instantiate */
++ if (rehash)
++ assert(list_empty(&dentry->d_hash)); /* d_rehash */
++ assert(list_empty(&dentry->d_subdirs));
++
++ spin_lock(&dcache_lock);
++ if (!inode)
++ goto do_rehash;
++
+ /* preferrably return a connected dentry */
+ list_for_each(lp, &inode->i_dentry) {
+ tmp = list_entry(lp, struct dentry, d_alias);
+ }
+
+ if (!goal)
-+ return NULL;
++ goto do_instantiate;
+
+ /* Move the goal to the de hash queue - like d_move() */
+ goal->d_flags &= ~DCACHE_NFSD_DISCONNECTED;
+ list_del_init(&goal->d_hash);
+
+ list_del(&goal->d_child);
-+ list_del(&de->d_child);
++ list_del(&dentry->d_child);
+
+ /* Switch the parents and the names.. */
-+ switch_names(goal, de);
-+ do_switch(goal->d_parent, de->d_parent);
-+ do_switch(goal->d_name.len, de->d_name.len);
-+ do_switch(goal->d_name.hash, de->d_name.hash);
++ switch_names(goal, dentry);
++ do_switch(goal->d_parent, dentry->d_parent);
++ do_switch(goal->d_name.len, dentry->d_name.len);
++ do_switch(goal->d_name.hash, dentry->d_name.hash);
+
+ /* And add them back to the (new) parent lists */
+ list_add(&goal->d_child, &goal->d_parent->d_subdirs);
-+ list_add(&de->d_child, &de->d_parent->d_subdirs);
++ list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);
+ __d_rehash(goal, 0);
++ spin_unlock(&dcache_lock);
++ iput(inode);
+
+ return goal;
++
++ /* d_add(), but don't drop dcache_lock before adding dentry to inode */
++do_instantiate:
++ list_add(&dentry->d_alias, &inode->i_dentry); /* d_instantiate */
++ dentry->d_inode = inode;
++do_rehash:
++ if (rehash)
++ __d_rehash(dentry, 0); /* d_rehash */
++ spin_unlock(&dcache_lock);
++
++ return NULL;
+}
+
+/*
/*
* define how far ahead to read directories while searching them.
-@@ -928,10 +928,14 @@
- struct inode * inode;
- struct ext3_dir_entry_2 * de;
- struct buffer_head * bh;
-+ struct dentry *alternate = NULL;
-
+@@ -926,6 +927,9 @@
if (dentry->d_name.len > EXT3_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
bh = ext3_find_entry(dentry, &de);
inode = NULL;
if (bh) {
-@@ -943,7 +947,28 @@
+@@ -943,8 +948,8 @@
return ERR_PTR(-EACCES);
}
}
- d_add(dentry, inode);
+- return NULL;
+
-+ /* verify this dentry is really new */
-+ assert(!dentry->d_inode);
-+ assert(list_empty(&dentry->d_alias)); /* d_instantiate */
-+ assert(list_empty(&dentry->d_hash)); /* d_rehash */
-+ assert(list_empty(&dentry->d_subdirs));
-+
-+ spin_lock(&dcache_lock);
-+ if (inode && (alternate = iopen_connect_dentry(dentry, inode))) {
-+ spin_unlock(&dcache_lock);
-+ iput(inode);
-+ return alternate;
++ return iopen_connect_dentry(dentry, inode, 1);
+ }
+
+ #define S_SHIFT 12
+@@ -1932,10 +1935,6 @@
+ inode->i_nlink);
+ inode->i_version = ++event;
+ inode->i_nlink = 0;
+- /* 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);
+ dir->i_nlink--;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+@@ -2086,6 +2085,23 @@
+ return err;
+ }
+
++/* Like ext3_add_nondir() except for call to iopen_connect_dentry */
++static int ext3_add_link(handle_t *handle, struct dentry *dentry,
++ struct inode *inode)
++{
++ int err = ext3_add_entry(handle, dentry, inode);
++ if (!err) {
++ err = ext3_mark_inode_dirty(handle, inode);
++ if (err == 0) {
++ dput(iopen_connect_dentry(dentry, inode, 0));
++ return 0;
++ }
+ }
++ ext3_dec_count(handle, inode);
++ iput(inode);
++ return err;
++}
+
-+ /* d_add(), but don't drop dcache_lock before adding dentry to inode */
-+ if (inode) /* d_instantiate */
-+ list_add(&dentry->d_alias, &inode->i_dentry);
-+ dentry->d_inode = inode;
-+
-+ __d_rehash(dentry, 0); /* d_rehash */
-+ spin_unlock(&dcache_lock);
-+
- return NULL;
- }
+ static int ext3_link (struct dentry * old_dentry,
+ struct inode * dir, struct dentry *dentry)
+ {
+@@ -2113,7 +2129,8 @@
+ 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;
+ }
Index: linux-2.4.19/fs/ext3/super.c
===================================================================
--- linux-2.4.19.orig/fs/ext3/super.c 2004-04-23 22:37:42.000000000 -0400