diff options
-rw-r--r-- | fs/namei.c | 95 | ||||
-rw-r--r-- | include/linux/namei.h | 1 |
2 files changed, 44 insertions, 52 deletions
diff --git a/fs/namei.c b/fs/namei.c index 0bebd13e5cb7..8ee7785d5642 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -401,9 +401,11 @@ static int nameidata_drop_rcu(struct nameidata *nd) | |||
401 | { | 401 | { |
402 | struct fs_struct *fs = current->fs; | 402 | struct fs_struct *fs = current->fs; |
403 | struct dentry *dentry = nd->path.dentry; | 403 | struct dentry *dentry = nd->path.dentry; |
404 | int want_root = 0; | ||
404 | 405 | ||
405 | BUG_ON(!(nd->flags & LOOKUP_RCU)); | 406 | BUG_ON(!(nd->flags & LOOKUP_RCU)); |
406 | if (nd->root.mnt) { | 407 | if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { |
408 | want_root = 1; | ||
407 | spin_lock(&fs->lock); | 409 | spin_lock(&fs->lock); |
408 | if (nd->root.mnt != fs->root.mnt || | 410 | if (nd->root.mnt != fs->root.mnt || |
409 | nd->root.dentry != fs->root.dentry) | 411 | nd->root.dentry != fs->root.dentry) |
@@ -414,7 +416,7 @@ static int nameidata_drop_rcu(struct nameidata *nd) | |||
414 | goto err; | 416 | goto err; |
415 | BUG_ON(nd->inode != dentry->d_inode); | 417 | BUG_ON(nd->inode != dentry->d_inode); |
416 | spin_unlock(&dentry->d_lock); | 418 | spin_unlock(&dentry->d_lock); |
417 | if (nd->root.mnt) { | 419 | if (want_root) { |
418 | path_get(&nd->root); | 420 | path_get(&nd->root); |
419 | spin_unlock(&fs->lock); | 421 | spin_unlock(&fs->lock); |
420 | } | 422 | } |
@@ -427,7 +429,7 @@ static int nameidata_drop_rcu(struct nameidata *nd) | |||
427 | err: | 429 | err: |
428 | spin_unlock(&dentry->d_lock); | 430 | spin_unlock(&dentry->d_lock); |
429 | err_root: | 431 | err_root: |
430 | if (nd->root.mnt) | 432 | if (want_root) |
431 | spin_unlock(&fs->lock); | 433 | spin_unlock(&fs->lock); |
432 | return -ECHILD; | 434 | return -ECHILD; |
433 | } | 435 | } |
@@ -454,9 +456,11 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry | |||
454 | { | 456 | { |
455 | struct fs_struct *fs = current->fs; | 457 | struct fs_struct *fs = current->fs; |
456 | struct dentry *parent = nd->path.dentry; | 458 | struct dentry *parent = nd->path.dentry; |
459 | int want_root = 0; | ||
457 | 460 | ||
458 | BUG_ON(!(nd->flags & LOOKUP_RCU)); | 461 | BUG_ON(!(nd->flags & LOOKUP_RCU)); |
459 | if (nd->root.mnt) { | 462 | if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { |
463 | want_root = 1; | ||
460 | spin_lock(&fs->lock); | 464 | spin_lock(&fs->lock); |
461 | if (nd->root.mnt != fs->root.mnt || | 465 | if (nd->root.mnt != fs->root.mnt || |
462 | nd->root.dentry != fs->root.dentry) | 466 | nd->root.dentry != fs->root.dentry) |
@@ -476,7 +480,7 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry | |||
476 | parent->d_count++; | 480 | parent->d_count++; |
477 | spin_unlock(&dentry->d_lock); | 481 | spin_unlock(&dentry->d_lock); |
478 | spin_unlock(&parent->d_lock); | 482 | spin_unlock(&parent->d_lock); |
479 | if (nd->root.mnt) { | 483 | if (want_root) { |
480 | path_get(&nd->root); | 484 | path_get(&nd->root); |
481 | spin_unlock(&fs->lock); | 485 | spin_unlock(&fs->lock); |
482 | } | 486 | } |
@@ -490,7 +494,7 @@ err: | |||
490 | spin_unlock(&dentry->d_lock); | 494 | spin_unlock(&dentry->d_lock); |
491 | spin_unlock(&parent->d_lock); | 495 | spin_unlock(&parent->d_lock); |
492 | err_root: | 496 | err_root: |
493 | if (nd->root.mnt) | 497 | if (want_root) |
494 | spin_unlock(&fs->lock); | 498 | spin_unlock(&fs->lock); |
495 | return -ECHILD; | 499 | return -ECHILD; |
496 | } | 500 | } |
@@ -501,7 +505,8 @@ static inline int nameidata_dentry_drop_rcu_maybe(struct nameidata *nd, struct d | |||
501 | if (nd->flags & LOOKUP_RCU) { | 505 | if (nd->flags & LOOKUP_RCU) { |
502 | if (unlikely(nameidata_dentry_drop_rcu(nd, dentry))) { | 506 | if (unlikely(nameidata_dentry_drop_rcu(nd, dentry))) { |
503 | nd->flags &= ~LOOKUP_RCU; | 507 | nd->flags &= ~LOOKUP_RCU; |
504 | nd->root.mnt = NULL; | 508 | if (!(nd->flags & LOOKUP_ROOT)) |
509 | nd->root.mnt = NULL; | ||
505 | rcu_read_unlock(); | 510 | rcu_read_unlock(); |
506 | br_read_unlock(vfsmount_lock); | 511 | br_read_unlock(vfsmount_lock); |
507 | return -ECHILD; | 512 | return -ECHILD; |
@@ -525,7 +530,8 @@ static int nameidata_drop_rcu_last(struct nameidata *nd) | |||
525 | 530 | ||
526 | BUG_ON(!(nd->flags & LOOKUP_RCU)); | 531 | BUG_ON(!(nd->flags & LOOKUP_RCU)); |
527 | nd->flags &= ~LOOKUP_RCU; | 532 | nd->flags &= ~LOOKUP_RCU; |
528 | nd->root.mnt = NULL; | 533 | if (!(nd->flags & LOOKUP_ROOT)) |
534 | nd->root.mnt = NULL; | ||
529 | spin_lock(&dentry->d_lock); | 535 | spin_lock(&dentry->d_lock); |
530 | if (!__d_rcu_to_refcount(dentry, nd->seq)) | 536 | if (!__d_rcu_to_refcount(dentry, nd->seq)) |
531 | goto err_unlock; | 537 | goto err_unlock; |
@@ -1053,7 +1059,8 @@ static int follow_dotdot_rcu(struct nameidata *nd) | |||
1053 | 1059 | ||
1054 | failed: | 1060 | failed: |
1055 | nd->flags &= ~LOOKUP_RCU; | 1061 | nd->flags &= ~LOOKUP_RCU; |
1056 | nd->root.mnt = NULL; | 1062 | if (!(nd->flags & LOOKUP_ROOT)) |
1063 | nd->root.mnt = NULL; | ||
1057 | rcu_read_unlock(); | 1064 | rcu_read_unlock(); |
1058 | br_read_unlock(vfsmount_lock); | 1065 | br_read_unlock(vfsmount_lock); |
1059 | return -ECHILD; | 1066 | return -ECHILD; |
@@ -1310,7 +1317,8 @@ static void terminate_walk(struct nameidata *nd) | |||
1310 | path_put(&nd->path); | 1317 | path_put(&nd->path); |
1311 | } else { | 1318 | } else { |
1312 | nd->flags &= ~LOOKUP_RCU; | 1319 | nd->flags &= ~LOOKUP_RCU; |
1313 | nd->root.mnt = NULL; | 1320 | if (!(nd->flags & LOOKUP_ROOT)) |
1321 | nd->root.mnt = NULL; | ||
1314 | rcu_read_unlock(); | 1322 | rcu_read_unlock(); |
1315 | br_read_unlock(vfsmount_lock); | 1323 | br_read_unlock(vfsmount_lock); |
1316 | } | 1324 | } |
@@ -1477,6 +1485,25 @@ static int path_init(int dfd, const char *name, unsigned int flags, | |||
1477 | nd->last_type = LAST_ROOT; /* if there are only slashes... */ | 1485 | nd->last_type = LAST_ROOT; /* if there are only slashes... */ |
1478 | nd->flags = flags | LOOKUP_JUMPED; | 1486 | nd->flags = flags | LOOKUP_JUMPED; |
1479 | nd->depth = 0; | 1487 | nd->depth = 0; |
1488 | if (flags & LOOKUP_ROOT) { | ||
1489 | struct inode *inode = nd->root.dentry->d_inode; | ||
1490 | if (!inode->i_op->lookup) | ||
1491 | return -ENOTDIR; | ||
1492 | retval = inode_permission(inode, MAY_EXEC); | ||
1493 | if (retval) | ||
1494 | return retval; | ||
1495 | nd->path = nd->root; | ||
1496 | nd->inode = inode; | ||
1497 | if (flags & LOOKUP_RCU) { | ||
1498 | br_read_lock(vfsmount_lock); | ||
1499 | rcu_read_lock(); | ||
1500 | nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq); | ||
1501 | } else { | ||
1502 | path_get(&nd->path); | ||
1503 | } | ||
1504 | return 0; | ||
1505 | } | ||
1506 | |||
1480 | nd->root.mnt = NULL; | 1507 | nd->root.mnt = NULL; |
1481 | 1508 | ||
1482 | if (*name=='/') { | 1509 | if (*name=='/') { |
@@ -1587,7 +1614,7 @@ static int path_lookupat(int dfd, const char *name, | |||
1587 | if (base) | 1614 | if (base) |
1588 | fput(base); | 1615 | fput(base); |
1589 | 1616 | ||
1590 | if (nd->root.mnt) { | 1617 | if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { |
1591 | path_put(&nd->root); | 1618 | path_put(&nd->root); |
1592 | nd->root.mnt = NULL; | 1619 | nd->root.mnt = NULL; |
1593 | } | 1620 | } |
@@ -1638,46 +1665,10 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, | |||
1638 | const char *name, unsigned int flags, | 1665 | const char *name, unsigned int flags, |
1639 | struct nameidata *nd) | 1666 | struct nameidata *nd) |
1640 | { | 1667 | { |
1641 | int result; | 1668 | nd->root.dentry = dentry; |
1642 | 1669 | nd->root.mnt = mnt; | |
1643 | /* same as do_path_lookup */ | 1670 | /* the first argument of do_path_lookup() is ignored with LOOKUP_ROOT */ |
1644 | nd->last_type = LAST_ROOT; | 1671 | return do_path_lookup(AT_FDCWD, name, flags | LOOKUP_ROOT, nd); |
1645 | nd->flags = flags | LOOKUP_JUMPED; | ||
1646 | nd->depth = 0; | ||
1647 | |||
1648 | nd->path.dentry = dentry; | ||
1649 | nd->path.mnt = mnt; | ||
1650 | path_get(&nd->path); | ||
1651 | nd->root = nd->path; | ||
1652 | path_get(&nd->root); | ||
1653 | nd->inode = nd->path.dentry->d_inode; | ||
1654 | |||
1655 | current->total_link_count = 0; | ||
1656 | |||
1657 | result = link_path_walk(name, nd); | ||
1658 | if (!result) | ||
1659 | result = handle_reval_path(nd); | ||
1660 | if (result == -ESTALE) { | ||
1661 | /* nd->path had been dropped */ | ||
1662 | current->total_link_count = 0; | ||
1663 | nd->path.dentry = dentry; | ||
1664 | nd->path.mnt = mnt; | ||
1665 | nd->inode = dentry->d_inode; | ||
1666 | path_get(&nd->path); | ||
1667 | nd->flags = flags | LOOKUP_JUMPED | LOOKUP_REVAL; | ||
1668 | |||
1669 | result = link_path_walk(name, nd); | ||
1670 | if (!result) | ||
1671 | result = handle_reval_path(nd); | ||
1672 | } | ||
1673 | if (unlikely(!result && !audit_dummy_context() && nd->path.dentry && | ||
1674 | nd->inode)) | ||
1675 | audit_inode(name, nd->path.dentry); | ||
1676 | |||
1677 | path_put(&nd->root); | ||
1678 | nd->root.mnt = NULL; | ||
1679 | |||
1680 | return result; | ||
1681 | } | 1672 | } |
1682 | 1673 | ||
1683 | static struct dentry *__lookup_hash(struct qstr *name, | 1674 | static struct dentry *__lookup_hash(struct qstr *name, |
@@ -2320,7 +2311,7 @@ static struct file *path_openat(int dfd, const char *pathname, | |||
2320 | path_put(&link); | 2311 | path_put(&link); |
2321 | } | 2312 | } |
2322 | out: | 2313 | out: |
2323 | if (nd.root.mnt) | 2314 | if (nd.root.mnt && !(nd.flags & LOOKUP_ROOT)) |
2324 | path_put(&nd.root); | 2315 | path_put(&nd.root); |
2325 | if (base) | 2316 | if (base) |
2326 | fput(base); | 2317 | fput(base); |
diff --git a/include/linux/namei.h b/include/linux/namei.h index 72ffd62ac736..83cd6e5cd7dc 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h | |||
@@ -63,6 +63,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; | |||
63 | #define LOOKUP_RENAME_TARGET 0x0800 | 63 | #define LOOKUP_RENAME_TARGET 0x0800 |
64 | 64 | ||
65 | #define LOOKUP_JUMPED 0x1000 | 65 | #define LOOKUP_JUMPED 0x1000 |
66 | #define LOOKUP_ROOT 0x2000 | ||
66 | 67 | ||
67 | extern int user_path_at(int, const char __user *, unsigned, struct path *); | 68 | extern int user_path_at(int, const char __user *, unsigned, struct path *); |
68 | 69 | ||