From 648fa8611de3d4d43bbd64af3226679d2d0eb609 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Dec 2009 01:26:48 -0500 Subject: beginning to untangle do_filp_open() That's going to be a long and painful series. The first step: take the stuff reachable from 'ok' label in do_filp_open() into a new helper (finish_open()). Signed-off-by: Al Viro --- fs/namei.c | 106 ++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 0741c69b331..60b74b3946a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1590,6 +1590,61 @@ static int open_will_truncate(int flag, struct inode *inode) return (flag & O_TRUNC); } +static struct file *finish_open(struct nameidata *nd, + int open_flag, int flag, int acc_mode) +{ + struct file *filp; + int will_truncate; + int error; + + will_truncate = open_will_truncate(flag, nd->path.dentry->d_inode); + if (will_truncate) { + error = mnt_want_write(nd->path.mnt); + if (error) + goto exit; + } + error = may_open(&nd->path, acc_mode, open_flag); + if (error) { + if (will_truncate) + mnt_drop_write(nd->path.mnt); + goto exit; + } + filp = nameidata_to_filp(nd); + if (!IS_ERR(filp)) { + error = ima_file_check(filp, acc_mode); + if (error) { + fput(filp); + filp = ERR_PTR(error); + } + } + if (!IS_ERR(filp)) { + if (acc_mode & MAY_WRITE) + vfs_dq_init(nd->path.dentry->d_inode); + + if (will_truncate) { + error = handle_truncate(&nd->path); + if (error) { + fput(filp); + filp = ERR_PTR(error); + } + } + } + /* + * It is now safe to drop the mnt write + * because the filp has had a write taken + * on its behalf. + */ + if (will_truncate) + mnt_drop_write(nd->path.mnt); + return filp; + +exit: + if (!IS_ERR(nd->intent.open.file)) + release_open_intent(nd); + path_put(&nd->path); + return ERR_PTR(error); +} + /* * Note that the low bits of the passed in "open_flag" * are not the same as in the local variable "flag". See @@ -1604,7 +1659,6 @@ struct file *do_filp_open(int dfd, const char *pathname, struct path path; struct dentry *dir; int count = 0; - int will_truncate; int flag = open_to_namei_flags(open_flag); int force_reval = 0; @@ -1769,55 +1823,7 @@ do_last: if (S_ISDIR(path.dentry->d_inode->i_mode)) goto exit; ok: - /* - * Consider: - * 1. may_open() truncates a file - * 2. a rw->ro mount transition occurs - * 3. nameidata_to_filp() fails due to - * the ro mount. - * That would be inconsistent, and should - * be avoided. Taking this mnt write here - * ensures that (2) can not occur. - */ - will_truncate = open_will_truncate(flag, nd.path.dentry->d_inode); - if (will_truncate) { - error = mnt_want_write(nd.path.mnt); - if (error) - goto exit; - } - error = may_open(&nd.path, acc_mode, open_flag); - if (error) { - if (will_truncate) - mnt_drop_write(nd.path.mnt); - goto exit; - } - filp = nameidata_to_filp(&nd); - if (!IS_ERR(filp)) { - error = ima_file_check(filp, acc_mode); - if (error) { - fput(filp); - filp = ERR_PTR(error); - } - } - if (!IS_ERR(filp)) { - if (acc_mode & MAY_WRITE) - vfs_dq_init(nd.path.dentry->d_inode); - - if (will_truncate) { - error = handle_truncate(&nd.path); - if (error) { - fput(filp); - filp = ERR_PTR(error); - } - } - } - /* - * It is now safe to drop the mnt write - * because the filp has had a write taken - * on its behalf. - */ - if (will_truncate) - mnt_drop_write(nd.path.mnt); + filp = finish_open(&nd, open_flag, flag, acc_mode); if (nd.root.mnt) path_put(&nd.root); return filp; -- cgit v1.2.3 From fb1cc555d533869910e20de4b8d5147570afdfad Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Dec 2009 01:58:28 -0500 Subject: gut do_filp_open() a bit more (do_last separation) Brute-force separation of stuff reachable from do_last: with the exception of do_link:; just take all that crap to a helper function as-is and have it tell the caller if it has to go to do_link. Signed-off-by: Al Viro --- fs/namei.c | 171 +++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 103 insertions(+), 68 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 60b74b3946a..3c39fa1608c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1645,6 +1645,104 @@ exit: return ERR_PTR(error); } +static struct file *do_last(struct nameidata *nd, struct path *path, + int open_flag, int flag, int acc_mode, + int mode, const char *pathname, + struct dentry *dir, int *is_link) +{ + struct file *filp; + int error; + + *is_link = 0; + + error = PTR_ERR(path->dentry); + if (IS_ERR(path->dentry)) { + mutex_unlock(&dir->d_inode->i_mutex); + goto exit; + } + + if (IS_ERR(nd->intent.open.file)) { + error = PTR_ERR(nd->intent.open.file); + goto exit_mutex_unlock; + } + + /* Negative dentry, just create the file */ + if (!path->dentry->d_inode) { + /* + * This write is needed to ensure that a + * ro->rw transition does not occur between + * the time when the file is created and when + * a permanent write count is taken through + * the 'struct file' in nameidata_to_filp(). + */ + error = mnt_want_write(nd->path.mnt); + if (error) + goto exit_mutex_unlock; + error = __open_namei_create(nd, path, open_flag, mode); + if (error) { + mnt_drop_write(nd->path.mnt); + goto exit; + } + filp = nameidata_to_filp(nd); + mnt_drop_write(nd->path.mnt); + if (nd->root.mnt) + path_put(&nd->root); + if (!IS_ERR(filp)) { + error = ima_file_check(filp, acc_mode); + if (error) { + fput(filp); + filp = ERR_PTR(error); + } + } + return filp; + } + + /* + * It already exists. + */ + mutex_unlock(&dir->d_inode->i_mutex); + audit_inode(pathname, path->dentry); + + error = -EEXIST; + if (flag & O_EXCL) + goto exit_dput; + + if (__follow_mount(path)) { + error = -ELOOP; + if (flag & O_NOFOLLOW) + goto exit_dput; + } + + error = -ENOENT; + if (!path->dentry->d_inode) + goto exit_dput; + if (path->dentry->d_inode->i_op->follow_link) { + *is_link = 1; + return NULL; + } + + path_to_nameidata(path, nd); + error = -EISDIR; + if (S_ISDIR(path->dentry->d_inode->i_mode)) + goto exit; + filp = finish_open(nd, open_flag, flag, acc_mode); + if (nd->root.mnt) + path_put(&nd->root); + return filp; + +exit_mutex_unlock: + mutex_unlock(&dir->d_inode->i_mutex); +exit_dput: + path_put_conditional(path, nd); +exit: + if (!IS_ERR(nd->intent.open.file)) + release_open_intent(nd); + if (nd->root.mnt) + path_put(&nd->root); + path_put(&nd->path); + return ERR_PTR(error); +} + /* * Note that the low bits of the passed in "open_flag" * are not the same as in the local variable "flag". See @@ -1661,6 +1759,7 @@ struct file *do_filp_open(int dfd, const char *pathname, int count = 0; int flag = open_to_namei_flags(open_flag); int force_reval = 0; + int is_link; /* * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only @@ -1754,82 +1853,18 @@ reval: path.mnt = nd.path.mnt; do_last: - error = PTR_ERR(path.dentry); - if (IS_ERR(path.dentry)) { - mutex_unlock(&dir->d_inode->i_mutex); - goto exit; - } - - if (IS_ERR(nd.intent.open.file)) { - error = PTR_ERR(nd.intent.open.file); - goto exit_mutex_unlock; - } - - /* Negative dentry, just create the file */ - if (!path.dentry->d_inode) { - /* - * This write is needed to ensure that a - * ro->rw transition does not occur between - * the time when the file is created and when - * a permanent write count is taken through - * the 'struct file' in nameidata_to_filp(). - */ - error = mnt_want_write(nd.path.mnt); - if (error) - goto exit_mutex_unlock; - error = __open_namei_create(&nd, &path, open_flag, mode); - if (error) { - mnt_drop_write(nd.path.mnt); - goto exit; - } - filp = nameidata_to_filp(&nd); - mnt_drop_write(nd.path.mnt); - if (nd.root.mnt) - path_put(&nd.root); - if (!IS_ERR(filp)) { - error = ima_file_check(filp, acc_mode); - if (error) { - fput(filp); - filp = ERR_PTR(error); - } - } - return filp; - } - - /* - * It already exists. - */ - mutex_unlock(&dir->d_inode->i_mutex); - audit_inode(pathname, path.dentry); - - error = -EEXIST; - if (flag & O_EXCL) - goto exit_dput; - - if (__follow_mount(&path)) { - error = -ELOOP; - if (flag & O_NOFOLLOW) - goto exit_dput; - } - - error = -ENOENT; - if (!path.dentry->d_inode) - goto exit_dput; - if (path.dentry->d_inode->i_op->follow_link) + filp = do_last(&nd, &path, open_flag, flag, acc_mode, mode, + pathname, dir, &is_link); + if (is_link) goto do_link; + return filp; - path_to_nameidata(&path, &nd); - error = -EISDIR; - if (S_ISDIR(path.dentry->d_inode->i_mode)) - goto exit; ok: filp = finish_open(&nd, open_flag, flag, acc_mode); if (nd.root.mnt) path_put(&nd.root); return filp; -exit_mutex_unlock: - mutex_unlock(&dir->d_inode->i_mutex); exit_dput: path_put_conditional(&path, &nd); exit: -- cgit v1.2.3 From 3343eb8209cc69f0d2059f8c484ad7a3e1834c0b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Dec 2009 02:02:38 -0500 Subject: Shift releasing nd->root from do_last() to its caller Signed-off-by: Al Viro --- fs/namei.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 3c39fa1608c..bff27c08134 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1685,8 +1685,6 @@ static struct file *do_last(struct nameidata *nd, struct path *path, } filp = nameidata_to_filp(nd); mnt_drop_write(nd->path.mnt); - if (nd->root.mnt) - path_put(&nd->root); if (!IS_ERR(filp)) { error = ima_file_check(filp, acc_mode); if (error) { @@ -1726,8 +1724,6 @@ static struct file *do_last(struct nameidata *nd, struct path *path, if (S_ISDIR(path->dentry->d_inode->i_mode)) goto exit; filp = finish_open(nd, open_flag, flag, acc_mode); - if (nd->root.mnt) - path_put(&nd->root); return filp; exit_mutex_unlock: @@ -1737,8 +1733,6 @@ exit_dput: exit: if (!IS_ERR(nd->intent.open.file)) release_open_intent(nd); - if (nd->root.mnt) - path_put(&nd->root); path_put(&nd->path); return ERR_PTR(error); } @@ -1857,6 +1851,8 @@ do_last: pathname, dir, &is_link); if (is_link) goto do_link; + if (nd.root.mnt) + path_put(&nd.root); return filp; ok: -- cgit v1.2.3 From 27bff34300482632caf52ff589a4e7d755b32539 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Dec 2009 02:05:43 -0500 Subject: unroll do_last: loop in do_filp_open() Signed-off-by: Al Viro --- fs/namei.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index bff27c08134..fc6bed7215c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1845,8 +1845,6 @@ reval: mutex_lock(&dir->d_inode->i_mutex); path.dentry = lookup_hash(&nd); path.mnt = nd.path.mnt; - -do_last: filp = do_last(&nd, &path, open_flag, flag, acc_mode, mode, pathname, dir, &is_link); if (is_link) @@ -1926,7 +1924,13 @@ do_link: path.dentry = lookup_hash(&nd); path.mnt = nd.path.mnt; __putname(nd.last.name); - goto do_last; + filp = do_last(&nd, &path, open_flag, flag, acc_mode, mode, + pathname, dir, &is_link); + if (is_link) + goto do_link; + if (nd.root.mnt) + path_put(&nd.root); + return filp; } /** -- cgit v1.2.3 From c41c14056210e4a328659c82b1edaccb0910d18c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Dec 2009 02:08:19 -0500 Subject: postpone __putname() until after do_last() Since do_last() doesn't mangle nd->last_name, we can safely postpone __putname() done in handling of trailing symlinks until after the call of do_last() Signed-off-by: Al Viro --- fs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index fc6bed7215c..30ba3f3a25e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1923,9 +1923,9 @@ do_link: mutex_lock(&dir->d_inode->i_mutex); path.dentry = lookup_hash(&nd); path.mnt = nd.path.mnt; - __putname(nd.last.name); filp = do_last(&nd, &path, open_flag, flag, acc_mode, mode, pathname, dir, &is_link); + __putname(nd.last.name); if (is_link) goto do_link; if (nd.root.mnt) -- cgit v1.2.3 From a1e28038df98e186807ff55a49c1c26d33d530a5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Dec 2009 02:12:06 -0500 Subject: pull the common predecessors into do_last() Signed-off-by: Al Viro --- fs/namei.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 30ba3f3a25e..976fc323272 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1648,13 +1648,19 @@ exit: static struct file *do_last(struct nameidata *nd, struct path *path, int open_flag, int flag, int acc_mode, int mode, const char *pathname, - struct dentry *dir, int *is_link) + int *is_link) { + struct dentry *dir = nd->path.dentry; struct file *filp; int error; *is_link = 0; + mutex_lock(&dir->d_inode->i_mutex); + + path->dentry = lookup_hash(nd); + path->mnt = nd->path.mnt; + error = PTR_ERR(path->dentry); if (IS_ERR(path->dentry)) { mutex_unlock(&dir->d_inode->i_mutex); @@ -1749,7 +1755,6 @@ struct file *do_filp_open(int dfd, const char *pathname, struct nameidata nd; int error; struct path path; - struct dentry *dir; int count = 0; int flag = open_to_namei_flags(open_flag); int force_reval = 0; @@ -1837,16 +1842,12 @@ reval: filp->f_flags = open_flag; nd.intent.open.flags = flag; nd.intent.open.create_mode = mode; - dir = nd.path.dentry; nd.flags &= ~LOOKUP_PARENT; nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN; 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; filp = do_last(&nd, &path, open_flag, flag, acc_mode, mode, - pathname, dir, &is_link); + pathname, &is_link); if (is_link) goto do_link; if (nd.root.mnt) @@ -1919,12 +1920,8 @@ do_link: __putname(nd.last.name); goto exit; } - dir = nd.path.dentry; - mutex_lock(&dir->d_inode->i_mutex); - path.dentry = lookup_hash(&nd); - path.mnt = nd.path.mnt; filp = do_last(&nd, &path, open_flag, flag, acc_mode, mode, - pathname, dir, &is_link); + pathname, &is_link); __putname(nd.last.name); if (is_link) goto do_link; -- cgit v1.2.3 From c99658fe970f442199733bcace1a00b087336a0d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Dec 2009 02:27:30 -0500 Subject: bail out with ELOOP earlier in do_link loop If we'd passed through 32 trailing symlinks already, there's no sense following the 33rd - we'll bail out anyway. Better bugger off earlier. It *does* change behaviour, after a fashion - if the 33rd happens to be a procfs-style symlink, original code *would* allow it. This one will not. Cry me a river if that hurts you. Please, do. And post a video of that, while you are at it. Signed-off-by: Al Viro --- fs/namei.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 976fc323272..84f1ec3b4a5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1873,7 +1873,7 @@ exit_parent: do_link: error = -ELOOP; - if (flag & O_NOFOLLOW) + if ((flag & O_NOFOLLOW) || count++ == 32) goto exit_dput; /* * This is subtle. Instead of calling do_follow_link() we do the @@ -1915,11 +1915,6 @@ do_link: __putname(nd.last.name); goto exit; } - error = -ELOOP; - if (count++==32) { - __putname(nd.last.name); - goto exit; - } filp = do_last(&nd, &path, open_flag, flag, acc_mode, mode, pathname, &is_link); __putname(nd.last.name); -- cgit v1.2.3 From a2c36b450ee68470836cb858c58a6ba3a52c5ec5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Dec 2009 03:39:50 -0500 Subject: pull more into do_last() Handling of LAST_DOT/LAST_ROOT/LAST_DOTDOT/terminating slash can be pulled in as well Signed-off-by: Al Viro --- fs/namei.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 84f1ec3b4a5..52517e0bbdd 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1656,6 +1656,10 @@ static struct file *do_last(struct nameidata *nd, struct path *path, *is_link = 0; + error = -EISDIR; + if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len]) + goto exit; + mutex_lock(&dir->d_inode->i_mutex); path->dentry = lookup_hash(nd); @@ -1826,13 +1830,8 @@ reval: audit_inode(pathname, nd.path.dentry); /* - * We have the parent and last component. First of all, check - * that we are not asked to creat(2) an obvious directory - that - * will not do. + * We have the parent and last component. */ - error = -EISDIR; - if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len]) - goto exit_parent; error = -ENFILE; filp = get_empty_filp(); @@ -1908,16 +1907,10 @@ do_link: nd.flags &= ~LOOKUP_PARENT; if (nd.last_type == LAST_BIND) goto ok; - error = -EISDIR; - if (nd.last_type != LAST_NORM) - goto exit; - if (nd.last.name[nd.last.len]) { - __putname(nd.last.name); - goto exit; - } filp = do_last(&nd, &path, open_flag, flag, acc_mode, mode, pathname, &is_link); - __putname(nd.last.name); + if (nd.last_type == LAST_NORM) + __putname(nd.last.name); if (is_link) goto do_link; if (nd.root.mnt) -- cgit v1.2.3 From 9a66179e13504c676f891908a1e94912ec5cdefb Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Dec 2009 06:49:47 -0500 Subject: Don't pass mangled open_flag to finish_open() Signed-off-by: Al Viro --- fs/namei.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 52517e0bbdd..5b901600691 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1591,13 +1591,13 @@ static int open_will_truncate(int flag, struct inode *inode) } static struct file *finish_open(struct nameidata *nd, - int open_flag, int flag, int acc_mode) + int open_flag, int acc_mode) { struct file *filp; int will_truncate; int error; - will_truncate = open_will_truncate(flag, nd->path.dentry->d_inode); + will_truncate = open_will_truncate(open_flag, nd->path.dentry->d_inode); if (will_truncate) { error = mnt_want_write(nd->path.mnt); if (error) @@ -1733,7 +1733,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, error = -EISDIR; if (S_ISDIR(path->dentry->d_inode->i_mode)) goto exit; - filp = finish_open(nd, open_flag, flag, acc_mode); + filp = finish_open(nd, open_flag, acc_mode); return filp; exit_mutex_unlock: @@ -1854,7 +1854,7 @@ reval: return filp; ok: - filp = finish_open(&nd, open_flag, flag, acc_mode); + filp = finish_open(&nd, open_flag, acc_mode); if (nd.root.mnt) path_put(&nd.root); return filp; -- cgit v1.2.3 From 5b369df8263fe7ab4dac2bb08b8f423dc5e33752 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Dec 2009 06:51:13 -0500 Subject: Get rid of passing mangled flag to do_last() Signed-off-by: Al Viro --- fs/namei.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 5b901600691..5ea7330c184 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1646,7 +1646,7 @@ exit: } static struct file *do_last(struct nameidata *nd, struct path *path, - int open_flag, int flag, int acc_mode, + int open_flag, int acc_mode, int mode, const char *pathname, int *is_link) { @@ -1712,12 +1712,12 @@ static struct file *do_last(struct nameidata *nd, struct path *path, audit_inode(pathname, path->dentry); error = -EEXIST; - if (flag & O_EXCL) + if (open_flag & O_EXCL) goto exit_dput; if (__follow_mount(path)) { error = -ELOOP; - if (flag & O_NOFOLLOW) + if (open_flag & O_NOFOLLOW) goto exit_dput; } @@ -1845,7 +1845,7 @@ reval: nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN; if (flag & O_EXCL) nd.flags |= LOOKUP_EXCL; - filp = do_last(&nd, &path, open_flag, flag, acc_mode, mode, + filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname, &is_link); if (is_link) goto do_link; @@ -1907,7 +1907,7 @@ do_link: nd.flags &= ~LOOKUP_PARENT; if (nd.last_type == LAST_BIND) goto ok; - filp = do_last(&nd, &path, open_flag, flag, acc_mode, mode, + filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname, &is_link); if (nd.last_type == LAST_NORM) __putname(nd.last.name); -- cgit v1.2.3 From 4296e2cbf2138b5831b83f03e81de916ce1a967d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Dec 2009 07:15:41 -0500 Subject: Leave mangled flag only for setting nd.intent.open.flag Nothing else uses it anymore Signed-off-by: Al Viro --- fs/namei.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 5ea7330c184..f5e4397dcd7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1777,18 +1777,18 @@ struct file *do_filp_open(int dfd, const char *pathname, acc_mode = MAY_OPEN | ACC_MODE(open_flag); /* O_TRUNC implies we need access checks for write permissions */ - if (flag & O_TRUNC) + if (open_flag & O_TRUNC) acc_mode |= MAY_WRITE; /* Allow the LSM permission hook to distinguish append access from general write access. */ - if (flag & O_APPEND) + if (open_flag & O_APPEND) acc_mode |= MAY_APPEND; /* * The simplest case - just a plain lookup. */ - if (!(flag & O_CREAT)) { + if (!(open_flag & O_CREAT)) { filp = get_empty_filp(); if (filp == NULL) @@ -1798,7 +1798,7 @@ struct file *do_filp_open(int dfd, const char *pathname, nd.intent.open.flags = flag; nd.intent.open.create_mode = 0; error = do_path_lookup(dfd, pathname, - lookup_flags(flag)|LOOKUP_OPEN, &nd); + lookup_flags(open_flag)|LOOKUP_OPEN, &nd); if (IS_ERR(nd.intent.open.file)) { if (error == 0) { error = PTR_ERR(nd.intent.open.file); @@ -1843,7 +1843,7 @@ reval: nd.intent.open.create_mode = mode; nd.flags &= ~LOOKUP_PARENT; nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN; - if (flag & O_EXCL) + if (open_flag & O_EXCL) nd.flags |= LOOKUP_EXCL; filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname, &is_link); @@ -1872,7 +1872,7 @@ exit_parent: do_link: error = -ELOOP; - if ((flag & O_NOFOLLOW) || count++ == 32) + if ((open_flag & O_NOFOLLOW) || count++ == 32) goto exit_dput; /* * This is subtle. Instead of calling do_follow_link() we do the -- cgit v1.2.3 From 67ee3ad21d0d0b2cc0b70708de8aed860fadda44 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Dec 2009 07:01:01 -0500 Subject: Pull handling of LAST_BIND into do_last(), clean up ok: part in do_filp_open() Note that in case of !O_CREAT we know that nd.root has already been given up Signed-off-by: Al Viro --- fs/namei.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index f5e4397dcd7..0b4d19d47e6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1656,6 +1656,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path, *is_link = 0; + if (nd->last_type == LAST_BIND) + goto ok; + error = -EISDIR; if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len]) goto exit; @@ -1733,6 +1736,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, error = -EISDIR; if (S_ISDIR(path->dentry->d_inode->i_mode)) goto exit; +ok: filp = finish_open(nd, open_flag, acc_mode); return filp; @@ -1808,7 +1812,7 @@ struct file *do_filp_open(int dfd, const char *pathname, release_open_intent(&nd); if (error) return ERR_PTR(error); - goto ok; + return finish_open(&nd, open_flag, acc_mode); } /* @@ -1853,21 +1857,14 @@ reval: path_put(&nd.root); return filp; -ok: - filp = finish_open(&nd, open_flag, acc_mode); - if (nd.root.mnt) - path_put(&nd.root); - return filp; - exit_dput: path_put_conditional(&path, &nd); -exit: if (!IS_ERR(nd.intent.open.file)) release_open_intent(&nd); exit_parent: + path_put(&nd.path); if (nd.root.mnt) path_put(&nd.root); - path_put(&nd.path); return ERR_PTR(error); do_link: @@ -1905,8 +1902,6 @@ do_link: return ERR_PTR(error); } nd.flags &= ~LOOKUP_PARENT; - if (nd.last_type == LAST_BIND) - goto ok; filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname, &is_link); if (nd.last_type == LAST_NORM) -- cgit v1.2.3 From 9e67f36169117e07daf16dc7ca314f1db9e2050a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Dec 2009 07:04:50 -0500 Subject: Kill is_link argument of do_last() We set it to 1 iff we return NULL Signed-off-by: Al Viro --- fs/namei.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 0b4d19d47e6..b0c74fe91fb 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1647,15 +1647,12 @@ exit: static struct file *do_last(struct nameidata *nd, struct path *path, int open_flag, int acc_mode, - int mode, const char *pathname, - int *is_link) + int mode, const char *pathname) { struct dentry *dir = nd->path.dentry; struct file *filp; int error; - *is_link = 0; - if (nd->last_type == LAST_BIND) goto ok; @@ -1727,10 +1724,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path, error = -ENOENT; if (!path->dentry->d_inode) goto exit_dput; - if (path->dentry->d_inode->i_op->follow_link) { - *is_link = 1; + + if (path->dentry->d_inode->i_op->follow_link) return NULL; - } path_to_nameidata(path, nd); error = -EISDIR; @@ -1766,7 +1762,6 @@ struct file *do_filp_open(int dfd, const char *pathname, int count = 0; int flag = open_to_namei_flags(open_flag); int force_reval = 0; - int is_link; /* * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only @@ -1849,9 +1844,8 @@ reval: nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN; if (open_flag & O_EXCL) nd.flags |= LOOKUP_EXCL; - filp = do_last(&nd, &path, open_flag, acc_mode, mode, - pathname, &is_link); - if (is_link) + filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); + if (!filp) goto do_link; if (nd.root.mnt) path_put(&nd.root); @@ -1902,11 +1896,10 @@ do_link: return ERR_PTR(error); } nd.flags &= ~LOOKUP_PARENT; - filp = do_last(&nd, &path, open_flag, acc_mode, mode, - pathname, &is_link); + filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); if (nd.last_type == LAST_NORM) __putname(nd.last.name); - if (is_link) + if (!filp) goto do_link; if (nd.root.mnt) path_put(&nd.root); -- cgit v1.2.3 From 10fa8e62f2bc33c452516585911f151d88389e4c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Dec 2009 07:09:49 -0500 Subject: Unify exits in O_CREAT handling Signed-off-by: Al Viro --- fs/namei.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index b0c74fe91fb..675a712137f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1821,9 +1821,8 @@ reval: nd.flags |= LOOKUP_REVAL; error = path_walk(pathname, &nd); if (error) { - if (nd.root.mnt) - path_put(&nd.root); - return ERR_PTR(error); + filp = ERR_PTR(error); + goto out; } if (unlikely(!audit_dummy_context())) audit_inode(pathname, nd.path.dentry); @@ -1847,9 +1846,7 @@ reval: filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); if (!filp) goto do_link; - if (nd.root.mnt) - path_put(&nd.root); - return filp; + goto out; exit_dput: path_put_conditional(&path, &nd); @@ -1857,9 +1854,15 @@ exit_dput: release_open_intent(&nd); exit_parent: path_put(&nd.path); + filp = ERR_PTR(error); +out: if (nd.root.mnt) path_put(&nd.root); - return ERR_PTR(error); + if (filp == ERR_PTR(-ESTALE) && !force_reval) { + force_reval = 1; + goto reval; + } + return filp; do_link: error = -ELOOP; @@ -1887,13 +1890,8 @@ do_link: * with "intent.open". */ release_open_intent(&nd); - if (nd.root.mnt) - path_put(&nd.root); - if (error == -ESTALE && !force_reval) { - force_reval = 1; - goto reval; - } - return ERR_PTR(error); + filp = ERR_PTR(error); + goto out; } nd.flags &= ~LOOKUP_PARENT; filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); @@ -1901,9 +1899,7 @@ do_link: __putname(nd.last.name); if (!filp) goto do_link; - if (nd.root.mnt) - path_put(&nd.root); - return filp; + goto out; } /** -- cgit v1.2.3 From 806b681cbe588bebe8fe47dd24da62f2d1c55851 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Dec 2009 07:16:40 -0500 Subject: Turn do_link spaghetty into a normal loop Signed-off-by: Al Viro --- fs/namei.c | 83 ++++++++++++++++++++++++++++---------------------------------- 1 file changed, 38 insertions(+), 45 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 675a712137f..08da937b1ee 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1844,17 +1844,38 @@ reval: if (open_flag & O_EXCL) nd.flags |= LOOKUP_EXCL; filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); - if (!filp) - goto do_link; - goto out; - -exit_dput: - path_put_conditional(&path, &nd); - if (!IS_ERR(nd.intent.open.file)) - release_open_intent(&nd); -exit_parent: - path_put(&nd.path); - filp = ERR_PTR(error); + while (unlikely(!filp)) { /* trailing symlink */ + error = -ELOOP; + if ((open_flag & O_NOFOLLOW) || count++ == 32) + goto exit_dput; + /* + * This is subtle. Instead of calling do_follow_link() we do + * the thing by hands. The reason is that this way we have zero + * link_count and path_walk() (called from ->follow_link) + * honoring LOOKUP_PARENT. After that we have the parent and + * last component, i.e. we are in the same situation as after + * the first path_walk(). Well, almost - if the last component + * is normal we get its copy stored in nd->last.name and we will + * have to putname() it when we are done. Procfs-like symlinks + * just set LAST_BIND. + */ + nd.flags |= LOOKUP_PARENT; + error = security_inode_follow_link(path.dentry, &nd); + if (error) + goto exit_dput; + error = __do_follow_link(&path, &nd); + path_put(&path); + if (error) { + /* nd.path had been dropped */ + release_open_intent(&nd); + filp = ERR_PTR(error); + goto out; + } + nd.flags &= ~LOOKUP_PARENT; + filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); + if (nd.last_type == LAST_NORM) + __putname(nd.last.name); + } out: if (nd.root.mnt) path_put(&nd.root); @@ -1864,41 +1885,13 @@ out: } return filp; -do_link: - error = -ELOOP; - if ((open_flag & O_NOFOLLOW) || count++ == 32) - goto exit_dput; - /* - * This is subtle. Instead of calling do_follow_link() we do the - * thing by hands. The reason is that this way we have zero link_count - * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT. - * After that we have the parent and last component, i.e. - * we are in the same situation as after the first path_walk(). - * Well, almost - if the last component is normal we get its copy - * stored in nd->last.name and we will have to putname() it when we - * are done. Procfs-like symlinks just set LAST_BIND. - */ - nd.flags |= LOOKUP_PARENT; - error = security_inode_follow_link(path.dentry, &nd); - if (error) - goto exit_dput; - error = __do_follow_link(&path, &nd); - path_put(&path); - if (error) { - /* Does someone understand code flow here? Or it is only - * me so stupid? Anathema to whoever designed this non-sense - * with "intent.open". - */ +exit_dput: + path_put_conditional(&path, &nd); + if (!IS_ERR(nd.intent.open.file)) release_open_intent(&nd); - filp = ERR_PTR(error); - goto out; - } - nd.flags &= ~LOOKUP_PARENT; - filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); - if (nd.last_type == LAST_NORM) - __putname(nd.last.name); - if (!filp) - goto do_link; +exit_parent: + path_put(&nd.path); + filp = ERR_PTR(error); goto out; } -- cgit v1.2.3 From 3866248e5f86d74960a3d1592882490ec3021675 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Dec 2009 07:21:48 -0500 Subject: Finish pulling of -ESTALE handling to upper level in do_filp_open() Don't bother with path_walk() (and its retry loop); link_path_walk() will do it. Signed-off-by: Al Viro --- fs/namei.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index 08da937b1ee..adfbaf5c04a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1819,7 +1819,9 @@ reval: return ERR_PTR(error); if (force_reval) nd.flags |= LOOKUP_REVAL; - error = path_walk(pathname, &nd); + + current->total_link_count = 0; + error = link_path_walk(pathname, &nd); if (error) { filp = ERR_PTR(error); goto out; -- cgit v1.2.3 From def4af30cf945a3735ffca865788ea84b30b25d9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Dec 2009 08:37:05 -0500 Subject: Get rid of symlink body copying Now that nd->last stays around until ->put_link() is called, we can just postpone that ->put_link() in do_filp_open() a bit and don't bother with copying. Signed-off-by: Al Viro --- fs/namei.c | 55 ++++++++++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index adfbaf5c04a..1f5d86d1fbf 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -498,8 +498,6 @@ static int link_path_walk(const char *, struct nameidata *); static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link) { - int res = 0; - char *name; if (IS_ERR(link)) goto fail; @@ -510,22 +508,7 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l path_get(&nd->root); } - res = link_path_walk(link, nd); - if (nd->depth || res || nd->last_type!=LAST_NORM) - return res; - /* - * If it is an iterative symlinks resolution in open_namei() we - * have to copy the last component. And all that crap because of - * bloody create() on broken symlinks. Furrfu... - */ - name = __getname(); - if (unlikely(!name)) { - path_put(&nd->path); - return -ENOMEM; - } - strcpy(name, nd->last.name); - nd->last.name = name; - return 0; + return link_path_walk(link, nd); fail: path_put(&nd->path); return PTR_ERR(link); @@ -547,10 +530,10 @@ static inline void path_to_nameidata(struct path *path, struct nameidata *nd) nd->path.dentry = path->dentry; } -static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd) +static __always_inline int +__do_follow_link(struct path *path, struct nameidata *nd, void **p) { int error; - void *cookie; struct dentry *dentry = path->dentry; touch_atime(path->mnt, dentry); @@ -562,9 +545,9 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata } mntget(path->mnt); nd->last_type = LAST_BIND; - cookie = dentry->d_inode->i_op->follow_link(dentry, nd); - error = PTR_ERR(cookie); - if (!IS_ERR(cookie)) { + *p = dentry->d_inode->i_op->follow_link(dentry, nd); + error = PTR_ERR(*p); + if (!IS_ERR(*p)) { char *s = nd_get_link(nd); error = 0; if (s) @@ -574,8 +557,6 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata if (error) path_put(&nd->path); } - if (dentry->d_inode->i_op->put_link) - dentry->d_inode->i_op->put_link(dentry, nd, cookie); } return error; } @@ -589,6 +570,7 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata */ static inline int do_follow_link(struct path *path, struct nameidata *nd) { + void *cookie; int err = -ELOOP; if (current->link_count >= MAX_NESTED_LINKS) goto loop; @@ -602,7 +584,9 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd) current->link_count++; current->total_link_count++; nd->depth++; - err = __do_follow_link(path, nd); + err = __do_follow_link(path, nd, &cookie); + if (!IS_ERR(cookie) && path->dentry->d_inode->i_op->put_link) + path->dentry->d_inode->i_op->put_link(path->dentry, nd, cookie); path_put(path); current->link_count--; nd->depth--; @@ -1847,6 +1831,9 @@ reval: nd.flags |= LOOKUP_EXCL; filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); while (unlikely(!filp)) { /* trailing symlink */ + struct path holder; + struct inode *inode; + void *cookie; error = -ELOOP; if ((open_flag & O_NOFOLLOW) || count++ == 32) goto exit_dput; @@ -1865,18 +1852,24 @@ reval: error = security_inode_follow_link(path.dentry, &nd); if (error) goto exit_dput; - error = __do_follow_link(&path, &nd); - path_put(&path); - if (error) { + error = __do_follow_link(&path, &nd, &cookie); + if (unlikely(error)) { /* nd.path had been dropped */ + inode = path.dentry->d_inode; + if (!IS_ERR(cookie) && inode->i_op->put_link) + inode->i_op->put_link(path.dentry, &nd, cookie); + path_put(&path); release_open_intent(&nd); filp = ERR_PTR(error); goto out; } + holder = path; nd.flags &= ~LOOKUP_PARENT; filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); - if (nd.last_type == LAST_NORM) - __putname(nd.last.name); + inode = holder.dentry->d_inode; + if (inode->i_op->put_link) + inode->i_op->put_link(holder.dentry, &nd, cookie); + path_put(&holder); } out: if (nd.root.mnt) -- cgit v1.2.3 From 1f36f774b22a0ceb7dd33eca626746c81a97b6a5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Dec 2009 10:56:19 -0500 Subject: Switch !O_CREAT case to use of do_last() ... and now we have all intents crap well localized Signed-off-by: Al Viro --- fs/namei.c | 127 ++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 66 insertions(+), 61 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 1f5d86d1fbf..9a6456099f1 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1359,22 +1359,6 @@ static inline int may_create(struct inode *dir, struct dentry *child) return inode_permission(dir, MAY_WRITE | MAY_EXEC); } -/* - * O_DIRECTORY translates into forcing a directory lookup. - */ -static inline int lookup_flags(unsigned int f) -{ - unsigned long retval = LOOKUP_FOLLOW; - - if (f & O_NOFOLLOW) - retval &= ~LOOKUP_FOLLOW; - - if (f & O_DIRECTORY) - retval |= LOOKUP_DIRECTORY; - - return retval; -} - /* * p1 and p2 should be directories on the same fs. */ @@ -1631,19 +1615,60 @@ exit: static struct file *do_last(struct nameidata *nd, struct path *path, int open_flag, int acc_mode, - int mode, const char *pathname) + int mode, const char *pathname, + int *want_dir) { struct dentry *dir = nd->path.dentry; struct file *filp; - int error; + int error = -EISDIR; - if (nd->last_type == LAST_BIND) + switch (nd->last_type) { + case LAST_DOTDOT: + follow_dotdot(nd); + dir = nd->path.dentry; + if (nd->path.mnt->mnt_sb->s_type->fs_flags & FS_REVAL_DOT) { + if (!dir->d_op->d_revalidate(dir, nd)) { + error = -ESTALE; + goto exit; + } + } + /* fallthrough */ + case LAST_DOT: + case LAST_ROOT: + if (open_flag & O_CREAT) + goto exit; + /* fallthrough */ + case LAST_BIND: + audit_inode(pathname, dir); goto ok; + } - error = -EISDIR; - if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len]) - goto exit; + /* trailing slashes? */ + if (nd->last.name[nd->last.len]) { + if (open_flag & O_CREAT) + goto exit; + *want_dir = 1; + } + /* just plain open? */ + if (!(open_flag & O_CREAT)) { + error = do_lookup(nd, &nd->last, path); + if (error) + goto exit; + error = -ENOENT; + if (!path->dentry->d_inode) + goto exit_dput; + if (path->dentry->d_inode->i_op->follow_link) + return NULL; + error = -ENOTDIR; + if (*want_dir & !path->dentry->d_inode->i_op->lookup) + goto exit_dput; + path_to_nameidata(path, nd); + audit_inode(pathname, nd->path.dentry); + goto ok; + } + + /* OK, it's O_CREAT */ mutex_lock(&dir->d_inode->i_mutex); path->dentry = lookup_hash(nd); @@ -1746,6 +1771,10 @@ struct file *do_filp_open(int dfd, const char *pathname, int count = 0; int flag = open_to_namei_flags(open_flag); int force_reval = 0; + int want_dir = open_flag & O_DIRECTORY; + + if (!(open_flag & O_CREAT)) + mode = 0; /* * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only @@ -1768,35 +1797,7 @@ struct file *do_filp_open(int dfd, const char *pathname, if (open_flag & O_APPEND) acc_mode |= MAY_APPEND; - /* - * The simplest case - just a plain lookup. - */ - if (!(open_flag & O_CREAT)) { - filp = get_empty_filp(); - - if (filp == NULL) - return ERR_PTR(-ENFILE); - nd.intent.open.file = filp; - filp->f_flags = open_flag; - nd.intent.open.flags = flag; - nd.intent.open.create_mode = 0; - error = do_path_lookup(dfd, pathname, - lookup_flags(open_flag)|LOOKUP_OPEN, &nd); - if (IS_ERR(nd.intent.open.file)) { - if (error == 0) { - error = PTR_ERR(nd.intent.open.file); - path_put(&nd.path); - } - } else if (error) - release_open_intent(&nd); - if (error) - return ERR_PTR(error); - return finish_open(&nd, open_flag, acc_mode); - } - - /* - * Create - we need to know the parent. - */ + /* find the parent */ reval: error = path_init(dfd, pathname, LOOKUP_PARENT, &nd); if (error) @@ -1810,7 +1811,7 @@ reval: filp = ERR_PTR(error); goto out; } - if (unlikely(!audit_dummy_context())) + if (unlikely(!audit_dummy_context()) && (open_flag & O_CREAT)) audit_inode(pathname, nd.path.dentry); /* @@ -1826,16 +1827,22 @@ reval: nd.intent.open.flags = flag; nd.intent.open.create_mode = mode; nd.flags &= ~LOOKUP_PARENT; - nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN; - if (open_flag & O_EXCL) - nd.flags |= LOOKUP_EXCL; - filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); + nd.flags |= LOOKUP_OPEN; + if (open_flag & O_CREAT) { + nd.flags |= LOOKUP_CREATE; + if (open_flag & O_EXCL) + nd.flags |= LOOKUP_EXCL; + } + filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname, &want_dir); while (unlikely(!filp)) { /* trailing symlink */ struct path holder; - struct inode *inode; + struct inode *inode = path.dentry->d_inode; void *cookie; error = -ELOOP; - if ((open_flag & O_NOFOLLOW) || count++ == 32) + /* S_ISDIR part is a temporary automount kludge */ + if ((open_flag & O_NOFOLLOW) && !S_ISDIR(inode->i_mode)) + goto exit_dput; + if (count++ == 32) goto exit_dput; /* * This is subtle. Instead of calling do_follow_link() we do @@ -1855,7 +1862,6 @@ reval: error = __do_follow_link(&path, &nd, &cookie); if (unlikely(error)) { /* nd.path had been dropped */ - inode = path.dentry->d_inode; if (!IS_ERR(cookie) && inode->i_op->put_link) inode->i_op->put_link(path.dentry, &nd, cookie); path_put(&path); @@ -1865,8 +1871,7 @@ reval: } holder = path; nd.flags &= ~LOOKUP_PARENT; - filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); - inode = holder.dentry->d_inode; + filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname, &want_dir); if (inode->i_op->put_link) inode->i_op->put_link(holder.dentry, &nd, cookie); path_put(&holder); -- cgit v1.2.3