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