Commit 9f1fafee9e42b73beb3aa51ab2d6a19bfddeb5fe
1 parent
19660af736
Exists in
master
and in
4 other branches
merge handle_reval_dot and nameidata_drop_rcu_last
new helper: complete_walk(). Done on successful completion of walk, drops out of RCU mode, does d_revalidate of final result if that hadn't been done already. handle_reval_dot() and nameidata_drop_rcu_last() subsumed into that one; callers converted to use of complete_walk(). Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Showing 1 changed file with 40 additions and 81 deletions Side-by-side Diff
fs/namei.c
... | ... | @@ -469,43 +469,6 @@ |
469 | 469 | } |
470 | 470 | |
471 | 471 | /** |
472 | - * nameidata_drop_rcu_last - drop nameidata ending path walk out of rcu-walk | |
473 | - * @nd: nameidata pathwalk data to drop | |
474 | - * Returns: 0 on success, -ECHILD on failure | |
475 | - * | |
476 | - * nameidata_drop_rcu_last attempts to drop the current nd->path into ref-walk. | |
477 | - * nd->path should be the final element of the lookup, so nd->root is discarded. | |
478 | - * Must be called from rcu-walk context. | |
479 | - */ | |
480 | -static int nameidata_drop_rcu_last(struct nameidata *nd) | |
481 | -{ | |
482 | - struct dentry *dentry = nd->path.dentry; | |
483 | - | |
484 | - BUG_ON(!(nd->flags & LOOKUP_RCU)); | |
485 | - nd->flags &= ~LOOKUP_RCU; | |
486 | - if (!(nd->flags & LOOKUP_ROOT)) | |
487 | - nd->root.mnt = NULL; | |
488 | - spin_lock(&dentry->d_lock); | |
489 | - if (!__d_rcu_to_refcount(dentry, nd->seq)) | |
490 | - goto err_unlock; | |
491 | - BUG_ON(nd->inode != dentry->d_inode); | |
492 | - spin_unlock(&dentry->d_lock); | |
493 | - | |
494 | - mntget(nd->path.mnt); | |
495 | - | |
496 | - rcu_read_unlock(); | |
497 | - br_read_unlock(vfsmount_lock); | |
498 | - | |
499 | - return 0; | |
500 | - | |
501 | -err_unlock: | |
502 | - spin_unlock(&dentry->d_lock); | |
503 | - rcu_read_unlock(); | |
504 | - br_read_unlock(vfsmount_lock); | |
505 | - return -ECHILD; | |
506 | -} | |
507 | - | |
508 | -/** | |
509 | 472 | * release_open_intent - free up open intent resources |
510 | 473 | * @nd: pointer to nameidata |
511 | 474 | */ |
512 | 475 | |
513 | 476 | |
514 | 477 | |
... | ... | @@ -548,26 +511,39 @@ |
548 | 511 | return dentry; |
549 | 512 | } |
550 | 513 | |
551 | -/* | |
552 | - * handle_reval_path - force revalidation of a dentry | |
514 | +/** | |
515 | + * complete_walk - successful completion of path walk | |
516 | + * @nd: pointer nameidata | |
553 | 517 | * |
554 | - * In some situations the path walking code will trust dentries without | |
555 | - * revalidating them. This causes problems for filesystems that depend on | |
556 | - * d_revalidate to handle file opens (e.g. NFSv4). When FS_REVAL_DOT is set | |
557 | - * (which indicates that it's possible for the dentry to go stale), force | |
558 | - * a d_revalidate call before proceeding. | |
559 | - * | |
560 | - * Returns 0 if the revalidation was successful. If the revalidation fails, | |
561 | - * either return the error returned by d_revalidate or -ESTALE if the | |
562 | - * revalidation it just returned 0. If d_revalidate returns 0, we attempt to | |
563 | - * invalidate the dentry. It's up to the caller to handle putting references | |
564 | - * to the path if necessary. | |
518 | + * If we had been in RCU mode, drop out of it and legitimize nd->path. | |
519 | + * Revalidate the final result, unless we'd already done that during | |
520 | + * the path walk or the filesystem doesn't ask for it. Return 0 on | |
521 | + * success, -error on failure. In case of failure caller does not | |
522 | + * need to drop nd->path. | |
565 | 523 | */ |
566 | -static inline int handle_reval_path(struct nameidata *nd) | |
524 | +static int complete_walk(struct nameidata *nd) | |
567 | 525 | { |
568 | 526 | struct dentry *dentry = nd->path.dentry; |
569 | 527 | int status; |
570 | 528 | |
529 | + if (nd->flags & LOOKUP_RCU) { | |
530 | + nd->flags &= ~LOOKUP_RCU; | |
531 | + if (!(nd->flags & LOOKUP_ROOT)) | |
532 | + nd->root.mnt = NULL; | |
533 | + spin_lock(&dentry->d_lock); | |
534 | + if (unlikely(!__d_rcu_to_refcount(dentry, nd->seq))) { | |
535 | + spin_unlock(&dentry->d_lock); | |
536 | + rcu_read_unlock(); | |
537 | + br_read_unlock(vfsmount_lock); | |
538 | + return -ECHILD; | |
539 | + } | |
540 | + BUG_ON(nd->inode != dentry->d_inode); | |
541 | + spin_unlock(&dentry->d_lock); | |
542 | + mntget(nd->path.mnt); | |
543 | + rcu_read_unlock(); | |
544 | + br_read_unlock(vfsmount_lock); | |
545 | + } | |
546 | + | |
571 | 547 | if (likely(!(nd->flags & LOOKUP_JUMPED))) |
572 | 548 | return 0; |
573 | 549 | |
... | ... | @@ -585,6 +561,7 @@ |
585 | 561 | if (!status) |
586 | 562 | status = -ESTALE; |
587 | 563 | |
564 | + path_put(&nd->path); | |
588 | 565 | return status; |
589 | 566 | } |
590 | 567 | |
591 | 568 | |
... | ... | @@ -1598,19 +1575,9 @@ |
1598 | 1575 | } |
1599 | 1576 | } |
1600 | 1577 | |
1601 | - if (nd->flags & LOOKUP_RCU) { | |
1602 | - /* went all way through without dropping RCU */ | |
1603 | - BUG_ON(err); | |
1604 | - if (nameidata_drop_rcu_last(nd)) | |
1605 | - err = -ECHILD; | |
1606 | - } | |
1578 | + if (!err) | |
1579 | + err = complete_walk(nd); | |
1607 | 1580 | |
1608 | - if (!err) { | |
1609 | - err = handle_reval_path(nd); | |
1610 | - if (err) | |
1611 | - path_put(&nd->path); | |
1612 | - } | |
1613 | - | |
1614 | 1581 | if (!err && nd->flags & LOOKUP_DIRECTORY) { |
1615 | 1582 | if (!nd->inode->i_op->lookup) { |
1616 | 1583 | path_put(&nd->path); |
1617 | 1584 | |
... | ... | @@ -2075,13 +2042,9 @@ |
2075 | 2042 | return ERR_PTR(error); |
2076 | 2043 | /* fallthrough */ |
2077 | 2044 | case LAST_ROOT: |
2078 | - if (nd->flags & LOOKUP_RCU) { | |
2079 | - if (nameidata_drop_rcu_last(nd)) | |
2080 | - return ERR_PTR(-ECHILD); | |
2081 | - } | |
2082 | - error = handle_reval_path(nd); | |
2045 | + error = complete_walk(nd); | |
2083 | 2046 | if (error) |
2084 | - goto exit; | |
2047 | + return ERR_PTR(error); | |
2085 | 2048 | audit_inode(pathname, nd->path.dentry); |
2086 | 2049 | if (open_flag & O_CREAT) { |
2087 | 2050 | error = -EISDIR; |
2088 | 2051 | |
... | ... | @@ -2089,10 +2052,9 @@ |
2089 | 2052 | } |
2090 | 2053 | goto ok; |
2091 | 2054 | case LAST_BIND: |
2092 | - /* can't be RCU mode here */ | |
2093 | - error = handle_reval_path(nd); | |
2055 | + error = complete_walk(nd); | |
2094 | 2056 | if (error) |
2095 | - goto exit; | |
2057 | + return ERR_PTR(error); | |
2096 | 2058 | audit_inode(pathname, dir); |
2097 | 2059 | goto ok; |
2098 | 2060 | } |
... | ... | @@ -2111,10 +2073,9 @@ |
2111 | 2073 | if (error) /* symlink */ |
2112 | 2074 | return NULL; |
2113 | 2075 | /* sayonara */ |
2114 | - if (nd->flags & LOOKUP_RCU) { | |
2115 | - if (nameidata_drop_rcu_last(nd)) | |
2116 | - return ERR_PTR(-ECHILD); | |
2117 | - } | |
2076 | + error = complete_walk(nd); | |
2077 | + if (error) | |
2078 | + return ERR_PTR(-ECHILD); | |
2118 | 2079 | |
2119 | 2080 | error = -ENOTDIR; |
2120 | 2081 | if (nd->flags & LOOKUP_DIRECTORY) { |
... | ... | @@ -2126,11 +2087,9 @@ |
2126 | 2087 | } |
2127 | 2088 | |
2128 | 2089 | /* create side of things */ |
2129 | - | |
2130 | - if (nd->flags & LOOKUP_RCU) { | |
2131 | - if (nameidata_drop_rcu_last(nd)) | |
2132 | - return ERR_PTR(-ECHILD); | |
2133 | - } | |
2090 | + error = complete_walk(nd); | |
2091 | + if (error) | |
2092 | + return ERR_PTR(error); | |
2134 | 2093 | |
2135 | 2094 | audit_inode(pathname, dir); |
2136 | 2095 | error = -EISDIR; |