diff --git a/fs/namei.c b/fs/namei.c index 2ffb4afb250a..06c3e4a6c440 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -494,7 +494,10 @@ EXPORT_SYMBOL(path_put); struct nameidata { struct path path; - struct qstr last; + union { + struct qstr last; + struct path link; + }; struct path root; struct inode *inode; /* path.dentry.d_inode */ unsigned int flags; @@ -1559,8 +1562,9 @@ static inline int should_follow_link(struct dentry *dentry, int follow) return unlikely(d_is_symlink(dentry)) ? follow : 0; } -static int walk_component(struct nameidata *nd, struct path *path, int follow) +static int walk_component(struct nameidata *nd, int follow) { + struct path path; struct inode *inode; int err; /* @@ -1570,38 +1574,39 @@ static int walk_component(struct nameidata *nd, struct path *path, int follow) */ if (unlikely(nd->last_type != LAST_NORM)) return handle_dots(nd, nd->last_type); - err = lookup_fast(nd, path, &inode); + err = lookup_fast(nd, &path, &inode); if (unlikely(err)) { if (err < 0) goto out_err; - err = lookup_slow(nd, path); + err = lookup_slow(nd, &path); if (err < 0) goto out_err; - inode = path->dentry->d_inode; + inode = path.dentry->d_inode; err = -ENOENT; - if (d_is_negative(path->dentry)) + if (d_is_negative(path.dentry)) goto out_path_put; } - if (should_follow_link(path->dentry, follow)) { + if (should_follow_link(path.dentry, follow)) { if (nd->flags & LOOKUP_RCU) { - if (unlikely(nd->path.mnt != path->mnt || - unlazy_walk(nd, path->dentry))) { + if (unlikely(nd->path.mnt != path.mnt || + unlazy_walk(nd, path.dentry))) { err = -ECHILD; goto out_err; } } - BUG_ON(inode != path->dentry->d_inode); + BUG_ON(inode != path.dentry->d_inode); + nd->link = path; return 1; } - path_to_nameidata(path, nd); + path_to_nameidata(&path, nd); nd->inode = inode; return 0; out_path_put: - path_to_nameidata(path, nd); + path_to_nameidata(&path, nd); out_err: terminate_walk(nd); return err; @@ -1614,12 +1619,12 @@ static int walk_component(struct nameidata *nd, struct path *path, int follow) * Without that kind of total limit, nasty chains of consecutive * symlinks can cause almost arbitrarily long lookups. */ -static inline int nested_symlink(struct path *path, struct nameidata *nd) +static inline int nested_symlink(struct nameidata *nd) { int res; if (unlikely(current->link_count >= MAX_NESTED_LINKS)) { - path_put_conditional(path, nd); + path_put_conditional(&nd->link, nd); path_put(&nd->path); return -ELOOP; } @@ -1629,13 +1634,13 @@ static inline int nested_symlink(struct path *path, struct nameidata *nd) current->link_count++; do { - struct path link = *path; + struct path link = nd->link; void *cookie; res = follow_link(&link, nd, &cookie); if (res) break; - res = walk_component(nd, path, LOOKUP_FOLLOW); + res = walk_component(nd, LOOKUP_FOLLOW); put_link(nd, &link, cookie); } while (res > 0); @@ -1770,7 +1775,6 @@ static inline u64 hash_name(const char *name) */ static int link_path_walk(const char *name, struct nameidata *nd) { - struct path next; int err; while (*name=='/') @@ -1830,17 +1834,17 @@ static int link_path_walk(const char *name, struct nameidata *nd) if (!*name) return 0; - err = walk_component(nd, &next, LOOKUP_FOLLOW); + err = walk_component(nd, LOOKUP_FOLLOW); if (err < 0) return err; if (err) { - err = nested_symlink(&next, nd); + err = nested_symlink(nd); if (err) return err; } if (!d_can_lookup(nd->path.dentry)) { - err = -ENOTDIR; + err = -ENOTDIR; break; } } @@ -1960,20 +1964,19 @@ static void path_cleanup(struct nameidata *nd) fput(nd->base); } -static inline int lookup_last(struct nameidata *nd, struct path *path) +static inline int lookup_last(struct nameidata *nd) { if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len]) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; nd->flags &= ~LOOKUP_PARENT; - return walk_component(nd, path, nd->flags & LOOKUP_FOLLOW); + return walk_component(nd, nd->flags & LOOKUP_FOLLOW); } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ static int path_lookupat(int dfd, const struct filename *name, unsigned int flags, struct nameidata *nd) { - struct path path; int err; /* @@ -1992,10 +1995,10 @@ static int path_lookupat(int dfd, const struct filename *name, */ err = path_init(dfd, name, flags, nd); if (!err && !(flags & LOOKUP_PARENT)) { - err = lookup_last(nd, &path); + err = lookup_last(nd); while (err > 0) { void *cookie; - struct path link = path; + struct path link = nd->link; err = may_follow_link(&link, nd); if (unlikely(err)) break; @@ -2003,7 +2006,7 @@ static int path_lookupat(int dfd, const struct filename *name, err = follow_link(&link, nd, &cookie); if (err) break; - err = lookup_last(nd, &path); + err = lookup_last(nd); put_link(nd, &link, cookie); } } @@ -2312,8 +2315,10 @@ mountpoint_last(struct nameidata *nd, struct path *path) } path->dentry = dentry; path->mnt = nd->path.mnt; - if (should_follow_link(dentry, nd->flags & LOOKUP_FOLLOW)) + if (should_follow_link(dentry, nd->flags & LOOKUP_FOLLOW)) { + nd->link = *path; return 1; + } mntget(path->mnt); follow_mount(path); error = 0; @@ -3040,6 +3045,7 @@ static int do_last(struct nameidata *nd, struct path *path, } } BUG_ON(inode != path->dentry->d_inode); + nd->link = *path; return 1; } @@ -3228,7 +3234,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, error = do_last(nd, &path, file, op, &opened, pathname); while (unlikely(error > 0)) { /* trailing symlink */ - struct path link = path; + struct path link = nd->link; void *cookie; error = may_follow_link(&link, nd); if (unlikely(error))