2009-06-17 20:45:30

by Jan Blunck

[permalink] [raw]
Subject: [PATCH] Preparation work for VFS based union mounts

Al,
here are some patches that are indenpendant from but necessary for the union
mount patch set. Valerie and I think they are ok to go in separately from the
rest of the patches.

Thanks,
Jan

Jan Blunck (6):
VFS: BUG() if somebody tries to rehash an already hashed dentry
VFS: propagate mnt_flags into do_loopback
VFS: Make lookup_hash() return a struct path
VFS: Remove unnecessary micro-optimization in cached_lookup()
VFS: Make real_lookup() return a struct path
VFS: Introduce dput() variant that maintains a kill-list

fs/dcache.c | 116 ++++++++++++++++++++++++++++---
fs/namei.c | 209 +++++++++++++++++++++++++++++++-------------------------
fs/namespace.c | 7 +-
3 files changed, 225 insertions(+), 107 deletions(-)


2009-06-17 20:45:49

by Jan Blunck

[permalink] [raw]
Subject: [PATCH] VFS: BUG() if somebody tries to rehash an already hashed dentry

Break early when somebody tries to rehash an already hashed dentry.
Otherwise this leads to interesting corruptions in the dcache hash table
later on.

Signed-off-by: Jan Blunck <[email protected]>
Signed-off-by: Valerie Aurora (Henson) <[email protected]>
---
fs/dcache.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index 75659a6..191c488 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1550,6 +1550,7 @@ void d_rehash(struct dentry * entry)
{
spin_lock(&dcache_lock);
spin_lock(&entry->d_lock);
+ BUG_ON(!d_unhashed(entry));
_d_rehash(entry);
spin_unlock(&entry->d_lock);
spin_unlock(&dcache_lock);
--
1.6.0.2

2009-06-17 20:46:01

by Jan Blunck

[permalink] [raw]
Subject: [PATCH] VFS: propagate mnt_flags into do_loopback

The mnt_flags are propagated into do_loopback(), so that they can be checked
when mounting something loopback into a union.

Signed-off-by: Jan Blunck <[email protected]>
Signed-off-by: Valerie Aurora (Henson) <[email protected]>
---
fs/namespace.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index 134d494..bdff9e4 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1475,8 +1475,8 @@ static int do_change_type(struct path *path, int flag)
/*
* do loopback mount.
*/
-static int do_loopback(struct path *path, char *old_name,
- int recurse)
+static int do_loopback(struct path *path, char *old_name, int recurse,
+ int mnt_flags)
{
struct path old_path;
struct vfsmount *mnt = NULL;
@@ -1971,7 +1971,8 @@ long do_mount(char *dev_name, char *dir_name, char *type_page,
retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
data_page);
else if (flags & MS_BIND)
- retval = do_loopback(&path, dev_name, flags & MS_REC);
+ retval = do_loopback(&path, dev_name, flags & MS_REC,
+ mnt_flags);
else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
retval = do_change_type(&path, flags);
else if (flags & MS_MOVE)
--
1.6.0.2

2009-06-17 20:46:23

by Jan Blunck

[permalink] [raw]
Subject: [PATCH] VFS: Remove unnecessary micro-optimization in cached_lookup()

d_lookup() takes rename_lock which is a seq_lock. This is so cheap
it's not worth calling lockless __d_lookup() first from
cache_lookup(). Rename cached_lookup() to cache_lookup() while we're
there.

Signed-off-by: Jan Blunck <[email protected]>
Signed-off-by: Valerie Aurora (Henson) <[email protected]>
---
fs/namei.c | 13 ++++---------
1 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index c0cd4cb..9b12363 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -404,15 +404,10 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd)
* Internal lookup() using the new generic dcache.
* SMP-safe
*/
-static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
+static struct dentry *cache_lookup(struct dentry *parent, struct qstr *name,
+ struct nameidata *nd)
{
- struct dentry * dentry = __d_lookup(parent, name);
-
- /* lockess __d_lookup may fail due to concurrent d_move()
- * in some unrelated directory, so try with d_lookup
- */
- if (!dentry)
- dentry = d_lookup(parent, name);
+ struct dentry *dentry = d_lookup(parent, name);

if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
dentry = do_revalidate(dentry, nd);
@@ -1172,7 +1167,7 @@ static struct dentry *__lookup_hash(struct qstr *name,
goto out;
}

- dentry = cached_lookup(base, name, nd);
+ dentry = cache_lookup(base, name, nd);
if (!dentry) {
struct dentry *new;

--
1.6.0.2

2009-06-17 20:46:39

by Jan Blunck

[permalink] [raw]
Subject: [PATCH] VFS: Make lookup_hash() return a struct path

This patch changes lookup_hash() into returning a struct path.

Signed-off-by: Jan Blunck <[email protected]>
Signed-off-by: Valerie Aurora (Henson) <[email protected]>
---
fs/namei.c | 114 +++++++++++++++++++++++++++++++----------------------------
1 files changed, 60 insertions(+), 54 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 967c3db..c0cd4cb 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1153,7 +1153,7 @@ static int path_lookup_open(int dfd, const char *name,
}

static struct dentry *__lookup_hash(struct qstr *name,
- struct dentry *base, struct nameidata *nd)
+ struct dentry *base, struct nameidata *nd)
{
struct dentry *dentry;
struct inode *inode;
@@ -1200,14 +1200,22 @@ out:
* needs parent already locked. Doesn't follow mounts.
* SMP-safe.
*/
-static struct dentry *lookup_hash(struct nameidata *nd)
+static int lookup_hash(struct nameidata *nd, struct qstr *name,
+ struct path *path)
{
int err;

err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC);
if (err)
- return ERR_PTR(err);
- return __lookup_hash(&nd->last, nd->path.dentry, nd);
+ return err;
+ path->mnt = nd->path.mnt;
+ path->dentry = __lookup_hash(name, nd->path.dentry, nd);
+ if (IS_ERR(path->dentry)) {
+ err = PTR_ERR(path->dentry);
+ path->dentry = NULL;
+ path->mnt = NULL;
+ }
+ return err;
}

static int __lookup_one_len(const char *name, struct qstr *this,
@@ -1700,12 +1708,10 @@ struct file *do_filp_open(int dfd, const char *pathname,
if (flag & O_EXCL)
nd.flags |= LOOKUP_EXCL;
mutex_lock(&dir->d_inode->i_mutex);
- path.dentry = lookup_hash(&nd);
- path.mnt = nd.path.mnt;
+ error = lookup_hash(&nd, &nd.last, &path);

do_last:
- error = PTR_ERR(path.dentry);
- if (IS_ERR(path.dentry)) {
+ if (error) {
mutex_unlock(&dir->d_inode->i_mutex);
goto exit;
}
@@ -1851,8 +1857,7 @@ do_link:
}
dir = nd.path.dentry;
mutex_lock(&dir->d_inode->i_mutex);
- path.dentry = lookup_hash(&nd);
- path.mnt = nd.path.mnt;
+ error = lookup_hash(&nd, &nd.last, &path);
__putname(nd.last.name);
goto do_last;
}
@@ -1886,7 +1891,8 @@ EXPORT_SYMBOL(filp_open);
*/
struct dentry *lookup_create(struct nameidata *nd, int is_dir)
{
- struct dentry *dentry = ERR_PTR(-EEXIST);
+ struct path path = { .dentry = ERR_PTR(-EEXIST) } ;
+ int err;

mutex_lock_nested(&nd->path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
/*
@@ -1902,11 +1908,13 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
/*
* Do the final lookup.
*/
- dentry = lookup_hash(nd);
- if (IS_ERR(dentry))
+ err = lookup_hash(nd, &nd->last, &path);
+ if (err) {
+ path.dentry = ERR_PTR(err);
goto fail;
+ }

- if (dentry->d_inode)
+ if (path.dentry->d_inode)
goto eexist;
/*
* Special case - lookup gave negative, but... we had foo/bar/
@@ -1915,15 +1923,17 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
* been asking for (non-existent) directory. -ENOENT for you.
*/
if (unlikely(!is_dir && nd->last.name[nd->last.len])) {
- dput(dentry);
- dentry = ERR_PTR(-ENOENT);
+ path_put_conditional(&path, nd);
+ path.dentry = ERR_PTR(-ENOENT);
}
- return dentry;
+ if (nd->path.mnt != path.mnt)
+ mntput(path.mnt);
+ return path.dentry;
eexist:
- dput(dentry);
- dentry = ERR_PTR(-EEXIST);
+ path_put_conditional(&path, nd);
+ path.dentry = ERR_PTR(-EEXIST);
fail:
- return dentry;
+ return path.dentry;
}
EXPORT_SYMBOL_GPL(lookup_create);

@@ -2160,7 +2170,7 @@ static long do_rmdir(int dfd, const char __user *pathname)
{
int error = 0;
char * name;
- struct dentry *dentry;
+ struct path path;
struct nameidata nd;

error = user_path_parent(dfd, pathname, &nd, &name);
@@ -2182,21 +2192,20 @@ static long do_rmdir(int dfd, const char __user *pathname)
nd.flags &= ~LOOKUP_PARENT;

mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
- dentry = lookup_hash(&nd);
- error = PTR_ERR(dentry);
- if (IS_ERR(dentry))
+ error = lookup_hash(&nd, &nd.last, &path);
+ if (error)
goto exit2;
error = mnt_want_write(nd.path.mnt);
if (error)
goto exit3;
- error = security_path_rmdir(&nd.path, dentry);
+ error = security_path_rmdir(&nd.path, path.dentry);
if (error)
goto exit4;
- error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+ error = vfs_rmdir(nd.path.dentry->d_inode, path.dentry);
exit4:
mnt_drop_write(nd.path.mnt);
exit3:
- dput(dentry);
+ path_put_conditional(&path, &nd);
exit2:
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
exit1:
@@ -2251,7 +2260,7 @@ static long do_unlinkat(int dfd, const char __user *pathname)
{
int error;
char *name;
- struct dentry *dentry;
+ struct path path;
struct nameidata nd;
struct inode *inode = NULL;

@@ -2266,26 +2275,25 @@ static long do_unlinkat(int dfd, const char __user *pathname)
nd.flags &= ~LOOKUP_PARENT;

mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
- dentry = lookup_hash(&nd);
- error = PTR_ERR(dentry);
- if (!IS_ERR(dentry)) {
+ error = lookup_hash(&nd, &nd.last, &path);
+ if (!error) {
/* Why not before? Because we want correct error value */
if (nd.last.name[nd.last.len])
goto slashes;
- inode = dentry->d_inode;
+ inode = path.dentry->d_inode;
if (inode)
atomic_inc(&inode->i_count);
error = mnt_want_write(nd.path.mnt);
if (error)
goto exit2;
- error = security_path_unlink(&nd.path, dentry);
+ error = security_path_unlink(&nd.path, path.dentry);
if (error)
goto exit3;
- error = vfs_unlink(nd.path.dentry->d_inode, dentry);
+ error = vfs_unlink(nd.path.dentry->d_inode, path.dentry);
exit3:
mnt_drop_write(nd.path.mnt);
exit2:
- dput(dentry);
+ path_put_conditional(&path, &nd);
}
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
if (inode)
@@ -2296,8 +2304,8 @@ exit1:
return error;

slashes:
- error = !dentry->d_inode ? -ENOENT :
- S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+ error = !path.dentry->d_inode ? -ENOENT :
+ S_ISDIR(path.dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
goto exit2;
}

@@ -2637,7 +2645,7 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
int, newdfd, const char __user *, newname)
{
struct dentry *old_dir, *new_dir;
- struct dentry *old_dentry, *new_dentry;
+ struct path old, new;
struct dentry *trap;
struct nameidata oldnd, newnd;
char *from;
@@ -2671,16 +2679,15 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,

trap = lock_rename(new_dir, old_dir);

- old_dentry = lookup_hash(&oldnd);
- error = PTR_ERR(old_dentry);
- if (IS_ERR(old_dentry))
+ error = lookup_hash(&oldnd, &oldnd.last, &old);
+ if (error)
goto exit3;
/* source must exist */
error = -ENOENT;
- if (!old_dentry->d_inode)
+ if (!old.dentry->d_inode)
goto exit4;
/* unless the source is a directory trailing slashes give -ENOTDIR */
- if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
+ if (!S_ISDIR(old.dentry->d_inode->i_mode)) {
error = -ENOTDIR;
if (oldnd.last.name[oldnd.last.len])
goto exit4;
@@ -2689,32 +2696,31 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
}
/* source should not be ancestor of target */
error = -EINVAL;
- if (old_dentry == trap)
+ if (old.dentry == trap)
goto exit4;
- new_dentry = lookup_hash(&newnd);
- error = PTR_ERR(new_dentry);
- if (IS_ERR(new_dentry))
+ error = lookup_hash(&newnd, &newnd.last, &new);
+ if (error)
goto exit4;
/* target should not be an ancestor of source */
error = -ENOTEMPTY;
- if (new_dentry == trap)
+ if (new.dentry == trap)
goto exit5;

error = mnt_want_write(oldnd.path.mnt);
if (error)
goto exit5;
- error = security_path_rename(&oldnd.path, old_dentry,
- &newnd.path, new_dentry);
+ error = security_path_rename(&oldnd.path, old.dentry,
+ &newnd.path, new.dentry);
if (error)
goto exit6;
- error = vfs_rename(old_dir->d_inode, old_dentry,
- new_dir->d_inode, new_dentry);
+ error = vfs_rename(old_dir->d_inode, old.dentry,
+ new_dir->d_inode, new.dentry);
exit6:
mnt_drop_write(oldnd.path.mnt);
exit5:
- dput(new_dentry);
+ path_put_conditional(&new, &newnd);
exit4:
- dput(old_dentry);
+ path_put_conditional(&old, &oldnd);
exit3:
unlock_rename(new_dir, old_dir);
exit2:
--
1.6.0.2

2009-06-17 20:46:55

by Jan Blunck

[permalink] [raw]
Subject: [PATCH] VFS: Introduce dput() variant that maintains a kill-list

This patch introduces a new variant of dput(). This becomes necessary to
prevent a recursive call to dput() from the union mount code.

void __dput(struct dentry *dentry, struct list_head *list, int greedy);
struct dentry *__d_kill(struct dentry *dentry, struct list_head *list,
int greedy);

__dput() works mostly like the original dput() did. The main difference is
that if it the greedy argument is zero it will put the parent on a special
list instead of trying to get rid of it directly.

Therefore the union mount code can safely call __dput() when it wants to get
rid of underlying dentry references during a dput(). After calling __dput()
or __d_kill() the caller must make sure that __d_kill_final() is called on all
dentries on the kill list. __d_kill_final() is actually doing the
dentry_iput() and is also dereferencing the parent.

Signed-off-by: Jan Blunck <[email protected]>
Signed-off-by: Valerie Aurora (Henson) <[email protected]>
---
fs/dcache.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 105 insertions(+), 10 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index 191c488..7ab322e 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -157,14 +157,19 @@ static void dentry_lru_del_init(struct dentry *dentry)
}

/**
- * d_kill - kill dentry and return parent
+ * __d_kill - kill dentry and return parent
* @dentry: dentry to kill
+ * @list: kill list
+ * @greedy: return parent instead of putting it on the kill list
*
* The dentry must already be unhashed and removed from the LRU.
*
- * If this is the root of the dentry tree, return NULL.
+ * If this is the root of the dentry tree, return NULL. If greedy is zero, we
+ * put the parent of this dentry on the kill list instead. The callers must
+ * make sure that __d_kill_final() is called on all dentries on the kill list.
*/
-static struct dentry *d_kill(struct dentry *dentry)
+static struct dentry *__d_kill(struct dentry *dentry, struct list_head *list,
+ int greedy)
__releases(dentry->d_lock)
__releases(dcache_lock)
{
@@ -172,6 +177,20 @@ static struct dentry *d_kill(struct dentry *dentry)

list_del(&dentry->d_u.d_child);
dentry_stat.nr_dentry--; /* For d_free, below */
+
+ /*
+ * If we are not greedy we just put this on a list for later processing
+ * (follow up to parent, releasing of inode and freeing dentry memory).
+ */
+ if (!greedy) {
+ list_del_init(&dentry->d_alias);
+ /* at this point nobody can reach this dentry */
+ list_add(&dentry->d_lru, list);
+ spin_unlock(&dentry->d_lock);
+ spin_unlock(&dcache_lock);
+ return NULL;
+ }
+
/*drops the locks, at that point nobody can reach this dentry */
dentry_iput(dentry);
if (IS_ROOT(dentry))
@@ -182,6 +201,54 @@ static struct dentry *d_kill(struct dentry *dentry)
return parent;
}

+void __dput(struct dentry *, struct list_head *, int);
+
+static void __d_kill_final(struct dentry *dentry, struct list_head *list)
+{
+ struct dentry *parent;
+ struct inode *inode = dentry->d_inode;
+
+ if (inode) {
+ dentry->d_inode = NULL;
+ if (!inode->i_nlink)
+ fsnotify_inoderemove(inode);
+ if (dentry->d_op && dentry->d_op->d_iput)
+ dentry->d_op->d_iput(dentry, inode);
+ else
+ iput(inode);
+ }
+
+ if (IS_ROOT(dentry))
+ parent = NULL;
+ else
+ parent = dentry->d_parent;
+ d_free(dentry);
+ __dput(parent, list, 1);
+}
+
+/**
+ * d_kill - kill dentry and return parent
+ * @dentry: dentry to kill
+ *
+ * The dentry must already be unhashed and removed from the LRU.
+ *
+ * If this is the root of the dentry tree, return NULL.
+ */
+static struct dentry *d_kill(struct dentry *dentry)
+{
+ LIST_HEAD(mortuary);
+ struct dentry *parent;
+
+ parent = __d_kill(dentry, &mortuary, 1);
+ while (!list_empty(&mortuary)) {
+ dentry = list_entry(mortuary.next, struct dentry, d_lru);
+ list_del(&dentry->d_lru);
+ __d_kill_final(dentry, &mortuary);
+ }
+
+ return parent;
+}
+
/*
* This is dput
*
@@ -199,19 +266,24 @@ static struct dentry *d_kill(struct dentry *dentry)
* Real recursion would eat up our stack space.
*/

-/*
- * dput - release a dentry
- * @dentry: dentry to release
+/**
+ * __dput - release a dentry
+ * @dentry: dentry to release
+ * @list: kill list argument for __d_kill()
+ * @greedy: greedy argument for __d_kill()
*
* Release a dentry. This will drop the usage count and if appropriate
* call the dentry unlink method as well as removing it from the queues and
* releasing its resources. If the parent dentries were scheduled for release
- * they too may now get deleted.
+ * they too may now get deleted if @greedy is not zero. Otherwise parent is
+ * added to the kill list. The callers must make sure that __d_kill_final() is
+ * called on all dentries on the kill list.
+ *
+ * You probably want to use dput() instead.
*
* no dcache lock, please.
*/
-
-void dput(struct dentry *dentry)
+void __dput(struct dentry *dentry, struct list_head *list, int greedy)
{
if (!dentry)
return;
@@ -252,12 +324,35 @@ unhash_it:
kill_it:
/* if dentry was on the d_lru list delete it from there */
dentry_lru_del(dentry);
- dentry = d_kill(dentry);
+ dentry = __d_kill(dentry, list, greedy);
if (dentry)
goto repeat;
}

/**
+ * dput - release a dentry
+ * @dentry: dentry to release
+ *
+ * Release a dentry. This will drop the usage count and if appropriate
+ * call the dentry unlink method as well as removing it from the queues and
+ * releasing its resources. If the parent dentries were scheduled for release
+ * they too may now get deleted.
+ *
+ * no dcache lock, please.
+ */
+void dput(struct dentry *dentry)
+{
+ LIST_HEAD(mortuary);
+
+ __dput(dentry, &mortuary, 1);
+ while (!list_empty(&mortuary)) {
+ dentry = list_entry(mortuary.next, struct dentry, d_lru);
+ list_del(&dentry->d_lru);
+ __d_kill_final(dentry, &mortuary);
+ }
+}
+
+/**
* d_invalidate - invalidate a dentry
* @dentry: dentry to invalidate
*
--
1.6.0.2

2009-06-17 20:47:16

by Jan Blunck

[permalink] [raw]
Subject: [PATCH] VFS: Make real_lookup() return a struct path

This patch changes real_lookup() into returning a struct path.

Signed-off-by: Jan Blunck <[email protected]>
Signed-off-by: Valerie Aurora (Henson) <[email protected]>
---
fs/namei.c | 82 +++++++++++++++++++++++++++++++++++++----------------------
1 files changed, 51 insertions(+), 31 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 9b12363..f40f42c 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -462,10 +462,11 @@ ok:
* make sure that nobody added the entry to the dcache in the meantime..
* SMP-safe
*/
-static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
+static int real_lookup(struct nameidata *nd, struct qstr *name,
+ struct path *path)
{
- struct dentry * result;
- struct inode *dir = parent->d_inode;
+ struct inode *dir = nd->path.dentry->d_inode;
+ int res = 0;

mutex_lock(&dir->i_mutex);
/*
@@ -482,27 +483,36 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
*
* so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
*/
- result = d_lookup(parent, name);
- if (!result) {
+ path->dentry = d_lookup(nd->path.dentry, name);
+ path->mnt = nd->path.mnt;
+ if (!path->dentry) {
struct dentry *dentry;

/* Don't create child dentry for a dead directory. */
- result = ERR_PTR(-ENOENT);
- if (IS_DEADDIR(dir))
+ if (IS_DEADDIR(dir)) {
+ res = -ENOENT;
goto out_unlock;
+ }

- dentry = d_alloc(parent, name);
- result = ERR_PTR(-ENOMEM);
+ dentry = d_alloc(nd->path.dentry, name);
if (dentry) {
- result = dir->i_op->lookup(dir, dentry, nd);
- if (result)
+ path->dentry = dir->i_op->lookup(dir, dentry, nd);
+ if (path->dentry) {
dput(dentry);
- else
- result = dentry;
+ if (IS_ERR(path->dentry)) {
+ res = PTR_ERR(path->dentry);
+ path->dentry = NULL;
+ path->mnt = NULL;
+ }
+ } else
+ path->dentry = dentry;
+ } else {
+ res = -ENOMEM;
+ path->mnt = NULL;
}
out_unlock:
mutex_unlock(&dir->i_mutex);
- return result;
+ return res;
}

/*
@@ -510,12 +520,20 @@ out_unlock:
* we waited on the semaphore. Need to revalidate.
*/
mutex_unlock(&dir->i_mutex);
- if (result->d_op && result->d_op->d_revalidate) {
- result = do_revalidate(result, nd);
- if (!result)
- result = ERR_PTR(-ENOENT);
+ if (path->dentry->d_op && path->dentry->d_op->d_revalidate) {
+ path->dentry = do_revalidate(path->dentry, nd);
+ if (!path->dentry) {
+ res = -ENOENT;
+ path->mnt = NULL;
+ }
+ if (IS_ERR(path->dentry)) {
+ res = PTR_ERR(path->dentry);
+ path->dentry = NULL;
+ path->mnt = NULL;
+ }
}
- return result;
+
+ return res;
}

/*
@@ -781,35 +799,37 @@ static __always_inline void follow_dotdot(struct nameidata *nd)
static int do_lookup(struct nameidata *nd, struct qstr *name,
struct path *path)
{
- struct vfsmount *mnt = nd->path.mnt;
- struct dentry *dentry = __d_lookup(nd->path.dentry, name);
+ int err;

- if (!dentry)
+ path->dentry = __d_lookup(nd->path.dentry, name);
+ path->mnt = nd->path.mnt;
+ if (!path->dentry)
goto need_lookup;
- if (dentry->d_op && dentry->d_op->d_revalidate)
+ if (path->dentry->d_op && path->dentry->d_op->d_revalidate)
goto need_revalidate;
+
done:
- path->mnt = mnt;
- path->dentry = dentry;
__follow_mount(path);
return 0;

need_lookup:
- dentry = real_lookup(nd->path.dentry, name, nd);
- if (IS_ERR(dentry))
+ err = real_lookup(nd, name, path);
+ if (err)
goto fail;
goto done;

need_revalidate:
- dentry = do_revalidate(dentry, nd);
- if (!dentry)
+ path->dentry = do_revalidate(path->dentry, nd);
+ if (!path->dentry)
goto need_lookup;
- if (IS_ERR(dentry))
+ if (IS_ERR(path->dentry)) {
+ err = PTR_ERR(path->dentry);
goto fail;
+ }
goto done;

fail:
- return PTR_ERR(dentry);
+ return err;
}

/*
--
1.6.0.2

2009-06-18 11:20:22

by James Morris

[permalink] [raw]
Subject: Re: [PATCH] VFS: Make lookup_hash() return a struct path

On Wed, 17 Jun 2009, Jan Blunck wrote:

> This patch changes lookup_hash() into returning a struct path.
>
> Signed-off-by: Jan Blunck <[email protected]>
> Signed-off-by: Valerie Aurora (Henson) <[email protected]>

Reviewed-by: James Morris <[email protected]>


--
James Morris
<[email protected]>

2009-06-18 11:22:32

by James Morris

[permalink] [raw]
Subject: Re: [PATCH] VFS: Make real_lookup() return a struct path

On Wed, 17 Jun 2009, Jan Blunck wrote:

> This patch changes real_lookup() into returning a struct path.
>
> Signed-off-by: Jan Blunck <[email protected]>
> Signed-off-by: Valerie Aurora (Henson) <[email protected]>

Reviewed-by: James Morris <[email protected]>

--
James Morris
<[email protected]>

2009-06-18 11:31:07

by James Morris

[permalink] [raw]
Subject: Re: [PATCH] VFS: Introduce dput() variant that maintains a kill-list

On Wed, 17 Jun 2009, Jan Blunck wrote:

> This patch introduces a new variant of dput(). This becomes necessary to
> prevent a recursive call to dput() from the union mount code.

Reviewed-by: James Morris <[email protected]>

--
James Morris <[email protected]>