From 90ebe5654febe3555a2516d51d3d251226d35fdb Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:35:58 -0700 Subject: [PATCH] namei fixes OK, here comes a patch series that hopefully should close all too-early-mntput() races in fs/namei.c. Entire area is convoluted as hell, so I'm splitting that series into _very_ small chunks. Patches alread in the tree close only (very wide) races in following symlinks (see "busy inodes after umount" thread some time ago). Unfortunately, quite a few narrower races of the same nature were not closed. Hopefully this should take care of all of them. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index dd78f01b6de..abeec34e755 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -493,6 +493,11 @@ fail: return PTR_ERR(link); } +struct path { + struct vfsmount *mnt; + struct dentry *dentry; +}; + static inline int __do_follow_link(struct dentry *dentry, struct nameidata *nd) { int error; @@ -518,7 +523,7 @@ static inline int __do_follow_link(struct dentry *dentry, struct nameidata *nd) * Without that kind of total limit, nasty chains of consecutive * symlinks can cause almost arbitrarily long lookups. */ -static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd) +static inline int do_follow_link(struct path *path, struct nameidata *nd) { int err = -ELOOP; if (current->link_count >= MAX_NESTED_LINKS) @@ -527,13 +532,13 @@ static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd) goto loop; BUG_ON(nd->depth >= MAX_NESTED_LINKS); cond_resched(); - err = security_inode_follow_link(dentry, nd); + err = security_inode_follow_link(path->dentry, nd); if (err) goto loop; current->link_count++; current->total_link_count++; nd->depth++; - err = __do_follow_link(dentry, nd); + err = __do_follow_link(path->dentry, nd); current->link_count--; nd->depth--; return err; @@ -641,11 +646,6 @@ static inline void follow_dotdot(struct vfsmount **mnt, struct dentry **dentry) follow_mount(mnt, dentry); } -struct path { - struct vfsmount *mnt; - struct dentry *dentry; -}; - /* * It's more convoluted than I'd like it to be, but... it's still fairly * small and for now I'd prefer to have fast path as straight as possible. @@ -784,7 +784,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) if (inode->i_op->follow_link) { mntget(next.mnt); - err = do_follow_link(next.dentry, nd); + err = do_follow_link(&next, nd); dput(next.dentry); mntput(next.mnt); if (err) @@ -838,7 +838,7 @@ last_component: if ((lookup_flags & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link) { mntget(next.mnt); - err = do_follow_link(next.dentry, nd); + err = do_follow_link(&next, nd); dput(next.dentry); mntput(next.mnt); if (err) -- cgit v1.2.3 From 5f92b3bcec0fa2e2d775b589850097f9dc6b2de2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:35:59 -0700 Subject: [PATCH] namei fixes (2/19) All callers of do_follow_link() do mntget() right before it and dput()+mntput() right after. These calls are moved inside do_follow_link() now. Obviously equivalent transformation. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index abeec34e755..12d75ed214f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -526,6 +526,7 @@ static inline int __do_follow_link(struct dentry *dentry, struct nameidata *nd) static inline int do_follow_link(struct path *path, struct nameidata *nd) { int err = -ELOOP; + mntget(path->mnt); if (current->link_count >= MAX_NESTED_LINKS) goto loop; if (current->total_link_count >= 40) @@ -541,9 +542,13 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd) err = __do_follow_link(path->dentry, nd); current->link_count--; nd->depth--; + dput(path->dentry); + mntput(path->mnt); return err; loop: path_release(nd); + dput(path->dentry); + mntput(path->mnt); return err; } @@ -783,10 +788,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) goto out_dput; if (inode->i_op->follow_link) { - mntget(next.mnt); err = do_follow_link(&next, nd); - dput(next.dentry); - mntput(next.mnt); if (err) goto return_err; err = -ENOENT; @@ -837,10 +839,7 @@ last_component: inode = next.dentry->d_inode; if ((lookup_flags & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link) { - mntget(next.mnt); err = do_follow_link(&next, nd); - dput(next.dentry); - mntput(next.mnt); if (err) goto return_err; inode = nd->dentry->d_inode; -- cgit v1.2.3 From 4e7506e4dd9e40c189fcbec95d5dbc92f2e5926a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:00 -0700 Subject: [PATCH] namei fixes (3/19) Replaced struct dentry *dentry in namei with struct path path. All uses of dentry replaced with path.dentry there. Obviously equivalent transformation. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 12d75ed214f..167f19b5e51 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1397,7 +1397,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) { int acc_mode, error = 0; - struct dentry *dentry; + struct path path; struct dentry *dir; int count = 0; @@ -1441,23 +1441,23 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) dir = nd->dentry; nd->flags &= ~LOOKUP_PARENT; down(&dir->d_inode->i_sem); - dentry = __lookup_hash(&nd->last, nd->dentry, nd); + path.dentry = __lookup_hash(&nd->last, nd->dentry, nd); do_last: - error = PTR_ERR(dentry); - if (IS_ERR(dentry)) { + error = PTR_ERR(path.dentry); + if (IS_ERR(path.dentry)) { up(&dir->d_inode->i_sem); goto exit; } /* Negative dentry, just create the file */ - if (!dentry->d_inode) { + if (!path.dentry->d_inode) { if (!IS_POSIXACL(dir->d_inode)) mode &= ~current->fs->umask; - error = vfs_create(dir->d_inode, dentry, mode, nd); + error = vfs_create(dir->d_inode, path.dentry, mode, nd); up(&dir->d_inode->i_sem); dput(nd->dentry); - nd->dentry = dentry; + nd->dentry = path.dentry; if (error) goto exit; /* Don't check for write permission, don't truncate */ @@ -1475,22 +1475,22 @@ do_last: if (flag & O_EXCL) goto exit_dput; - if (d_mountpoint(dentry)) { + if (d_mountpoint(path.dentry)) { error = -ELOOP; if (flag & O_NOFOLLOW) goto exit_dput; - while (__follow_down(&nd->mnt,&dentry) && d_mountpoint(dentry)); + while (__follow_down(&nd->mnt,&path.dentry) && d_mountpoint(path.dentry)); } error = -ENOENT; - if (!dentry->d_inode) + if (!path.dentry->d_inode) goto exit_dput; - if (dentry->d_inode->i_op && dentry->d_inode->i_op->follow_link) + if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link) goto do_link; dput(nd->dentry); - nd->dentry = dentry; + nd->dentry = path.dentry; error = -EISDIR; - if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) + if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode)) goto exit; ok: error = may_open(nd, acc_mode, flag); @@ -1499,7 +1499,7 @@ ok: return 0; exit_dput: - dput(dentry); + dput(path.dentry); exit: path_release(nd); return error; @@ -1519,16 +1519,16 @@ do_link: * are done. Procfs-like symlinks just set LAST_BIND. */ nd->flags |= LOOKUP_PARENT; - error = security_inode_follow_link(dentry, nd); + error = security_inode_follow_link(path.dentry, nd); if (error) goto exit_dput; - error = __do_follow_link(dentry, nd); - dput(dentry); + error = __do_follow_link(path.dentry, nd); + dput(path.dentry); if (error) return error; nd->flags &= ~LOOKUP_PARENT; if (nd->last_type == LAST_BIND) { - dentry = nd->dentry; + path.dentry = nd->dentry; goto ok; } error = -EISDIR; @@ -1545,7 +1545,7 @@ do_link: } dir = nd->dentry; down(&dir->d_inode->i_sem); - dentry = __lookup_hash(&nd->last, nd->dentry, nd); + path.dentry = __lookup_hash(&nd->last, nd->dentry, nd); putname(nd->last.name); goto do_last; } -- cgit v1.2.3 From d73ffe16b8baafae6e9249acee6b50c24099c6de Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:01 -0700 Subject: [PATCH] namei fixes (4/19) path.mnt in open_namei() set to mirror nd->mnt. nd->mnt is set in 3 places in that function - path_lookup() in the beginning, __follow_down() loop after do_last: and __do_follow_link() call after do_link:. We set path.mnt to nd->mnt after path_lookup() and __do_follow_link(). In __follow_down() loop we use &path.mnt instead of &nd->mnt and set nd->mnt to path.mnt immediately after that loop. Obviously equivalent transformation. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 167f19b5e51..e42f7c35545 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1442,6 +1442,7 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) nd->flags &= ~LOOKUP_PARENT; down(&dir->d_inode->i_sem); path.dentry = __lookup_hash(&nd->last, nd->dentry, nd); + path.mnt = nd->mnt; do_last: error = PTR_ERR(path.dentry); @@ -1479,7 +1480,8 @@ do_last: error = -ELOOP; if (flag & O_NOFOLLOW) goto exit_dput; - while (__follow_down(&nd->mnt,&path.dentry) && d_mountpoint(path.dentry)); + while (__follow_down(&path.mnt,&path.dentry) && d_mountpoint(path.dentry)); + nd->mnt = path.mnt; } error = -ENOENT; if (!path.dentry->d_inode) @@ -1524,6 +1526,7 @@ do_link: goto exit_dput; error = __do_follow_link(path.dentry, nd); dput(path.dentry); + path.mnt = nd->mnt; if (error) return error; nd->flags &= ~LOOKUP_PARENT; -- cgit v1.2.3 From 1be4a0900be5d2c2fd9cd012e3a153e1ea47b96a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:01 -0700 Subject: [PATCH] namei fixes (5/19) fix for too early mntput() in open_namei() - we pin path.mnt down for the duration of __do_follow_link(). Otherwise we could get the fs where our symlink lived unmounted while we were in __do_follow_link(). That would end up with dentry of symlink staying pinned down through the fs shutdown. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index e42f7c35545..020fb8c8d1c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1524,8 +1524,10 @@ do_link: error = security_inode_follow_link(path.dentry, nd); if (error) goto exit_dput; + mntget(path.mnt); error = __do_follow_link(path.dentry, nd); dput(path.dentry); + mntput(path.mnt); path.mnt = nd->mnt; if (error) return error; -- cgit v1.2.3 From 839d9f93c9f1623fb37234d464d739617879d97e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:02 -0700 Subject: [PATCH] namei fixes (6/19) mntget(path->mnt) in do_follow_link() moved down to right before the __do_follow_link() call and rigth after loop: resp. dput()+mntput() on non-ELOOP branch moved up to right after __do_follow_link() call. resulting loop: mntget(path->mnt); path_release(nd); dput(path->mnt); mntput(path->mnt); replaced with equivalent dput(path->mnt); path_release(nd); Equivalent transformations - the reason why we have that mntget() is that __do_follow_link() can drop a reference to nd->mnt and that's what holds path->mnt. So that call can happen at any point prior to __do_follow_link() touching nd->mnt. The rest is obvious. NOTE: current tree relies on symlinks *never* being mounted on anything. It's not hard to get rid of that assumption (actually, that will come for free later in the series). For now we are just not making the situation worse than it is. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 020fb8c8d1c..519900d83bc 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -526,7 +526,6 @@ static inline int __do_follow_link(struct dentry *dentry, struct nameidata *nd) static inline int do_follow_link(struct path *path, struct nameidata *nd) { int err = -ELOOP; - mntget(path->mnt); if (current->link_count >= MAX_NESTED_LINKS) goto loop; if (current->total_link_count >= 40) @@ -539,16 +538,16 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd) current->link_count++; current->total_link_count++; nd->depth++; + mntget(path->mnt); err = __do_follow_link(path->dentry, nd); - current->link_count--; - nd->depth--; dput(path->dentry); mntput(path->mnt); + current->link_count--; + nd->depth--; return err; loop: - path_release(nd); dput(path->dentry); - mntput(path->mnt); + path_release(nd); return err; } -- cgit v1.2.3 From cd4e91d3bca8d5527289f5984cf32e9fe6fb8293 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:03 -0700 Subject: [PATCH] namei fixes (7/19) The first argument of __do_follow_link() switched to struct path * (__do_follow_link(path->dentry, ...) -> __do_follow_link(path, ...)). All callers have the same calls of mntget() right before and dput()/mntput() right after __do_follow_link(); these calls have been moved inside. Obviously equivalent transformations. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 519900d83bc..18ea0606145 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -498,12 +498,15 @@ struct path { struct dentry *dentry; }; -static inline int __do_follow_link(struct dentry *dentry, struct nameidata *nd) +static inline int __do_follow_link(struct path *path, struct nameidata *nd) { int error; + struct dentry *dentry = path->dentry; touch_atime(nd->mnt, dentry); nd_set_link(nd, NULL); + + mntget(path->mnt); error = dentry->d_inode->i_op->follow_link(dentry, nd); if (!error) { char *s = nd_get_link(nd); @@ -512,6 +515,8 @@ static inline int __do_follow_link(struct dentry *dentry, struct nameidata *nd) if (dentry->d_inode->i_op->put_link) dentry->d_inode->i_op->put_link(dentry, nd); } + dput(dentry); + mntput(path->mnt); return error; } @@ -538,10 +543,7 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd) current->link_count++; current->total_link_count++; nd->depth++; - mntget(path->mnt); - err = __do_follow_link(path->dentry, nd); - dput(path->dentry); - mntput(path->mnt); + err = __do_follow_link(path, nd); current->link_count--; nd->depth--; return err; @@ -1523,10 +1525,7 @@ do_link: error = security_inode_follow_link(path.dentry, nd); if (error) goto exit_dput; - mntget(path.mnt); - error = __do_follow_link(path.dentry, nd); - dput(path.dentry); - mntput(path.mnt); + error = __do_follow_link(&path, nd); path.mnt = nd->mnt; if (error) return error; -- cgit v1.2.3 From d671d5e51400aab03c713a16ce3545aa81ad7b1c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:04 -0700 Subject: [PATCH] namei fixes (8/19) In open_namei() we never use path.mnt or path.dentry after exit: or ok:. Assignment of path.dentry in case of LAST_BIND is dead code and only obfuscates already convoluted function; assignment of path.mnt after __do_follow_link() can be moved down to the place where we set path.dentry. Obviously equivalent transformations, just to clean the air a bit in that region. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 18ea0606145..3d08478d313 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1526,14 +1526,11 @@ do_link: if (error) goto exit_dput; error = __do_follow_link(&path, nd); - path.mnt = nd->mnt; if (error) return error; nd->flags &= ~LOOKUP_PARENT; - if (nd->last_type == LAST_BIND) { - path.dentry = nd->dentry; + if (nd->last_type == LAST_BIND) goto ok; - } error = -EISDIR; if (nd->last_type != LAST_NORM) goto exit; @@ -1549,6 +1546,7 @@ do_link: dir = nd->dentry; down(&dir->d_inode->i_sem); path.dentry = __lookup_hash(&nd->last, nd->dentry, nd); + path.mnt = nd->mnt; putname(nd->last.name); goto do_last; } -- cgit v1.2.3 From 463ffb2e9d39c2a3fd8c3c1d4a34e01f2078f972 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:05 -0700 Subject: [PATCH] namei fixes (9/19) New helper: __follow_mount(struct path *path). Same as follow_mount(), except that we do *not* do mntput() after the first lookup_mnt(). IOW, original path->mnt stays pinned down. We also take care to do dput() before mntput() in the loop body (follow_mount() also needs that reordering, but that will be done later in the series). The following are equivalent, assuming that path.mnt == x: (1) follow_mount(&path.mnt, &path.dentry) (2) __follow_mount(&path); if (path->mnt != x) mntput(x); (3) if (__follow_mount(&path)) mntput(x); Callers of follow_mount() in __link_path_walk() converted to (2). Equivalent transformation + fix for too-late-mntput() race in __follow_mount() loop. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 3d08478d313..23a1ad46797 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -576,6 +576,23 @@ int follow_up(struct vfsmount **mnt, struct dentry **dentry) /* no need for dcache_lock, as serialization is taken care in * namespace.c */ +static int __follow_mount(struct path *path) +{ + int res = 0; + while (d_mountpoint(path->dentry)) { + struct vfsmount *mounted = lookup_mnt(path->mnt, path->dentry); + if (!mounted) + break; + dput(path->dentry); + if (res) + mntput(path->mnt); + path->mnt = mounted; + path->dentry = dget(mounted->mnt_root); + res = 1; + } + return res; +} + static int follow_mount(struct vfsmount **mnt, struct dentry **dentry) { int res = 0; @@ -778,7 +795,9 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) if (err) break; /* Check mountpoints.. */ - follow_mount(&next.mnt, &next.dentry); + __follow_mount(&next); + if (nd->mnt != next.mnt) + mntput(nd->mnt); err = -ENOENT; inode = next.dentry->d_inode; @@ -836,7 +855,9 @@ last_component: err = do_lookup(nd, &this, &next); if (err) break; - follow_mount(&next.mnt, &next.dentry); + __follow_mount(&next); + if (nd->mnt != next.mnt) + mntput(nd->mnt); inode = next.dentry->d_inode; if ((lookup_flags & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link) { -- cgit v1.2.3 From e13b210f6f7bdc44dfee0a9bbd633a32db0d6333 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:06 -0700 Subject: [PATCH] namei fixes (10/19) In open_namei(), __follow_down() loop turned into __follow_mount(). Instead of if we are on a mountpoint dentry if O_NOFOLLOW checks fail drop path.dentry drop nd return do equivalent of follow_mount(&path.mnt, &path.dentry) nd->mnt = path.mnt we do if __follow_mount(path) had, indeed, traversed mountpoint /* now both nd->mnt and path.mnt are pinned down */ if O_NOFOLLOW checks fail drop path.dentry drop path.mnt drop nd return mntput(nd->mnt) nd->mnt = path.mnt Now __follow_down() can be folded into follow_down() - no other callers left. We need to reorder dput()/mntput() there - same problem as in follow_mount(). Equivalent transformation + fix for a bug in O_NOFOLLOW handling - we used to get -ELOOP if we had the same fs mounted on /foo and /bar, had something bound on /bar/baz and tried to open /foo/baz with O_NOFOLLOW. And fix of too-early-mntput() race in follow_down() Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 23a1ad46797..935b08d8dcd 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -612,26 +612,21 @@ static int follow_mount(struct vfsmount **mnt, struct dentry **dentry) /* no need for dcache_lock, as serialization is taken care in * namespace.c */ -static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry) +int follow_down(struct vfsmount **mnt, struct dentry **dentry) { struct vfsmount *mounted; mounted = lookup_mnt(*mnt, *dentry); if (mounted) { + dput(*dentry); mntput(*mnt); *mnt = mounted; - dput(*dentry); *dentry = dget(mounted->mnt_root); return 1; } return 0; } -int follow_down(struct vfsmount **mnt, struct dentry **dentry) -{ - return __follow_down(mnt,dentry); -} - static inline void follow_dotdot(struct vfsmount **mnt, struct dentry **dentry) { while(1) { @@ -1498,11 +1493,14 @@ do_last: if (flag & O_EXCL) goto exit_dput; - if (d_mountpoint(path.dentry)) { + if (__follow_mount(&path)) { error = -ELOOP; - if (flag & O_NOFOLLOW) - goto exit_dput; - while (__follow_down(&path.mnt,&path.dentry) && d_mountpoint(path.dentry)); + if (flag & O_NOFOLLOW) { + dput(path.dentry); + mntput(path.mnt); + goto exit; + } + mntput(nd->mnt); nd->mnt = path.mnt; } error = -ENOENT; -- cgit v1.2.3 From 2f12dbfbb6286c725c283a169f8f05e89a86848b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:07 -0700 Subject: [PATCH] namei fixes (11/19) shifted conditional mntput() calls in __link_path_walk() downstream. Obviously equivalent transformation. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 935b08d8dcd..907a3f2b4c9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -791,8 +791,6 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) break; /* Check mountpoints.. */ __follow_mount(&next); - if (nd->mnt != next.mnt) - mntput(nd->mnt); err = -ENOENT; inode = next.dentry->d_inode; @@ -803,6 +801,8 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) goto out_dput; if (inode->i_op->follow_link) { + if (nd->mnt != next.mnt) + mntput(nd->mnt); err = do_follow_link(&next, nd); if (err) goto return_err; @@ -815,6 +815,8 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) break; } else { dput(nd->dentry); + if (nd->mnt != next.mnt) + mntput(nd->mnt); nd->mnt = next.mnt; nd->dentry = next.dentry; } @@ -851,17 +853,19 @@ last_component: if (err) break; __follow_mount(&next); - if (nd->mnt != next.mnt) - mntput(nd->mnt); inode = next.dentry->d_inode; if ((lookup_flags & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link) { + if (next.mnt != nd->mnt) + mntput(nd->mnt); err = do_follow_link(&next, nd); if (err) goto return_err; inode = nd->dentry->d_inode; } else { dput(nd->dentry); + if (nd->mnt != next.mnt) + mntput(nd->mnt); nd->mnt = next.mnt; nd->dentry = next.dentry; } @@ -901,6 +905,8 @@ return_base: return 0; out_dput: dput(next.dentry); + if (nd->mnt != next.mnt) + mntput(nd->mnt); break; } path_release(nd); -- cgit v1.2.3 From a15a3f6fc67d910f43098acec6e19d25a37d7cb9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:08 -0700 Subject: [PATCH] namei fixes (12/19) In open_namei() we take mntput(nd->mnt);nd->mnt=path.mnt; out of the if (__follow_mount(...)), making it conditional on nd->mnt != path.mnt instead. Then we shift the result downstream. Equivalent transformations. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 907a3f2b4c9..37fcf941fa3 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1506,8 +1506,6 @@ do_last: mntput(path.mnt); goto exit; } - mntput(nd->mnt); - nd->mnt = path.mnt; } error = -ENOENT; if (!path.dentry->d_inode) @@ -1517,6 +1515,9 @@ do_last: dput(nd->dentry); nd->dentry = path.dentry; + if (nd->mnt != path.mnt) + mntput(nd->mnt); + nd->mnt = path.mnt; error = -EISDIR; if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode)) goto exit; @@ -1528,6 +1529,9 @@ ok: exit_dput: dput(path.dentry); + if (nd->mnt != path.mnt) + mntput(nd->mnt); + nd->mnt = path.mnt; exit: path_release(nd); return error; @@ -1550,6 +1554,9 @@ do_link: error = security_inode_follow_link(path.dentry, nd); if (error) goto exit_dput; + if (nd->mnt != path.mnt) + mntput(nd->mnt); + nd->mnt = path.mnt; error = __do_follow_link(&path, nd); if (error) return error; -- cgit v1.2.3 From ba7a4c1a76f56c607560f1676680ff491747bdae Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:08 -0700 Subject: [PATCH] namei fixes (13/19) In open_namei() exit_dput: we have mntput() done in the wrong order - if nd->mnt != path.mnt we end up doing mntput(nd->mnt); nd->mnt = path.mnt; dput(nd->dentry); mntput(nd->mnt); which drops nd->dentry too late. Fixed by having path.mnt go first. That allows to switch O_NOFOLLOW under if (__follow_mount(...)) back to exit_dput, while we are at it. Fix for early-mntput() race + equivalent transformation. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 37fcf941fa3..5153f57ee6b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1501,11 +1501,8 @@ do_last: if (__follow_mount(&path)) { error = -ELOOP; - if (flag & O_NOFOLLOW) { - dput(path.dentry); - mntput(path.mnt); - goto exit; - } + if (flag & O_NOFOLLOW) + goto exit_dput; } error = -ENOENT; if (!path.dentry->d_inode) @@ -1530,8 +1527,7 @@ ok: exit_dput: dput(path.dentry); if (nd->mnt != path.mnt) - mntput(nd->mnt); - nd->mnt = path.mnt; + mntput(path.mnt); exit: path_release(nd); return error; -- cgit v1.2.3 From 4b7b9772e4c3d87e649d4c419d2487aacf1235aa Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:10 -0700 Subject: [PATCH] namei fixes (14/19) shifted conditional mntput() into do_follow_link() - all callers were doing the same thing. Obviously equivalent transformation. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 5153f57ee6b..411bb3243c3 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -543,11 +543,15 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd) current->link_count++; current->total_link_count++; nd->depth++; + if (path->mnt != nd->mnt) + mntput(nd->mnt); err = __do_follow_link(path, nd); current->link_count--; nd->depth--; return err; loop: + if (path->mnt != nd->mnt) + mntput(nd->mnt); dput(path->dentry); path_release(nd); return err; @@ -801,8 +805,6 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) goto out_dput; if (inode->i_op->follow_link) { - if (nd->mnt != next.mnt) - mntput(nd->mnt); err = do_follow_link(&next, nd); if (err) goto return_err; @@ -856,8 +858,6 @@ last_component: inode = next.dentry->d_inode; if ((lookup_flags & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link) { - if (next.mnt != nd->mnt) - mntput(nd->mnt); err = do_follow_link(&next, nd); if (err) goto return_err; -- cgit v1.2.3 From d9d29a29669f96903d9950bb881c2a393fd33849 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:11 -0700 Subject: [PATCH] namei fixes (15/19) Getting rid of sloppy logics: a) in do_follow_link() we have the wrong vfsmount dropped if our symlink had been mounted on something. Currently it worls only because we never get such situation (modulo filesystem playing dirty tricks on us). And it obfuscates already convoluted logics... b) same goes for open_namei(). c) in __link_path_walk() we have another "it should never happen" sloppiness - out_dput: there does double-free on underlying vfsmount and leaks the covering one if we hit it just after crossing a mountpoint. Again, wrong vfsmount getting dropped. d) another too-early-mntput() race - in do_follow_mount() we need to postpone conditional mntput(path->mnt) until after dput(path->dentry). Again, this one happens only in it-currently-never-happens-unless-some-fs-plays-dirty scenario... Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 411bb3243c3..6a884682b0a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -544,15 +544,15 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd) current->total_link_count++; nd->depth++; if (path->mnt != nd->mnt) - mntput(nd->mnt); + mntput(path->mnt); err = __do_follow_link(path, nd); current->link_count--; nd->depth--; return err; loop: - if (path->mnt != nd->mnt) - mntput(nd->mnt); dput(path->dentry); + if (path->mnt != nd->mnt) + mntput(path->mnt); path_release(nd); return err; } @@ -906,7 +906,7 @@ return_base: out_dput: dput(next.dentry); if (nd->mnt != next.mnt) - mntput(nd->mnt); + mntput(next.mnt); break; } path_release(nd); @@ -1551,8 +1551,7 @@ do_link: if (error) goto exit_dput; if (nd->mnt != path.mnt) - mntput(nd->mnt); - nd->mnt = path.mnt; + mntput(path.mnt); error = __do_follow_link(&path, nd); if (error) return error; -- cgit v1.2.3 From 39ca6d49759346d4710c759d443eec8048b27213 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:12 -0700 Subject: [PATCH] namei fixes (16/19) Conditional mntput() moved into __do_follow_link(). There it collapses with unconditional mntget() on the same sucker, closing another too-early-mntput() race. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 6a884682b0a..444086d441e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -506,7 +506,8 @@ static inline int __do_follow_link(struct path *path, struct nameidata *nd) touch_atime(nd->mnt, dentry); nd_set_link(nd, NULL); - mntget(path->mnt); + if (path->mnt == nd->mnt) + mntget(path->mnt); error = dentry->d_inode->i_op->follow_link(dentry, nd); if (!error) { char *s = nd_get_link(nd); @@ -543,8 +544,6 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd) current->link_count++; current->total_link_count++; nd->depth++; - if (path->mnt != nd->mnt) - mntput(path->mnt); err = __do_follow_link(path, nd); current->link_count--; nd->depth--; @@ -1550,8 +1549,6 @@ do_link: error = security_inode_follow_link(path.dentry, nd); if (error) goto exit_dput; - if (nd->mnt != path.mnt) - mntput(path.mnt); error = __do_follow_link(&path, nd); if (error) return error; -- cgit v1.2.3 From 58c465eba4d7ed307c4c7cb3382ba7ee565e8858 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:13 -0700 Subject: [PATCH] namei fixes (17/19) follow_mount() made void, reordered dput()/mntput() in it. follow_dotdot() switched from struct vfmount ** + struct dentry ** to struct nameidata *; callers updated. Equivalent transformation + fix for too-early-mntput() race. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 444086d441e..36925ff307b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -596,20 +596,17 @@ static int __follow_mount(struct path *path) return res; } -static int follow_mount(struct vfsmount **mnt, struct dentry **dentry) +static void follow_mount(struct vfsmount **mnt, struct dentry **dentry) { - int res = 0; while (d_mountpoint(*dentry)) { struct vfsmount *mounted = lookup_mnt(*mnt, *dentry); if (!mounted) break; + dput(*dentry); mntput(*mnt); *mnt = mounted; - dput(*dentry); *dentry = dget(mounted->mnt_root); - res = 1; } - return res; } /* no need for dcache_lock, as serialization is taken care in @@ -630,41 +627,41 @@ int follow_down(struct vfsmount **mnt, struct dentry **dentry) return 0; } -static inline void follow_dotdot(struct vfsmount **mnt, struct dentry **dentry) +static inline void follow_dotdot(struct nameidata *nd) { while(1) { struct vfsmount *parent; - struct dentry *old = *dentry; + struct dentry *old = nd->dentry; read_lock(¤t->fs->lock); - if (*dentry == current->fs->root && - *mnt == current->fs->rootmnt) { + if (nd->dentry == current->fs->root && + nd->mnt == current->fs->rootmnt) { read_unlock(¤t->fs->lock); break; } read_unlock(¤t->fs->lock); spin_lock(&dcache_lock); - if (*dentry != (*mnt)->mnt_root) { - *dentry = dget((*dentry)->d_parent); + if (nd->dentry != nd->mnt->mnt_root) { + nd->dentry = dget(nd->dentry->d_parent); spin_unlock(&dcache_lock); dput(old); break; } spin_unlock(&dcache_lock); spin_lock(&vfsmount_lock); - parent = (*mnt)->mnt_parent; - if (parent == *mnt) { + parent = nd->mnt->mnt_parent; + if (parent == nd->mnt) { spin_unlock(&vfsmount_lock); break; } mntget(parent); - *dentry = dget((*mnt)->mnt_mountpoint); + nd->dentry = dget(nd->mnt->mnt_mountpoint); spin_unlock(&vfsmount_lock); dput(old); - mntput(*mnt); - *mnt = parent; + mntput(nd->mnt); + nd->mnt = parent; } - follow_mount(mnt, dentry); + follow_mount(&nd->mnt, &nd->dentry); } /* @@ -772,7 +769,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) case 2: if (this.name[1] != '.') break; - follow_dotdot(&nd->mnt, &nd->dentry); + follow_dotdot(nd); inode = nd->dentry->d_inode; /* fallthrough */ case 1: @@ -839,7 +836,7 @@ last_component: case 2: if (this.name[1] != '.') break; - follow_dotdot(&nd->mnt, &nd->dentry); + follow_dotdot(nd); inode = nd->dentry->d_inode; /* fallthrough */ case 1: -- cgit v1.2.3 From 634ee7017b31e46e28c0bd2cb488213331bfd39a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:13 -0700 Subject: [PATCH] namei fixes (18/19) Cosmetical cleanups - __follow_mount() calls in __link_path_walk() absorbed into do_lookup(). Obviously equivalent transformation. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 36925ff307b..5af7681e8ea 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -682,6 +682,7 @@ static int do_lookup(struct nameidata *nd, struct qstr *name, done: path->mnt = mnt; path->dentry = dentry; + __follow_mount(path); return 0; need_lookup: @@ -789,8 +790,6 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) err = do_lookup(nd, &this, &next); if (err) break; - /* Check mountpoints.. */ - __follow_mount(&next); err = -ENOENT; inode = next.dentry->d_inode; @@ -850,7 +849,6 @@ last_component: err = do_lookup(nd, &this, &next); if (err) break; - __follow_mount(&next); inode = next.dentry->d_inode; if ((lookup_flags & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link) { -- cgit v1.2.3 From d671a1cbf7c7a5c5562106d39eb7f830ae5273ae Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 Jun 2005 13:36:14 -0700 Subject: [PATCH] namei fixes (19/19) __do_follow_link() passes potentially worng vfsmount to touch_atime(). It matters only in (currently impossible) case of symlink mounted on something, but it's trivial to fix and that actually makes more sense. Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 5af7681e8ea..a7f7f44119b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -503,7 +503,7 @@ static inline int __do_follow_link(struct path *path, struct nameidata *nd) int error; struct dentry *dentry = path->dentry; - touch_atime(nd->mnt, dentry); + touch_atime(path->mnt, dentry); nd_set_link(nd, NULL); if (path->mnt == nd->mnt) -- cgit v1.2.3 From 8f5bb0438b86d1a5393176ceeec2836fd469edf8 Mon Sep 17 00:00:00 2001 From: Yoshinori Sato Date: Mon, 6 Jun 2005 14:46:32 -0700 Subject: [PATCH] binfmt_flat mmap flag fix Make sure that binfmt_flat passes the correct flags into do_mmap(). nommu's validate_mmap_request() will simple return -EINVAL if we try and pass it a flags value of zero. Signed-off-by: Yoshinori Sato Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_flat.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c index f0cd67d9d31..c8998dc6688 100644 --- a/fs/binfmt_flat.c +++ b/fs/binfmt_flat.c @@ -520,7 +520,7 @@ static int load_flat_file(struct linux_binprm * bprm, DBG_FLT("BINFMT_FLAT: ROM mapping of file (we hope)\n"); down_write(¤t->mm->mmap_sem); - textpos = do_mmap(bprm->file, 0, text_len, PROT_READ|PROT_EXEC, 0, 0); + textpos = do_mmap(bprm->file, 0, text_len, PROT_READ|PROT_EXEC, MAP_SHARED, 0); up_write(¤t->mm->mmap_sem); if (!textpos || textpos >= (unsigned long) -4096) { if (!textpos) @@ -532,7 +532,7 @@ static int load_flat_file(struct linux_binprm * bprm, down_write(¤t->mm->mmap_sem); realdatastart = do_mmap(0, 0, data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long), - PROT_READ|PROT_WRITE|PROT_EXEC, 0, 0); + PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 0); up_write(¤t->mm->mmap_sem); if (realdatastart == 0 || realdatastart >= (unsigned long)-4096) { @@ -574,7 +574,7 @@ static int load_flat_file(struct linux_binprm * bprm, down_write(¤t->mm->mmap_sem); textpos = do_mmap(0, 0, text_len + data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long), - PROT_READ | PROT_EXEC | PROT_WRITE, 0, 0); + PROT_READ | PROT_EXEC | PROT_WRITE, MAP_PRIVATE, 0); up_write(¤t->mm->mmap_sem); if (!textpos || textpos >= (unsigned long) -4096) { if (!textpos) -- cgit v1.2.3 From 1d6757fbff5bc86e94e59ab0d7bdd7e71351d839 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 7 Jun 2005 18:37:01 -0400 Subject: [PATCH] NFS: Fix lookup intent handling We should never apply a lookup intent to anything other than the last path component in an open(), create() or access() call. Introduce the helper nfs_lookup_check_intent() which always returns zero if LOOKUP_CONTINUE or LOOKUP_PARENT are set, and returns the intent flags if we're on the last component of the lookup. By doing so, we fix a bug in open(O_EXCL), where we may end up optimizing away a real lookup of the parent directory. Problem noticed by Linda Dunaphant Signed-off-by: Trond Myklebust Signed-off-by: Linus Torvalds --- fs/nfs/dir.c | 49 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 73f96acd5d3..ff6155f5e8d 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -528,19 +528,39 @@ static inline void nfs_renew_times(struct dentry * dentry) dentry->d_time = jiffies; } +/* + * Return the intent data that applies to this particular path component + * + * Note that the current set of intents only apply to the very last + * component of the path. + * We check for this using LOOKUP_CONTINUE and LOOKUP_PARENT. + */ +static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd, unsigned int mask) +{ + if (nd->flags & (LOOKUP_CONTINUE|LOOKUP_PARENT)) + return 0; + return nd->flags & mask; +} + +/* + * Inode and filehandle revalidation for lookups. + * + * We force revalidation in the cases where the VFS sets LOOKUP_REVAL, + * or if the intent information indicates that we're about to open this + * particular file and the "nocto" mount flag is not set. + * + */ static inline int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd) { struct nfs_server *server = NFS_SERVER(inode); if (nd != NULL) { - int ndflags = nd->flags; /* VFS wants an on-the-wire revalidation */ - if (ndflags & LOOKUP_REVAL) + if (nd->flags & LOOKUP_REVAL) goto out_force; /* This is an open(2) */ - if ((ndflags & LOOKUP_OPEN) && - !(ndflags & LOOKUP_CONTINUE) && + if (nfs_lookup_check_intent(nd, LOOKUP_OPEN) != 0 && !(server->flags & NFS_MOUNT_NOCTO)) goto out_force; } @@ -560,12 +580,8 @@ static inline int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { - int ndflags = 0; - - if (nd) - ndflags = nd->flags; /* Don't revalidate a negative dentry if we're creating a new file */ - if ((ndflags & LOOKUP_CREATE) && !(ndflags & LOOKUP_CONTINUE)) + if (nd != NULL && nfs_lookup_check_intent(nd, LOOKUP_CREATE) != 0) return 0; return !nfs_check_verifier(dir, dentry); } @@ -700,12 +716,16 @@ struct dentry_operations nfs_dentry_operations = { .d_iput = nfs_dentry_iput, }; +/* + * Use intent information to check whether or not we're going to do + * an O_EXCL create using this path component. + */ static inline int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd) { if (NFS_PROTO(dir)->version == 2) return 0; - if (!nd || (nd->flags & LOOKUP_CONTINUE) || !(nd->flags & LOOKUP_CREATE)) + if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_CREATE) == 0) return 0; return (nd->intent.open.flags & O_EXCL) != 0; } @@ -772,12 +792,13 @@ struct dentry_operations nfs4_dentry_operations = { .d_iput = nfs_dentry_iput, }; +/* + * Use intent information to determine whether we need to substitute + * the NFSv4-style stateful OPEN for the LOOKUP call + */ static int is_atomic_open(struct inode *dir, struct nameidata *nd) { - if (!nd) - return 0; - /* Check that we are indeed trying to open this file */ - if ((nd->flags & LOOKUP_CONTINUE) || !(nd->flags & LOOKUP_OPEN)) + if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_OPEN) == 0) return 0; /* NFS does not (yet) have a stateful open for directories */ if (nd->flags & LOOKUP_DIRECTORY) -- cgit v1.2.3