diff options
-rw-r--r-- | fs/namei.c | 83 |
1 files 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; } |