The current patch and diff from previous are attached. This seems to
handle most combinations of touch, rm and fsync and umount (note: must
remember to fsync the test directory before umount because the patch
does not keep track of dirty directories yet).
The new patch adds deferred create handling. Various stupid bugs are
fixed, and the dentry state flag handling is more logical.
Create of an in-use negative dentry is not handled yet. In this case
the in-use dentry has to be unhashed atomically and a new dentry
created as a copy of the unhashed dentry. Rename is not supported yet.
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/dcache.c b/fs/dcache.c
index 6068c25..a61d7ab 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;
@@ -973,6 +973,7 @@ void d_instantiate(struct dentry *entry, struct inode * 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);
@@ -1024,6 +1025,7 @@ static struct dentry *__d_instantiate_unique(struct dentry *entry,
list_add(&entry->d_alias, &inode->i_dentry);
entry->d_inode = inode;
+ entry->d_flags &= ~DCACHE_HIDDEN;
fsnotify_d_instantiate(entry, inode);
return NULL;
}
@@ -1387,20 +1389,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 +1518,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 +2107,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..b076fd7 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);
}
@@ -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/namei.c b/fs/namei.c
index 3b26a24..f4eae06 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;
@@ -1618,12 +1618,14 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
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 +1830,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 +1868,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 +2019,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 +2383,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 +2404,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 +2682,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 +2712,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 +2779,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/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/fs/dcache.c b/fs/dcache.c
index 94b3656..a61d7ab 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -973,6 +973,7 @@ void d_instantiate(struct dentry *entry, struct inode * 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);
@@ -1024,6 +1025,7 @@ static struct dentry *__d_instantiate_unique(struct dentry *entry,
list_add(&entry->d_alias, &inode->i_dentry);
entry->d_inode = inode;
+ entry->d_flags &= ~DCACHE_HIDDEN;
fsnotify_d_instantiate(entry, inode);
return NULL;
}
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index 06b5d05..c22db38 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -270,7 +270,7 @@ static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode)
de->file_type = 0;
}
-int ext2_unlink_deferred(struct inode *dir, struct dentry *dentry);
+int ext2_unlink(struct inode *dir, struct dentry *dentry);
void dentry_iput(struct dentry *dentry);
int ext2_flush_dir(struct dentry *dir)
@@ -280,30 +280,46 @@ int ext2_flush_dir(struct dentry *dir)
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;
- show_dentry("dentry", dentry);
- next = next->next;
spin_lock(&dentry->d_lock);
if (d_negative(dentry)) {
- if ((dentry->d_flags & DCACHE_HIDDEN)) {
- dentry->d_flags &= ~DCACHE_HIDDEN;
- show_dentry("drop hidden dentry", dentry);
- if ((dentry->d_flags & DCACHE_BACKED)) {
- dentry->d_flags &= ~DCACHE_BACKED;
- show_dentry("deferred unlink", dentry);
- spin_unlock(&dentry->d_lock);
- spin_unlock(&dcache_lock);
- ext2_unlink_deferred(dir->d_inode, dentry);
- } else {
- spin_unlock(&dentry->d_lock);
- spin_unlock(&dcache_lock);
+ 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);
}
- d_delete(dentry);
dput(dentry);
spin_lock(&dcache_lock);
+ continue;
}
}
+ spin_unlock(&dentry->d_lock);
}
spin_unlock(&dcache_lock);
return 0; // really??
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index bdd640c..b076fd7 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -41,10 +41,14 @@ 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;
}
@@ -52,7 +56,15 @@ static struct dentry_operations ext2_dentry_operations = {
.d_hide = ext2_hide_dentry,
};
-static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
+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) {
@@ -65,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.
*/
@@ -83,8 +106,9 @@ 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_flags |= DCACHE_BACKED;
dentry->d_op = &ext2_dentry_operations;
return d_splice_alias(inode, dentry);
}
@@ -270,43 +294,13 @@ out_dir:
goto out;
}
-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 ext2_unlink_deferred(struct inode *dir, struct dentry *dentry)
+int ext2_unlink(struct inode *dir, struct dentry *dentry)
{
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_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;
-}
-
static int ext2_rmdir (struct inode * dir, struct dentry *dentry)
{
struct inode * inode = dentry->d_inode;
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index c434d8d..fb5666b 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -176,9 +176,10 @@ d_iput: no no no yes
#define DCACHE_UNHASHED 0x0010
#define DCACHE_INOTIFY_PARENT_WATCHED 0x0020 /* Parent inode is watched */
-#define DCACHE_HIDDEN 0x0020 /* Unlinked but referenced by an open file */
-#define DCACHE_BACKED 0x0040 /* FS has dirent */
-#define DCACHE_WRONG 0x0080 /* FS has wrong dirent */
+
+#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;
@@ -374,8 +375,8 @@ extern int sysctl_vfs_cache_pressure;
static inline void show_dentry(char *tag, struct dentry *dentry)
{
- printk(">>> %s: %p/%i %x \"%.*s\"\n", tag, dentry,
- atomic_read(&dentry->d_count), dentry->d_flags,
+ 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);
}
_______________________________________________
Tux3 mailing list
[email protected]
http://mailman.tux3.org/cgi-bin/mailman/listinfo/tux3