Commit 16c2cd7179881d5dd87779512ca5a0d657c64f62
1 parent
fe479a580d
Exists in
master
and in
20 other branches
untangle the "need_reval_dot" mess
instead of ad-hackery around need_reval_dot(), do the following: set a flag (LOOKUP_JUMPED) in the beginning of path, on absolute symlink traversal, on ".." and on procfs-style symlinks. Clear on normal components, leave unchanged on ".". Non-nested callers of link_path_walk() call handle_reval_path(), which checks that flag is set and that fs does want the final revalidate thing, then does ->d_revalidate(). In link_path_walk() all the return_reval stuff is gone. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Showing 2 changed files with 46 additions and 63 deletions Side-by-side Diff
fs/namei.c
... | ... | @@ -613,19 +613,8 @@ |
613 | 613 | return dentry; |
614 | 614 | } |
615 | 615 | |
616 | -static inline int need_reval_dot(struct dentry *dentry) | |
617 | -{ | |
618 | - if (likely(!(dentry->d_flags & DCACHE_OP_REVALIDATE))) | |
619 | - return 0; | |
620 | - | |
621 | - if (likely(!(dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT))) | |
622 | - return 0; | |
623 | - | |
624 | - return 1; | |
625 | -} | |
626 | - | |
627 | 616 | /* |
628 | - * force_reval_path - force revalidation of a dentry | |
617 | + * handle_reval_path - force revalidation of a dentry | |
629 | 618 | * |
630 | 619 | * In some situations the path walking code will trust dentries without |
631 | 620 | * revalidating them. This causes problems for filesystems that depend on |
632 | 621 | |
633 | 622 | |
634 | 623 | |
635 | 624 | |
636 | 625 | |
637 | 626 | |
... | ... | @@ -639,27 +628,28 @@ |
639 | 628 | * invalidate the dentry. It's up to the caller to handle putting references |
640 | 629 | * to the path if necessary. |
641 | 630 | */ |
642 | -static int | |
643 | -force_reval_path(struct path *path, struct nameidata *nd) | |
631 | +static inline int handle_reval_path(struct nameidata *nd) | |
644 | 632 | { |
633 | + struct dentry *dentry = nd->path.dentry; | |
645 | 634 | int status; |
646 | - struct dentry *dentry = path->dentry; | |
647 | 635 | |
648 | - /* | |
649 | - * only check on filesystems where it's possible for the dentry to | |
650 | - * become stale. | |
651 | - */ | |
652 | - if (!need_reval_dot(dentry)) | |
636 | + if (likely(!(nd->flags & LOOKUP_JUMPED))) | |
653 | 637 | return 0; |
654 | 638 | |
639 | + if (likely(!(dentry->d_flags & DCACHE_OP_REVALIDATE))) | |
640 | + return 0; | |
641 | + | |
642 | + if (likely(!(dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT))) | |
643 | + return 0; | |
644 | + | |
645 | + /* Note: we do not d_invalidate() */ | |
655 | 646 | status = d_revalidate(dentry, nd); |
656 | 647 | if (status > 0) |
657 | 648 | return 0; |
658 | 649 | |
659 | - if (!status) { | |
660 | - d_invalidate(dentry); | |
650 | + if (!status) | |
661 | 651 | status = -ESTALE; |
662 | - } | |
652 | + | |
663 | 653 | return status; |
664 | 654 | } |
665 | 655 | |
... | ... | @@ -728,6 +718,7 @@ |
728 | 718 | path_put(&nd->path); |
729 | 719 | nd->path = nd->root; |
730 | 720 | path_get(&nd->root); |
721 | + nd->flags |= LOOKUP_JUMPED; | |
731 | 722 | } |
732 | 723 | nd->inode = nd->path.dentry->d_inode; |
733 | 724 | |
... | ... | @@ -779,11 +770,8 @@ |
779 | 770 | error = 0; |
780 | 771 | if (s) |
781 | 772 | error = __vfs_follow_link(nd, s); |
782 | - else if (nd->last_type == LAST_BIND) { | |
783 | - error = force_reval_path(&nd->path, nd); | |
784 | - if (error) | |
785 | - path_put(&nd->path); | |
786 | - } | |
773 | + else if (nd->last_type == LAST_BIND) | |
774 | + nd->flags |= LOOKUP_JUMPED; | |
787 | 775 | } |
788 | 776 | return error; |
789 | 777 | } |
... | ... | @@ -1351,7 +1339,7 @@ |
1351 | 1339 | while (*name=='/') |
1352 | 1340 | name++; |
1353 | 1341 | if (!*name) |
1354 | - goto return_reval; | |
1342 | + goto return_base; | |
1355 | 1343 | |
1356 | 1344 | if (nd->depth) |
1357 | 1345 | lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE); |
1358 | 1346 | |
1359 | 1347 | |
... | ... | @@ -1385,12 +1373,16 @@ |
1385 | 1373 | type = LAST_NORM; |
1386 | 1374 | if (this.name[0] == '.') switch (this.len) { |
1387 | 1375 | case 2: |
1388 | - if (this.name[1] == '.') | |
1376 | + if (this.name[1] == '.') { | |
1389 | 1377 | type = LAST_DOTDOT; |
1378 | + nd->flags |= LOOKUP_JUMPED; | |
1379 | + } | |
1390 | 1380 | break; |
1391 | 1381 | case 1: |
1392 | 1382 | type = LAST_DOT; |
1393 | 1383 | } |
1384 | + if (likely(type == LAST_NORM)) | |
1385 | + nd->flags &= ~LOOKUP_JUMPED; | |
1394 | 1386 | |
1395 | 1387 | /* remove trailing slashes? */ |
1396 | 1388 | if (!c) |
... | ... | @@ -1456,7 +1448,7 @@ |
1456 | 1448 | } else |
1457 | 1449 | follow_dotdot(nd); |
1458 | 1450 | } |
1459 | - goto return_reval; | |
1451 | + goto return_base; | |
1460 | 1452 | } |
1461 | 1453 | err = do_lookup(nd, &this, &next, &inode); |
1462 | 1454 | if (err) |
... | ... | @@ -1483,24 +1475,6 @@ |
1483 | 1475 | lookup_parent: |
1484 | 1476 | nd->last = this; |
1485 | 1477 | nd->last_type = type; |
1486 | - if (type == LAST_NORM) | |
1487 | - goto return_base; | |
1488 | -return_reval: | |
1489 | - /* | |
1490 | - * We bypassed the ordinary revalidation routines. | |
1491 | - * We may need to check the cached dentry for staleness. | |
1492 | - */ | |
1493 | - if (need_reval_dot(nd->path.dentry)) { | |
1494 | - if (nameidata_drop_rcu_last_maybe(nd)) | |
1495 | - return -ECHILD; | |
1496 | - /* Note: we do not d_invalidate() */ | |
1497 | - err = d_revalidate(nd->path.dentry, nd); | |
1498 | - if (!err) | |
1499 | - err = -ESTALE; | |
1500 | - if (err < 0) | |
1501 | - break; | |
1502 | - return 0; | |
1503 | - } | |
1504 | 1478 | return_base: |
1505 | 1479 | if (nameidata_drop_rcu_last_maybe(nd)) |
1506 | 1480 | return -ECHILD; |
... | ... | @@ -1523,7 +1497,7 @@ |
1523 | 1497 | struct file *file; |
1524 | 1498 | |
1525 | 1499 | nd->last_type = LAST_ROOT; /* if there are only slashes... */ |
1526 | - nd->flags = flags; | |
1500 | + nd->flags = flags | LOOKUP_JUMPED; | |
1527 | 1501 | nd->depth = 0; |
1528 | 1502 | nd->root.mnt = NULL; |
1529 | 1503 | nd->file = NULL; |
... | ... | @@ -1630,6 +1604,9 @@ |
1630 | 1604 | br_read_unlock(vfsmount_lock); |
1631 | 1605 | } |
1632 | 1606 | |
1607 | + if (!retval) | |
1608 | + retval = handle_reval_path(nd); | |
1609 | + | |
1633 | 1610 | if (nd->file) { |
1634 | 1611 | fput(nd->file); |
1635 | 1612 | nd->file = NULL; |
... | ... | @@ -1690,7 +1667,7 @@ |
1690 | 1667 | |
1691 | 1668 | /* same as do_path_lookup */ |
1692 | 1669 | nd->last_type = LAST_ROOT; |
1693 | - nd->flags = flags; | |
1670 | + nd->flags = flags | LOOKUP_JUMPED; | |
1694 | 1671 | nd->depth = 0; |
1695 | 1672 | |
1696 | 1673 | nd->path.dentry = dentry; |
... | ... | @@ -1703,6 +1680,8 @@ |
1703 | 1680 | current->total_link_count = 0; |
1704 | 1681 | |
1705 | 1682 | result = link_path_walk(name, nd); |
1683 | + if (!result) | |
1684 | + result = handle_reval_path(nd); | |
1706 | 1685 | if (result == -ESTALE) { |
1707 | 1686 | /* nd->path had been dropped */ |
1708 | 1687 | current->total_link_count = 0; |
1709 | 1688 | |
... | ... | @@ -1710,8 +1689,11 @@ |
1710 | 1689 | nd->path.mnt = mnt; |
1711 | 1690 | nd->inode = dentry->d_inode; |
1712 | 1691 | path_get(&nd->path); |
1713 | - nd->flags |= LOOKUP_REVAL; | |
1692 | + nd->flags = flags | LOOKUP_JUMPED | LOOKUP_REVAL; | |
1693 | + | |
1714 | 1694 | result = link_path_walk(name, nd); |
1695 | + if (!result) | |
1696 | + result = handle_reval_path(nd); | |
1715 | 1697 | } |
1716 | 1698 | if (unlikely(!result && !audit_dummy_context() && nd->path.dentry && |
1717 | 1699 | nd->inode)) |
1718 | 1700 | |
1719 | 1701 | |
1720 | 1702 | |
1721 | 1703 | |
... | ... | @@ -2198,30 +2180,29 @@ |
2198 | 2180 | { |
2199 | 2181 | struct dentry *dir = nd->path.dentry; |
2200 | 2182 | struct file *filp; |
2201 | - int error = -EISDIR; | |
2183 | + int error; | |
2202 | 2184 | |
2203 | 2185 | switch (nd->last_type) { |
2204 | 2186 | case LAST_DOTDOT: |
2205 | 2187 | follow_dotdot(nd); |
2206 | 2188 | dir = nd->path.dentry; |
2207 | 2189 | case LAST_DOT: |
2208 | - if (need_reval_dot(dir)) { | |
2209 | - int status = d_revalidate(nd->path.dentry, nd); | |
2210 | - if (!status) | |
2211 | - status = -ESTALE; | |
2212 | - if (status < 0) { | |
2213 | - error = status; | |
2214 | - goto exit; | |
2215 | - } | |
2216 | - } | |
2217 | 2190 | /* fallthrough */ |
2218 | 2191 | case LAST_ROOT: |
2192 | + error = handle_reval_path(nd); | |
2193 | + if (error) | |
2194 | + goto exit; | |
2195 | + error = -EISDIR; | |
2219 | 2196 | goto exit; |
2220 | 2197 | case LAST_BIND: |
2198 | + error = handle_reval_path(nd); | |
2199 | + if (error) | |
2200 | + goto exit; | |
2221 | 2201 | audit_inode(pathname, dir); |
2222 | 2202 | goto ok; |
2223 | 2203 | } |
2224 | 2204 | |
2205 | + error = -EISDIR; | |
2225 | 2206 | /* trailing slashes? */ |
2226 | 2207 | if (nd->last.name[nd->last.len]) |
2227 | 2208 | goto exit; |
... | ... | @@ -2422,7 +2403,7 @@ |
2422 | 2403 | /* |
2423 | 2404 | * We have the parent and last component. |
2424 | 2405 | */ |
2425 | - nd.flags = flags; | |
2406 | + nd.flags = (nd.flags & ~LOOKUP_PARENT) | flags; | |
2426 | 2407 | filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); |
2427 | 2408 | while (unlikely(!filp)) { /* trailing symlink */ |
2428 | 2409 | struct path link = path; |
include/linux/namei.h
... | ... | @@ -63,6 +63,8 @@ |
63 | 63 | #define LOOKUP_EXCL 0x0400 |
64 | 64 | #define LOOKUP_RENAME_TARGET 0x0800 |
65 | 65 | |
66 | +#define LOOKUP_JUMPED 0x1000 | |
67 | + | |
66 | 68 | extern int user_path_at(int, const char __user *, unsigned, struct path *); |
67 | 69 | |
68 | 70 | #define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path) |