aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/namei.c95
-rw-r--r--include/linux/namei.h1
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)
427err: 429err:
428 spin_unlock(&dentry->d_lock); 430 spin_unlock(&dentry->d_lock);
429err_root: 431err_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);
492err_root: 496err_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
1054failed: 1060failed:
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
1683static struct dentry *__lookup_hash(struct qstr *name, 1674static 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 }
2322out: 2313out:
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
67extern int user_path_at(int, const char __user *, unsigned, struct path *); 68extern int user_path_at(int, const char __user *, unsigned, struct path *);
68 69