diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-13 19:58:58 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-15 17:16:20 -0400 |
commit | ce57dfc1791221ef58b6d6b8f5437fccefc4e187 (patch) | |
tree | f4745174c126231bbd2c4a652d37f086ad035e3c | |
parent | 11a7b371b64ef39fc5fb1b6f2218eef7c4d035e3 (diff) |
pull handling of one pathname component into a helper
new helper: walk_component(). Handles everything except symlinks;
returns negative on error, 0 on success and 1 on symlinks we decided
to follow. Drops out of RCU mode on such symlinks.
link_path_walk() and do_last() switched to using that.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/namei.c | 123 |
1 files changed, 55 insertions, 68 deletions
diff --git a/fs/namei.c b/fs/namei.c index c9b7f5b7e92a..549bbe2f25c6 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -785,16 +785,11 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p) | |||
785 | * Without that kind of total limit, nasty chains of consecutive | 785 | * Without that kind of total limit, nasty chains of consecutive |
786 | * symlinks can cause almost arbitrarily long lookups. | 786 | * symlinks can cause almost arbitrarily long lookups. |
787 | */ | 787 | */ |
788 | static inline int do_follow_link(struct inode *inode, struct path *path, struct nameidata *nd) | 788 | static inline int do_follow_link(struct path *path, struct nameidata *nd) |
789 | { | 789 | { |
790 | void *cookie; | 790 | void *cookie; |
791 | int err = -ELOOP; | 791 | int err = -ELOOP; |
792 | 792 | ||
793 | /* We drop rcu-walk here */ | ||
794 | if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry)) | ||
795 | return -ECHILD; | ||
796 | BUG_ON(inode != path->dentry->d_inode); | ||
797 | |||
798 | if (current->link_count >= MAX_NESTED_LINKS) | 793 | if (current->link_count >= MAX_NESTED_LINKS) |
799 | goto loop; | 794 | goto loop; |
800 | if (current->total_link_count >= 40) | 795 | if (current->total_link_count >= 40) |
@@ -1337,6 +1332,39 @@ static void terminate_walk(struct nameidata *nd) | |||
1337 | } | 1332 | } |
1338 | } | 1333 | } |
1339 | 1334 | ||
1335 | static inline int walk_component(struct nameidata *nd, struct path *path, | ||
1336 | struct qstr *name, int type, int follow) | ||
1337 | { | ||
1338 | struct inode *inode; | ||
1339 | int err; | ||
1340 | /* | ||
1341 | * "." and ".." are special - ".." especially so because it has | ||
1342 | * to be able to know about the current root directory and | ||
1343 | * parent relationships. | ||
1344 | */ | ||
1345 | if (unlikely(type != LAST_NORM)) | ||
1346 | return handle_dots(nd, type); | ||
1347 | err = do_lookup(nd, name, path, &inode); | ||
1348 | if (unlikely(err)) { | ||
1349 | terminate_walk(nd); | ||
1350 | return err; | ||
1351 | } | ||
1352 | if (!inode) { | ||
1353 | path_to_nameidata(path, nd); | ||
1354 | terminate_walk(nd); | ||
1355 | return -ENOENT; | ||
1356 | } | ||
1357 | if (unlikely(inode->i_op->follow_link) && follow) { | ||
1358 | if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry)) | ||
1359 | return -ECHILD; | ||
1360 | BUG_ON(inode != path->dentry->d_inode); | ||
1361 | return 1; | ||
1362 | } | ||
1363 | path_to_nameidata(path, nd); | ||
1364 | nd->inode = inode; | ||
1365 | return 0; | ||
1366 | } | ||
1367 | |||
1340 | /* | 1368 | /* |
1341 | * Name resolution. | 1369 | * Name resolution. |
1342 | * This is the basic name resolution function, turning a pathname into | 1370 | * This is the basic name resolution function, turning a pathname into |
@@ -1361,7 +1389,6 @@ static int link_path_walk(const char *name, struct nameidata *nd) | |||
1361 | 1389 | ||
1362 | /* At this point we know we have a real path component. */ | 1390 | /* At this point we know we have a real path component. */ |
1363 | for(;;) { | 1391 | for(;;) { |
1364 | struct inode *inode; | ||
1365 | unsigned long hash; | 1392 | unsigned long hash; |
1366 | struct qstr this; | 1393 | struct qstr this; |
1367 | unsigned int c; | 1394 | unsigned int c; |
@@ -1414,34 +1441,16 @@ static int link_path_walk(const char *name, struct nameidata *nd) | |||
1414 | if (!*name) | 1441 | if (!*name) |
1415 | goto last_with_slashes; | 1442 | goto last_with_slashes; |
1416 | 1443 | ||
1417 | /* | 1444 | err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW); |
1418 | * "." and ".." are special - ".." especially so because it has | 1445 | if (err < 0) |
1419 | * to be able to know about the current root directory and | 1446 | return err; |
1420 | * parent relationships. | ||
1421 | */ | ||
1422 | if (unlikely(type != LAST_NORM)) { | ||
1423 | if (handle_dots(nd, type)) | ||
1424 | return -ECHILD; | ||
1425 | continue; | ||
1426 | } | ||
1427 | |||
1428 | /* This does the actual lookups.. */ | ||
1429 | err = do_lookup(nd, &this, &next, &inode); | ||
1430 | if (err) | ||
1431 | break; | ||
1432 | 1447 | ||
1433 | if (inode && inode->i_op->follow_link) { | 1448 | if (err) { |
1434 | err = do_follow_link(inode, &next, nd); | 1449 | err = do_follow_link(&next, nd); |
1435 | if (err) | 1450 | if (err) |
1436 | return err; | 1451 | return err; |
1437 | nd->inode = nd->path.dentry->d_inode; | 1452 | nd->inode = nd->path.dentry->d_inode; |
1438 | } else { | ||
1439 | path_to_nameidata(&next, nd); | ||
1440 | nd->inode = inode; | ||
1441 | } | 1453 | } |
1442 | err = -ENOENT; | ||
1443 | if (!nd->inode) | ||
1444 | break; | ||
1445 | err = -ENOTDIR; | 1454 | err = -ENOTDIR; |
1446 | if (!nd->inode->i_op->lookup) | 1455 | if (!nd->inode->i_op->lookup) |
1447 | break; | 1456 | break; |
@@ -1453,36 +1462,27 @@ last_with_slashes: | |||
1453 | last_component: | 1462 | last_component: |
1454 | /* Clear LOOKUP_CONTINUE iff it was previously unset */ | 1463 | /* Clear LOOKUP_CONTINUE iff it was previously unset */ |
1455 | nd->flags &= lookup_flags | ~LOOKUP_CONTINUE; | 1464 | nd->flags &= lookup_flags | ~LOOKUP_CONTINUE; |
1456 | if (lookup_flags & LOOKUP_PARENT) | 1465 | if (lookup_flags & LOOKUP_PARENT) { |
1457 | goto lookup_parent; | 1466 | nd->last = this; |
1458 | if (unlikely(type != LAST_NORM)) | 1467 | nd->last_type = type; |
1459 | return handle_dots(nd, type); | 1468 | return 0; |
1460 | err = do_lookup(nd, &this, &next, &inode); | 1469 | } |
1461 | if (err) | 1470 | err = walk_component(nd, &next, &this, type, |
1462 | break; | 1471 | lookup_flags & LOOKUP_FOLLOW); |
1463 | if (inode && unlikely(inode->i_op->follow_link) && | 1472 | if (err < 0) |
1464 | (lookup_flags & LOOKUP_FOLLOW)) { | 1473 | return err; |
1465 | err = do_follow_link(inode, &next, nd); | 1474 | if (err) { |
1475 | err = do_follow_link(&next, nd); | ||
1466 | if (err) | 1476 | if (err) |
1467 | return err; | 1477 | return err; |
1468 | nd->inode = nd->path.dentry->d_inode; | 1478 | nd->inode = nd->path.dentry->d_inode; |
1469 | } else { | ||
1470 | path_to_nameidata(&next, nd); | ||
1471 | nd->inode = inode; | ||
1472 | } | 1479 | } |
1473 | err = -ENOENT; | ||
1474 | if (!nd->inode) | ||
1475 | break; | ||
1476 | if (lookup_flags & LOOKUP_DIRECTORY) { | 1480 | if (lookup_flags & LOOKUP_DIRECTORY) { |
1477 | err = -ENOTDIR; | 1481 | err = -ENOTDIR; |
1478 | if (!nd->inode->i_op->lookup) | 1482 | if (!nd->inode->i_op->lookup) |
1479 | break; | 1483 | break; |
1480 | } | 1484 | } |
1481 | return 0; | 1485 | return 0; |
1482 | lookup_parent: | ||
1483 | nd->last = this; | ||
1484 | nd->last_type = type; | ||
1485 | return 0; | ||
1486 | } | 1486 | } |
1487 | terminate_walk(nd); | 1487 | terminate_walk(nd); |
1488 | return err; | 1488 | return err; |
@@ -2068,7 +2068,6 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2068 | int want_write = 0; | 2068 | int want_write = 0; |
2069 | int acc_mode = op->acc_mode; | 2069 | int acc_mode = op->acc_mode; |
2070 | struct file *filp; | 2070 | struct file *filp; |
2071 | struct inode *inode; | ||
2072 | int error; | 2071 | int error; |
2073 | 2072 | ||
2074 | nd->flags &= ~LOOKUP_PARENT; | 2073 | nd->flags &= ~LOOKUP_PARENT; |
@@ -2111,24 +2110,12 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2111 | if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW)) | 2110 | if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW)) |
2112 | symlink_ok = 1; | 2111 | symlink_ok = 1; |
2113 | /* we _can_ be in RCU mode here */ | 2112 | /* we _can_ be in RCU mode here */ |
2114 | error = do_lookup(nd, &nd->last, path, &inode); | 2113 | error = walk_component(nd, path, &nd->last, LAST_NORM, |
2115 | if (error) { | 2114 | !symlink_ok); |
2116 | terminate_walk(nd); | 2115 | if (error < 0) |
2117 | return ERR_PTR(error); | 2116 | return ERR_PTR(error); |
2118 | } | 2117 | if (error) /* symlink */ |
2119 | if (!inode) { | ||
2120 | path_to_nameidata(path, nd); | ||
2121 | terminate_walk(nd); | ||
2122 | return ERR_PTR(-ENOENT); | ||
2123 | } | ||
2124 | if (unlikely(inode->i_op->follow_link && !symlink_ok)) { | ||
2125 | /* We drop rcu-walk here */ | ||
2126 | if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry)) | ||
2127 | return ERR_PTR(-ECHILD); | ||
2128 | return NULL; | 2118 | return NULL; |
2129 | } | ||
2130 | path_to_nameidata(path, nd); | ||
2131 | nd->inode = inode; | ||
2132 | /* sayonara */ | 2119 | /* sayonara */ |
2133 | if (nd->flags & LOOKUP_RCU) { | 2120 | if (nd->flags & LOOKUP_RCU) { |
2134 | if (nameidata_drop_rcu_last(nd)) | 2121 | if (nameidata_drop_rcu_last(nd)) |
@@ -2137,7 +2124,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2137 | 2124 | ||
2138 | error = -ENOTDIR; | 2125 | error = -ENOTDIR; |
2139 | if (nd->flags & LOOKUP_DIRECTORY) { | 2126 | if (nd->flags & LOOKUP_DIRECTORY) { |
2140 | if (!inode->i_op->lookup) | 2127 | if (!nd->inode->i_op->lookup) |
2141 | goto exit; | 2128 | goto exit; |
2142 | } | 2129 | } |
2143 | audit_inode(pathname, nd->path.dentry); | 2130 | audit_inode(pathname, nd->path.dentry); |