This patch gets ready to support creating a name that collides with a
hidden dentry that still carries an inode because the file was unlinked
while somebody had it open.  The plan is to handle this in d_instantiate
by unhashing the hidden dentry and replacing it with a clone.  This
means that d_instantiate, or a varient, can return a different dentry
than it was given.  d_instantiate has, up till now, returned void, and
now it (or a variant) will return a dentry, which may possibly be
different than the one it was asked to instantiate.

It is probably ok to just change d_instantiate's return type, which
will be the same as passed for any filesystem that does not support
deferring, and thus can never create a negative dentry that still has
an inode.

The patch adds a (dentry *) return value to the vfs ->create method,
which requires every filesystem to be updated to return the dentry
that was passed or an ERR_PTR return in place of the current integer
error.  The original vfs_create, which called ->create is now called
namei_create and returns a dentry which is needed by a higher level
file notification API within vfs/namei.c.  A new vfs_create is a
wrapper, which keeps the old interface for external filesystem use.

Next, I will attempt to add the handling to d_instantiated for a
collision with a negative dentry for a file that is still open.  Unless
Hirofumi comes up with a simpler idea, which he is thinking about.

Regards,

Daniel
diff --git a/Makefile b/Makefile
index 56fb747..985a7ce 100644
--- a/Makefile
+++ b/Makefile
@@ -538,7 +538,7 @@ NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
 CHECKFLAGS     += $(NOSTDINC_FLAGS)
 
 # warn about C99 declaration after statement
-KBUILD_CFLAGS += $(call cc-option,-Wdeclaration-after-statement,)
+#KBUILD_CFLAGS += $(call cc-option,-Wdeclaration-after-statement,)
 
 # disable pointer signed / unsigned warnings in gcc 4.0
 KBUILD_CFLAGS += $(call cc-option,-Wno-pointer-sign,)
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
index f1c2ea8..ac7191b 100644
--- a/fs/bad_inode.c
+++ b/fs/bad_inode.c
@@ -185,10 +185,10 @@ static const struct file_operations bad_file_ops =
 	.splice_read	= bad_file_splice_read,
 };
 
-static int bad_inode_create (struct inode *dir, struct dentry *dentry,
+static struct dentry *bad_inode_create (struct inode *dir, struct dentry *dentry,
 		int mode, struct nameidata *nd)
 {
-	return -EIO;
+	return ERR_PTR(-EIO);
 }
 
 static struct dentry *bad_inode_lookup(struct inode *dir,
diff --git a/fs/dcache.c b/fs/dcache.c
index 6068c25..17b4f4a 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -272,7 +272,7 @@ int d_invalidate(struct dentry * dentry)
 	 */
 	spin_lock(&dentry->d_lock);
 	if (atomic_read(&dentry->d_count) > 1) {
-		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
+		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { // what to do here?
 			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dcache_lock);
 			return -EBUSY;
@@ -970,9 +970,11 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
 {
 	BUG_ON(!list_empty(&entry->d_alias));
 	spin_lock(&dcache_lock);
+	/* d_attach_locked(dentry, inode) */
 	if (inode)
 		list_add(&entry->d_alias, &inode->i_dentry);
 	entry->d_inode = inode;
+	entry->d_flags &= ~DCACHE_HIDDEN;
 	fsnotify_d_instantiate(entry, inode);
 	spin_unlock(&dcache_lock);
 	security_d_instantiate(entry, inode);
@@ -1022,8 +1024,10 @@ static struct dentry *__d_instantiate_unique(struct dentry *entry,
 		return alias;
 	}
 
+	/* d_attach_locked(dentry, inode) */
 	list_add(&entry->d_alias, &inode->i_dentry);
 	entry->d_inode = inode;
+	entry->d_flags &= ~DCACHE_HIDDEN;
 	fsnotify_d_instantiate(entry, inode);
 	return NULL;
 }
@@ -1130,6 +1134,7 @@ struct dentry * d_alloc_anon(struct inode *inode)
 		spin_lock(&res->d_lock);
 		res->d_sb = inode->i_sb;
 		res->d_parent = res;
+		/* d_attach_locked(dentry, inode) */
 		res->d_inode = inode;
 		res->d_flags |= DCACHE_DISCONNECTED;
 		res->d_flags &= ~DCACHE_UNHASHED;
@@ -1182,8 +1187,10 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
 			iput(inode);
 		} else {
 			/* d_instantiate takes dcache_lock, so we do it by hand */
+			/* d_attach_locked(dentry, inode) */
 			list_add(&dentry->d_alias, &inode->i_dentry);
 			dentry->d_inode = inode;
+			dentry->d_flags &= ~DCACHE_HIDDEN;
 			fsnotify_d_instantiate(dentry, inode);
 			spin_unlock(&dcache_lock);
 			security_d_instantiate(dentry, inode);
@@ -1387,20 +1394,22 @@ out:
  
 void d_delete(struct dentry * dentry)
 {
-	int isdir = 0;
+	int isdir = 0, hidden;
 	/*
 	 * Are we the only user?
 	 */
 	spin_lock(&dcache_lock);
 	spin_lock(&dentry->d_lock);
 	isdir = S_ISDIR(dentry->d_inode->i_mode);
-	if (atomic_read(&dentry->d_count) == 1) {
+	hidden = dentry->d_op && dentry->d_op->d_hide && dentry->d_op->d_hide(dentry);
+
+	if (atomic_read(&dentry->d_count) == 1 + hidden) {
 		dentry_iput(dentry);
 		fsnotify_nameremove(dentry, isdir);
 		return;
 	}
 
-	if (!d_unhashed(dentry))
+	if (!d_unhashed(dentry) && !hidden)
 		__d_drop(dentry);
 
 	spin_unlock(&dentry->d_lock);
@@ -1514,7 +1523,7 @@ static void d_move_locked(struct dentry * dentry, struct dentry * target)
 {
 	struct hlist_head *list;
 
-	if (!dentry->d_inode)
+	if (d_negative(dentry))
 		printk(KERN_WARNING "VFS: moving negative dcache entry\n");
 
 	write_seqlock(&rename_lock);
@@ -2103,7 +2112,7 @@ ino_t find_inode_number(struct dentry *dir, struct qstr *name)
 
 	dentry = d_hash_and_lookup(dir, name);
 	if (dentry) {
-		if (dentry->d_inode)
+		if (!d_negative(dentry))
 			ino = dentry->d_inode->i_ino;
 		dput(dentry);
 	}
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index a78c6b4..c22db38 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -270,6 +270,61 @@ static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode)
 		de->file_type = 0;
 }
 
+int ext2_unlink(struct inode *dir, struct dentry *dentry);
+void dentry_iput(struct dentry *dentry);
+
+int ext2_flush_dir(struct dentry *dir)
+{
+	printk(">>> ext2_sync_dir %p \"%.*s\"\n", dir, dir->d_name.len, dir->d_name.name);
+	struct list_head *next;
+	spin_lock(&dcache_lock);
+	for (next = dir->d_subdirs.next; next != &dir->d_subdirs;) {
+		struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child);
+		next = next->next;
+		show_dentry("dentry", dentry);
+		if (d_unhashed(dentry))
+			continue;
+		spin_lock(&dentry->d_lock);
+		if (d_negative(dentry)) {
+			if ((dentry->d_flags & DCACHE_STALE)) {
+				dentry->d_flags &= ~DCACHE_STALE;
+				show_dentry("deferred unlink", dentry);
+				spin_unlock(&dentry->d_lock);
+				spin_unlock(&dcache_lock);
+				ext2_unlink(dir->d_inode, dentry);
+				if (dentry->d_inode)
+					d_delete(dentry);
+				dput(dentry);
+				spin_lock(&dcache_lock);
+				continue;
+			}
+		} else {
+			if (!(dentry->d_flags & DCACHE_BACKED)) {
+				int stale = dentry->d_flags & DCACHE_STALE;
+				dentry->d_flags &= ~DCACHE_STALE;
+				dentry->d_flags |= DCACHE_BACKED;
+				show_dentry("deferred link", dentry);
+				spin_unlock(&dentry->d_lock);
+				spin_unlock(&dcache_lock);
+				if (stale)
+					ext2_unlink(dir->d_inode, dentry);
+				struct inode *inode = dentry->d_inode;
+				int err = ext2_add_link(dentry, inode);
+				if (err) {
+					inode_dec_link_count(inode);
+					iput(inode);
+				}
+				dput(dentry);
+				spin_lock(&dcache_lock);
+				continue;
+			}
+		}
+		spin_unlock(&dentry->d_lock);
+	}
+	spin_unlock(&dcache_lock);
+	return 0; // really??
+}
+
 static int
 ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
 {
@@ -283,6 +338,8 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
 	unsigned char *types = NULL;
 	int need_revalidate = filp->f_version != inode->i_version;
 
+	ext2_flush_dir(filp->f_path.dentry);
+
 	if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
 		return 0;
 
@@ -699,6 +756,12 @@ not_empty:
 	return 0;
 }
 
+int ext2_sync_dir(struct file *file, struct dentry *dir, int datasync)
+{
+	ext2_flush_dir(dir);
+	return ext2_sync_file(file, dir, datasync);
+}
+
 const struct file_operations ext2_dir_operations = {
 	.llseek		= generic_file_llseek,
 	.read		= generic_read_dir,
@@ -707,5 +770,5 @@ const struct file_operations ext2_dir_operations = {
 #ifdef CONFIG_COMPAT
 	.compat_ioctl	= ext2_compat_ioctl,
 #endif
-	.fsync		= ext2_sync_file,
+	.fsync		= ext2_sync_dir,
 };
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 384fc0d..a9ce89b 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -58,6 +58,7 @@ static inline int ext2_inode_is_fast_symlink(struct inode *inode)
  */
 void ext2_delete_inode (struct inode * inode)
 {
+	printk(">>> ext2_delete_inode\n");
 	truncate_inode_pages(&inode->i_data, 0);
 
 	if (is_bad_inode(inode))
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 80c97fd..d3a8f7f 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -36,10 +36,39 @@
 #include "acl.h"
 #include "xip.h"
 
-static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
+static int ext2_hide_dentry(struct dentry *dentry)
+{
+	if (dentry->d_flags & DCACHE_BACKED) {
+		BUG_ON(!dentry->d_inode);
+		show_dentry("hide dentry", dentry);
+		dentry->d_flags &= ~DCACHE_BACKED;
+		dentry->d_flags |= DCACHE_STALE;
+		dentry->d_flags |= DCACHE_HIDDEN;
+		dget(dentry);
+		return 1;
+	} else if (dentry->d_inode)
+		/* visible/unbacked => negative: cancel dget from create */
+		dput(dentry);
+	return 0;
+}
+
+static struct dentry_operations ext2_dentry_operations = {
+	.d_hide = ext2_hide_dentry,
+};
+
+static int defer_unlink(struct inode *dir, struct dentry *dentry)
+{
+	show_dentry("defer unlink", dentry);
+	dentry->d_inode->i_ctime = dir->i_ctime;
+	inode_dec_link_count(dentry->d_inode);
+	return 0;
+}
+
+int old_add_nondir(struct dentry *dentry, struct inode *inode)
 {
 	int err = ext2_add_link(dentry, inode);
 	if (!err) {
+		dentry->d_op = &ext2_dentry_operations;
 		d_instantiate(dentry, inode);
 		return 0;
 	}
@@ -48,6 +77,17 @@ static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
 	return err;
 }
 
+static int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
+{
+	show_dentry("defer create", dentry);
+	if (!(dentry->d_flags & DCACHE_HIDDEN))
+		dget(dentry);
+	dentry->d_op = &ext2_dentry_operations;
+	d_instantiate(dentry, inode);
+	show_dentry("instantiated", dentry);
+	return 0;
+}
+
 /*
  * Methods themselves.
  */
@@ -66,7 +106,10 @@ static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, str
 		inode = ext2_iget(dir->i_sb, ino);
 		if (IS_ERR(inode))
 			return ERR_CAST(inode);
+		dentry->d_flags |= DCACHE_BACKED;
+		show_dentry("found real dirent", dentry);
 	}
+	dentry->d_op = &ext2_dentry_operations;
 	return d_splice_alias(inode, dentry);
 }
 
@@ -103,7 +146,7 @@ struct dentry *ext2_get_parent(struct dentry *child)
  * If the create succeeds, we fill in the inode information
  * with d_instantiate(). 
  */
-static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd)
+static struct dentry *ext2_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
 {
 	struct inode * inode = ext2_new_inode (dir, mode);
 	int err = PTR_ERR(inode);
@@ -122,7 +165,7 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, st
 		mark_inode_dirty(inode);
 		err = ext2_add_nondir(dentry, inode);
 	}
-	return err;
+	return err ? ERR_PTR(err) : dentry;
 }
 
 static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t rdev)
@@ -237,6 +280,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
 	if (err)
 		goto out_fail;
 
+	dentry->d_op = &ext2_dentry_operations;
 	d_instantiate(dentry, inode);
 out:
 	return err;
@@ -250,26 +294,11 @@ out_dir:
 	goto out;
 }
 
-static int ext2_unlink(struct inode * dir, struct dentry *dentry)
+int ext2_unlink(struct inode *dir, struct dentry *dentry)
 {
-	struct inode * inode = dentry->d_inode;
-	struct ext2_dir_entry_2 * de;
-	struct page * page;
-	int err = -ENOENT;
-
-	de = ext2_find_entry (dir, dentry, &page);
-	if (!de)
-		goto out;
-
-	err = ext2_delete_entry (de, page);
-	if (err)
-		goto out;
-
-	inode->i_ctime = dir->i_ctime;
-	inode_dec_link_count(inode);
-	err = 0;
-out:
-	return err;
+	struct page *page;
+	struct ext2_dir_entry_2 *de = ext2_find_entry(dir, dentry, &page);
+	return de ? ext2_delete_entry(de, page) : -ENOENT;
 }
 
 static int ext2_rmdir (struct inode * dir, struct dentry *dentry)
@@ -377,7 +406,7 @@ const struct inode_operations ext2_dir_inode_operations = {
 	.create		= ext2_create,
 	.lookup		= ext2_lookup,
 	.link		= ext2_link,
-	.unlink		= ext2_unlink,
+	.unlink		= defer_unlink,
 	.symlink	= ext2_symlink,
 	.mkdir		= ext2_mkdir,
 	.rmdir		= ext2_rmdir,
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index ef50cbc..e8066b3 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -295,17 +295,52 @@ static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, siz
 static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off);
 #endif
 
+extern spinlock_t inode_lock;
+
+static inline void show_inode(char *tag, struct inode *inode)
+{
+	printk(">>> %s: %p/%i %x\n", tag, inode,
+		atomic_read(&inode->i_count), inode->i_flags);
+}
+
+static void defer_drop_inode(struct inode *inode)
+{
+	if (inode->i_nlink) {
+		generic_drop_inode(inode);
+		return;
+	}
+	show_inode("defer inode delete", inode);
+	inode->i_state |= I_DIRTY;
+	list_move(&inode->i_list, &EXT2_SB(inode->i_sb)->delete);
+	spin_unlock(&inode_lock);
+}
+
+extern struct list_head inode_unused;
+
+static int ext2_sync_fs(struct super_block *sb, int wait)
+{
+	while (!list_empty(&EXT2_SB(sb)->delete)) {
+		struct inode *inode = list_entry(EXT2_SB(sb)->delete.next, struct inode, i_list);
+		show_inode("delete deferred inode", inode);
+		spin_lock(&inode_lock);
+		generic_delete_inode(inode); /* removes from list and drops lock */
+	}
+	return 0;
+}
+
 static const struct super_operations ext2_sops = {
 	.alloc_inode	= ext2_alloc_inode,
 	.destroy_inode	= ext2_destroy_inode,
 	.write_inode	= ext2_write_inode,
 	.delete_inode	= ext2_delete_inode,
+	.drop_inode	= defer_drop_inode,
 	.put_super	= ext2_put_super,
 	.write_super	= ext2_write_super,
 	.statfs		= ext2_statfs,
 	.remount_fs	= ext2_remount,
 	.clear_inode	= ext2_clear_inode,
 	.show_options	= ext2_show_options,
+	.sync_fs	= ext2_sync_fs,
 #ifdef CONFIG_QUOTA
 	.quota_read	= ext2_quota_read,
 	.quota_write	= ext2_quota_write,
@@ -614,6 +649,7 @@ static int ext2_setup_super (struct super_block * sb,
 			EXT2_BLOCKS_PER_GROUP(sb),
 			EXT2_INODES_PER_GROUP(sb),
 			sbi->s_mount_opt);
+	INIT_LIST_HEAD(&sbi->delete);
 	return res;
 }
 
diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
index 0b8cf80..217ff66 100644
--- a/fs/ext3/namei.c
+++ b/fs/ext3/namei.c
@@ -1682,7 +1682,7 @@ static int ext3_add_nondir(handle_t *handle,
  * If the create succeeds, we fill in the inode information
  * with d_instantiate().
  */
-static int ext3_create (struct inode * dir, struct dentry * dentry, int mode,
+static struct dentry *ext3_create(struct inode *dir, struct dentry *dentry, int mode,
 		struct nameidata *nd)
 {
 	handle_t *handle;
@@ -1694,7 +1694,7 @@ retry:
 					EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
 					2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb));
 	if (IS_ERR(handle))
-		return PTR_ERR(handle);
+		return ERR_PTR(PTR_ERR(handle));
 
 	if (IS_DIRSYNC(dir))
 		handle->h_sync = 1;
@@ -1710,7 +1710,7 @@ retry:
 	ext3_journal_stop(handle);
 	if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
 		goto retry;
-	return err;
+	return err ? ERR_PTR(err) : dentry;
 }
 
 static int ext3_mknod (struct inode * dir, struct dentry *dentry,
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 5222345..9519ff7 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -592,7 +592,7 @@ static int init_inode(struct inode *inode, struct dentry *dentry)
 	return err;
 }
 
-int hostfs_create(struct inode *dir, struct dentry *dentry, int mode,
+struct dentry *hostfs_create(struct inode *dir, struct dentry *dentry, int mode,
 		  struct nameidata *nd)
 {
 	struct inode *inode;
@@ -634,7 +634,7 @@ int hostfs_create(struct inode *dir, struct dentry *dentry, int mode,
  out_put:
 	iput(inode);
  out:
-	return error;
+	return error ? ERR_PTR(error) : dentry;
 }
 
 struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry,
diff --git a/fs/namei.c b/fs/namei.c
index 3b26a24..165aa05 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -955,9 +955,9 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
 			break;
 
 		err = -ENOENT;
-		inode = next.dentry->d_inode;
-		if (!inode)
+		if (d_negative(next.dentry))
 			goto out_dput;
+		inode = next.dentry->d_inode;
 		err = -ENOTDIR; 
 		if (!inode->i_op)
 			goto out_dput;
@@ -967,9 +967,9 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
 			if (err)
 				goto return_err;
 			err = -ENOENT;
-			inode = nd->path.dentry->d_inode;
-			if (!inode)
+			if (d_negative(nd->path.dentry))
 				break;
+			inode = nd->path.dentry->d_inode;
 			err = -ENOTDIR; 
 			if (!inode->i_op)
 				break;
@@ -1077,7 +1077,7 @@ static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
 	if (path_walk(name, nd))
 		return 0;		/* something went wrong... */
 
-	if (!nd->path.dentry->d_inode ||
+	if (d_negative(nd->path.dentry) ||
 	    S_ISDIR(nd->path.dentry->d_inode->i_mode)) {
 		struct path old_path = nd->path;
 		struct qstr last = nd->last;
@@ -1094,7 +1094,7 @@ static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
 		path_get(&fs->root);
 		read_unlock(&fs->lock);
 		if (path_walk(name, nd) == 0) {
-			if (nd->path.dentry->d_inode) {
+			if (!d_negative(nd->path.dentry)) {
 				path_put(&old_path);
 				return 1;
 			}
@@ -1487,7 +1487,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
 {
 	int error;
 
-	if (!victim->d_inode)
+	if (d_negative(victim))
 		return -ENOENT;
 
 	BUG_ON(victim->d_parent->d_inode != dir);
@@ -1526,7 +1526,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
 static inline int may_create(struct inode *dir, struct dentry *child,
 			     struct nameidata *nd)
 {
-	if (child->d_inode)
+	if (!d_negative(child))
 		return -EEXIST;
 	if (IS_DEADDIR(dir))
 		return -ENOENT;
@@ -1593,37 +1593,43 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
 	}
 }
 
-int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
-		struct nameidata *nd)
+static struct dentry *namei_create(struct inode *dir, struct dentry *dentry,
+		int mode, struct nameidata *nd)
 {
 	int error = may_create(dir, dentry, nd);
 
 	if (error)
-		return error;
+		return ERR_PTR(error);
 
 	if (!dir->i_op || !dir->i_op->create)
-		return -EACCES;	/* shouldn't it be ENOSYS? */
+		return ERR_PTR(-EACCES); /* shouldn't it be ENOSYS? */
 	mode &= S_IALLUGO;
 	mode |= S_IFREG;
 	error = security_inode_create(dir, dentry, mode);
 	if (error)
-		return error;
+		return ERR_PTR(error);
 	DQUOT_INIT(dir);
-	error = dir->i_op->create(dir, dentry, mode, nd);
-	if (!error)
-		fsnotify_create(dir, dentry);
-	return error;
+	return dir->i_op->create(dir, dentry, mode, nd);
+}
+
+int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
+		struct nameidata *nd)
+{
+	struct dentry *result = namei_create(dir, dentry, mode, nd);
+	return IS_ERR(result) ? PTR_ERR(result) : 0;
 }
 
 int may_open(struct nameidata *nd, int acc_mode, int flag)
 {
 	struct dentry *dentry = nd->path.dentry;
-	struct inode *inode = dentry->d_inode;
+	struct inode *inode;
 	int error;
 
-	if (!inode)
+	if (d_negative(dentry))
 		return -ENOENT;
 
+	inode = dentry->d_inode;
+
 	if (S_ISLNK(inode->i_mode))
 		return -ELOOP;
 	
@@ -1828,7 +1834,7 @@ do_last:
 	}
 
 	/* Negative dentry, just create the file */
-	if (!path.dentry->d_inode) {
+	if (d_negative(path.dentry)) {
 		/*
 		 * This write is needed to ensure that a
 		 * ro->rw transition does not occur between
@@ -1866,14 +1872,14 @@ do_last:
 	}
 
 	error = -ENOENT;
-	if (!path.dentry->d_inode)
+	if (d_negative(path.dentry))
 		goto exit_dput;
 	if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
 		goto do_link;
 
 	path_to_nameidata(&path, &nd);
 	error = -EISDIR;
-	if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
+	if (!d_negative(path.dentry) && S_ISDIR(path.dentry->d_inode->i_mode))
 		goto exit;
 ok:
 	/*
@@ -2017,7 +2023,7 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
 	if (IS_ERR(dentry))
 		goto fail;
 
-	if (dentry->d_inode)
+	if (!d_negative(dentry))
 		goto eexist;
 	/*
 	 * Special case - lookup gave negative, but... we had foo/bar/
@@ -2381,7 +2387,7 @@ static long do_unlinkat(int dfd, const char __user *pathname)
 		/* Why not before? Because we want correct error value */
 		if (nd.last.name[nd.last.len])
 			goto slashes;
-		inode = dentry->d_inode;
+		inode = dentry->d_inode; // what to do here?
 		if (inode)
 			atomic_inc(&inode->i_count);
 		error = mnt_want_write(nd.path.mnt);
@@ -2402,7 +2408,7 @@ exit:
 	return error;
 
 slashes:
-	error = !dentry->d_inode ? -ENOENT :
+	error = d_negative(dentry) ? -ENOENT :
 		S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
 	goto exit2;
 }
@@ -2680,7 +2686,7 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
 
 	dget(new_dentry);
 	target = new_dentry->d_inode;
-	if (target)
+	if (target) // what to do here?
 		mutex_lock(&target->i_mutex);
 	if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
 		error = -EBUSY;
@@ -2710,7 +2716,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	if (error)
 		return error;
 
-	if (!new_dentry->d_inode)
+	if (d_negative(new_dentry))
 		error = may_create(new_dir, new_dentry, NULL);
 	else
 		error = may_delete(new_dir, new_dentry, is_dir);
@@ -2777,7 +2783,7 @@ static int do_rename(int olddfd, const char *oldname,
 		goto exit3;
 	/* source must exist */
 	error = -ENOENT;
-	if (!old_dentry->d_inode)
+	if (d_negative(old_dentry))
 		goto exit4;
 	/* unless the source is a directory trailing slashes give -ENOTDIR */
 	if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c
index b131234..b8e4cb8 100644
--- a/fs/ramfs/inode.c
+++ b/fs/ramfs/inode.c
@@ -117,9 +117,11 @@ static int ramfs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
 	return retval;
 }
 
-static int ramfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
+static struct dentry *ramfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
 {
-	return ramfs_mknod(dir, dentry, mode | S_IFREG, 0);
+	int err = ramfs_mknod(dir, dentry, mode | S_IFREG, 0);
+	return err ? ERR_PTR(err) : dentry;
+
 }
 
 static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index d982eb8..fb5666b 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -133,6 +133,7 @@ struct dentry_operations {
 	void (*d_release)(struct dentry *);
 	void (*d_iput)(struct dentry *, struct inode *);
 	char *(*d_dname)(struct dentry *, char *, int);
+	int (*d_hide)(struct dentry *);
 };
 
 /* the dentry parameter passed to d_hash and d_compare is the parent
@@ -176,6 +177,10 @@ d_iput:		no		no		no       yes
 
 #define DCACHE_INOTIFY_PARENT_WATCHED	0x0020 /* Parent inode is watched */
 
+#define DCACHE_HIDDEN		0x0040 /* Unlinked but referenced by an open file */
+#define DCACHE_BACKED		0x0080 /* FS has dirent */
+#define DCACHE_STALE		0x0100 /* FS has wrong dirent */
+
 extern spinlock_t dcache_lock;
 extern seqlock_t rename_lock;
 
@@ -341,6 +346,11 @@ static inline int d_unhashed(struct dentry *dentry)
 	return (dentry->d_flags & DCACHE_UNHASHED);
 }
 
+static inline int d_negative(struct dentry *dentry)
+{
+	return !dentry->d_inode || (dentry->d_flags & DCACHE_HIDDEN);
+}
+
 static inline struct dentry *dget_parent(struct dentry *dentry)
 {
 	struct dentry *ret;
@@ -363,4 +373,11 @@ extern struct dentry *lookup_create(struct nameidata *nd, int is_dir);
 
 extern int sysctl_vfs_cache_pressure;
 
+static inline void show_dentry(char *tag, struct dentry *dentry)
+{
+	printk(">>> %s: %p/%i %x %p\"%.*s\"\n", tag, dentry,
+		atomic_read(&dentry->d_count), dentry->d_flags, dentry->d_inode,
+		dentry->d_name.len, dentry->d_name.name);
+}
+
 #endif	/* __LINUX_DCACHE_H */
diff --git a/include/linux/ext2_fs_sb.h b/include/linux/ext2_fs_sb.h
index f273415..54ef185 100644
--- a/include/linux/ext2_fs_sb.h
+++ b/include/linux/ext2_fs_sb.h
@@ -106,6 +106,7 @@ struct ext2_sb_info {
 	spinlock_t s_rsv_window_lock;
 	struct rb_root s_rsv_window_root;
 	struct ext2_reserve_window_node s_rsv_window_head;
+	struct list_head delete;
 };
 
 #endif	/* _LINUX_EXT2_FS_SB */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index d8e2762..4523206 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1248,7 +1248,7 @@ struct file_operations {
 };
 
 struct inode_operations {
-	int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
+	struct dentry * (*create) (struct inode *,struct dentry *, int, struct nameidata *);
 	struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
 	int (*link) (struct dentry *,struct inode *,struct dentry *);
 	int (*unlink) (struct inode *,struct dentry *);
diff --git a/mm/shmem.c b/mm/shmem.c
index 8f8412b..942af9b 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1779,10 +1779,11 @@ static int shmem_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 	return 0;
 }
 
-static int shmem_create(struct inode *dir, struct dentry *dentry, int mode,
+static struct dentry *shmem_create(struct inode *dir, struct dentry *dentry, int mode,
 		struct nameidata *nd)
 {
-	return shmem_mknod(dir, dentry, mode | S_IFREG, 0);
+	int err = shmem_mknod(dir, dentry, mode | S_IFREG, 0);
+	return err ? ERR_PTR(err) : dentry;
 }
 
 /*
_______________________________________________
Tux3 mailing list
[email protected]
http://mailman.tux3.org/cgi-bin/mailman/listinfo/tux3

Reply via email to