aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/namei.c71
1 files changed, 26 insertions, 45 deletions
diff --git a/fs/namei.c b/fs/namei.c
index 017c3fa3a08e..0a601cae23de 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -737,14 +737,31 @@ static inline void path_to_nameidata(const struct path *path,
737 nd->path.dentry = path->dentry; 737 nd->path.dentry = path->dentry;
738} 738}
739 739
740static inline void put_link(struct nameidata *nd, struct path *link, void *cookie)
741{
742 struct inode *inode = link->dentry->d_inode;
743 if (!IS_ERR(cookie) && inode->i_op->put_link)
744 inode->i_op->put_link(link->dentry, nd, cookie);
745 path_put(link);
746}
747
740static __always_inline int 748static __always_inline int
741__do_follow_link(const struct path *link, struct nameidata *nd, void **p) 749follow_link(struct path *link, struct nameidata *nd, void **p)
742{ 750{
743 int error; 751 int error;
744 struct dentry *dentry = link->dentry; 752 struct dentry *dentry = link->dentry;
745 753
746 BUG_ON(nd->flags & LOOKUP_RCU); 754 BUG_ON(nd->flags & LOOKUP_RCU);
747 755
756 if (unlikely(current->total_link_count >= 40)) {
757 *p = ERR_PTR(-ELOOP); /* no ->put_link(), please */
758 path_put_conditional(link, nd);
759 path_put(&nd->path);
760 return -ELOOP;
761 }
762 cond_resched();
763 current->total_link_count++;
764
748 touch_atime(link->mnt, dentry); 765 touch_atime(link->mnt, dentry);
749 nd_set_link(nd, NULL); 766 nd_set_link(nd, NULL);
750 767
@@ -1356,21 +1373,12 @@ static inline int nested_symlink(struct path *path, struct nameidata *nd)
1356 do { 1373 do {
1357 struct path link = *path; 1374 struct path link = *path;
1358 void *cookie; 1375 void *cookie;
1359 if (unlikely(current->total_link_count >= 40)) { 1376
1360 path_put_conditional(path, nd); 1377 res = follow_link(&link, nd, &cookie);
1361 path_put(&nd->path);
1362 res = -ELOOP;
1363 break;
1364 }
1365 cond_resched();
1366 current->total_link_count++;
1367 res = __do_follow_link(&link, nd, &cookie);
1368 if (!res) 1378 if (!res)
1369 res = walk_component(nd, path, &nd->last, 1379 res = walk_component(nd, path, &nd->last,
1370 nd->last_type, LOOKUP_FOLLOW); 1380 nd->last_type, LOOKUP_FOLLOW);
1371 if (!IS_ERR(cookie) && link.dentry->d_inode->i_op->put_link) 1381 put_link(nd, &link, cookie);
1372 link.dentry->d_inode->i_op->put_link(link.dentry, nd, cookie);
1373 path_put(&link);
1374 } while (res > 0); 1382 } while (res > 0);
1375 1383
1376 current->link_count--; 1384 current->link_count--;
@@ -1619,27 +1627,15 @@ static int path_lookupat(int dfd, const char *name,
1619 err = link_path_walk(name, nd); 1627 err = link_path_walk(name, nd);
1620 1628
1621 if (!err && !(flags & LOOKUP_PARENT)) { 1629 if (!err && !(flags & LOOKUP_PARENT)) {
1622 int count = 0;
1623 err = lookup_last(nd, &path); 1630 err = lookup_last(nd, &path);
1624 while (err > 0) { 1631 while (err > 0) {
1625 void *cookie; 1632 void *cookie;
1626 struct path link = path; 1633 struct path link = path;
1627 struct inode *inode = link.dentry->d_inode;
1628
1629 if (count++ > 32) {
1630 path_put_conditional(&path, nd);
1631 path_put(&nd->path);
1632 err = -ELOOP;
1633 break;
1634 }
1635 cond_resched();
1636 nd->flags |= LOOKUP_PARENT; 1634 nd->flags |= LOOKUP_PARENT;
1637 err = __do_follow_link(&link, nd, &cookie); 1635 err = follow_link(&link, nd, &cookie);
1638 if (!err) 1636 if (!err)
1639 err = lookup_last(nd, &path); 1637 err = lookup_last(nd, &path);
1640 if (!IS_ERR(cookie) && inode->i_op->put_link) 1638 put_link(nd, &link, cookie);
1641 inode->i_op->put_link(link.dentry, nd, cookie);
1642 path_put(&link);
1643 } 1639 }
1644 } 1640 }
1645 1641
@@ -2298,7 +2294,6 @@ static struct file *path_openat(int dfd, const char *pathname,
2298 struct file *base = NULL; 2294 struct file *base = NULL;
2299 struct file *filp; 2295 struct file *filp;
2300 struct path path; 2296 struct path path;
2301 int count = 0;
2302 int error; 2297 int error;
2303 2298
2304 filp = get_empty_filp(); 2299 filp = get_empty_filp();
@@ -2322,35 +2317,21 @@ static struct file *path_openat(int dfd, const char *pathname,
2322 filp = do_last(nd, &path, op, pathname); 2317 filp = do_last(nd, &path, op, pathname);
2323 while (unlikely(!filp)) { /* trailing symlink */ 2318 while (unlikely(!filp)) { /* trailing symlink */
2324 struct path link = path; 2319 struct path link = path;
2325 struct inode *linki = link.dentry->d_inode;
2326 void *cookie; 2320 void *cookie;
2327 if (!(nd->flags & LOOKUP_FOLLOW) || count++ == 32) { 2321 if (!(nd->flags & LOOKUP_FOLLOW)) {
2328 path_put_conditional(&path, nd); 2322 path_put_conditional(&path, nd);
2329 path_put(&nd->path); 2323 path_put(&nd->path);
2330 filp = ERR_PTR(-ELOOP); 2324 filp = ERR_PTR(-ELOOP);
2331 break; 2325 break;
2332 } 2326 }
2333 /*
2334 * This is subtle. Instead of calling do_follow_link() we do
2335 * the thing by hands. The reason is that this way we have zero
2336 * link_count and path_walk() (called from ->follow_link)
2337 * honoring LOOKUP_PARENT. After that we have the parent and
2338 * last component, i.e. we are in the same situation as after
2339 * the first path_walk(). Well, almost - if the last component
2340 * is normal we get its copy stored in nd->last.name and we will
2341 * have to putname() it when we are done. Procfs-like symlinks
2342 * just set LAST_BIND.
2343 */
2344 nd->flags |= LOOKUP_PARENT; 2327 nd->flags |= LOOKUP_PARENT;
2345 nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); 2328 nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
2346 error = __do_follow_link(&link, nd, &cookie); 2329 error = follow_link(&link, nd, &cookie);
2347 if (unlikely(error)) 2330 if (unlikely(error))
2348 filp = ERR_PTR(error); 2331 filp = ERR_PTR(error);
2349 else 2332 else
2350 filp = do_last(nd, &path, op, pathname); 2333 filp = do_last(nd, &path, op, pathname);
2351 if (!IS_ERR(cookie) && linki->i_op->put_link) 2334 put_link(nd, &link, cookie);
2352 linki->i_op->put_link(link.dentry, nd, cookie);
2353 path_put(&link);
2354 } 2335 }
2355out: 2336out:
2356 if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) 2337 if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT))