diff options
-rw-r--r-- | fs/namei.c | 27 | ||||
-rw-r--r-- | fs/proc/base.c | 3 | ||||
-rw-r--r-- | include/linux/namei.h | 2 |
3 files changed, 20 insertions, 12 deletions
diff --git a/fs/namei.c b/fs/namei.c index a9b94c62c303..0e1b9c3eb36d 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -586,6 +586,21 @@ static inline void path_to_nameidata(const struct path *path, | |||
586 | nd->path.dentry = path->dentry; | 586 | nd->path.dentry = path->dentry; |
587 | } | 587 | } |
588 | 588 | ||
589 | /* | ||
590 | * Helper to directly jump to a known parsed path from ->follow_link, | ||
591 | * caller must have taken a reference to path beforehand. | ||
592 | */ | ||
593 | void nd_jump_link(struct nameidata *nd, struct path *path) | ||
594 | { | ||
595 | path_put(&nd->path); | ||
596 | |||
597 | nd->path = *path; | ||
598 | nd->inode = nd->path.dentry->d_inode; | ||
599 | nd->flags |= LOOKUP_JUMPED; | ||
600 | |||
601 | BUG_ON(nd->inode->i_op->follow_link); | ||
602 | } | ||
603 | |||
589 | static inline void put_link(struct nameidata *nd, struct path *link, void *cookie) | 604 | static inline void put_link(struct nameidata *nd, struct path *link, void *cookie) |
590 | { | 605 | { |
591 | struct inode *inode = link->dentry->d_inode; | 606 | struct inode *inode = link->dentry->d_inode; |
@@ -630,17 +645,9 @@ follow_link(struct path *link, struct nameidata *nd, void **p) | |||
630 | s = nd_get_link(nd); | 645 | s = nd_get_link(nd); |
631 | if (s) { | 646 | if (s) { |
632 | error = __vfs_follow_link(nd, s); | 647 | error = __vfs_follow_link(nd, s); |
633 | } else if (nd->last_type == LAST_BIND) { | 648 | if (unlikely(error)) |
634 | nd->flags |= LOOKUP_JUMPED; | 649 | put_link(nd, link, *p); |
635 | nd->inode = nd->path.dentry->d_inode; | ||
636 | if (nd->inode->i_op->follow_link) { | ||
637 | /* stepped on a _really_ weird one */ | ||
638 | path_put(&nd->path); | ||
639 | error = -ELOOP; | ||
640 | } | ||
641 | } | 650 | } |
642 | if (unlikely(error)) | ||
643 | put_link(nd, link, *p); | ||
644 | 651 | ||
645 | return error; | 652 | return error; |
646 | 653 | ||
diff --git a/fs/proc/base.c b/fs/proc/base.c index 3bd5ac1ff018..2772208338f8 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -1438,8 +1438,7 @@ static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
1438 | if (error) | 1438 | if (error) |
1439 | goto out; | 1439 | goto out; |
1440 | 1440 | ||
1441 | path_put(&nd->path); | 1441 | nd_jump_link(nd, &path); |
1442 | nd->path = path; | ||
1443 | return NULL; | 1442 | return NULL; |
1444 | out: | 1443 | out: |
1445 | return ERR_PTR(error); | 1444 | return ERR_PTR(error); |
diff --git a/include/linux/namei.h b/include/linux/namei.h index f5931489e150..d2ef8b34b967 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h | |||
@@ -80,6 +80,8 @@ extern int follow_up(struct path *); | |||
80 | extern struct dentry *lock_rename(struct dentry *, struct dentry *); | 80 | extern struct dentry *lock_rename(struct dentry *, struct dentry *); |
81 | extern void unlock_rename(struct dentry *, struct dentry *); | 81 | extern void unlock_rename(struct dentry *, struct dentry *); |
82 | 82 | ||
83 | extern void nd_jump_link(struct nameidata *nd, struct path *path); | ||
84 | |||
83 | static inline void nd_set_link(struct nameidata *nd, char *path) | 85 | static inline void nd_set_link(struct nameidata *nd, char *path) |
84 | { | 86 | { |
85 | nd->saved_names[nd->depth] = path; | 87 | nd->saved_names[nd->depth] = path; |