diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2009-04-07 11:49:53 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2009-06-11 21:35:59 -0400 |
commit | 2a737871108de9ba8930f7650d549f1383767f8b (patch) | |
tree | d4a8c5d7da4624c4a8973a4c9369af7a915dca05 | |
parent | 9b4a9b14a793bc69b505ed916051f6f32db13bb8 (diff) |
Cache root in nameidata
New field: nd->root. When pathname resolution wants to know the root,
check if nd->root.mnt is non-NULL; use nd->root if it is, otherwise
copy current->fs->root there. After path_walk() is finished, we check
if we'd got a cached value in nd->root and drop it. Before calling
path_walk() we should either set nd->root.mnt to NULL *or* copy (and
pin down) some path to nd->root. In the latter case we won't be
looking at current->fs->root at all.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/namei.c | 53 | ||||
-rw-r--r-- | include/linux/namei.h | 1 |
2 files changed, 35 insertions, 19 deletions
diff --git a/fs/namei.c b/fs/namei.c index 895733efc6b9..88baaf2b9167 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -552,6 +552,17 @@ static __always_inline int link_path_walk(const char *name, struct nameidata *nd | |||
552 | return result; | 552 | return result; |
553 | } | 553 | } |
554 | 554 | ||
555 | static __always_inline void set_root(struct nameidata *nd) | ||
556 | { | ||
557 | if (!nd->root.mnt) { | ||
558 | struct fs_struct *fs = current->fs; | ||
559 | read_lock(&fs->lock); | ||
560 | nd->root = fs->root; | ||
561 | path_get(&nd->root); | ||
562 | read_unlock(&fs->lock); | ||
563 | } | ||
564 | } | ||
565 | |||
555 | static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link) | 566 | static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link) |
556 | { | 567 | { |
557 | int res = 0; | 568 | int res = 0; |
@@ -560,14 +571,10 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l | |||
560 | goto fail; | 571 | goto fail; |
561 | 572 | ||
562 | if (*link == '/') { | 573 | if (*link == '/') { |
563 | struct fs_struct *fs = current->fs; | 574 | set_root(nd); |
564 | |||
565 | path_put(&nd->path); | 575 | path_put(&nd->path); |
566 | 576 | nd->path = nd->root; | |
567 | read_lock(&fs->lock); | 577 | path_get(&nd->root); |
568 | nd->path = fs->root; | ||
569 | path_get(&fs->root); | ||
570 | read_unlock(&fs->lock); | ||
571 | } | 578 | } |
572 | 579 | ||
573 | res = link_path_walk(link, nd); | 580 | res = link_path_walk(link, nd); |
@@ -741,19 +748,16 @@ int follow_down(struct vfsmount **mnt, struct dentry **dentry) | |||
741 | 748 | ||
742 | static __always_inline void follow_dotdot(struct nameidata *nd) | 749 | static __always_inline void follow_dotdot(struct nameidata *nd) |
743 | { | 750 | { |
744 | struct fs_struct *fs = current->fs; | 751 | set_root(nd); |
745 | 752 | ||
746 | while(1) { | 753 | while(1) { |
747 | struct vfsmount *parent; | 754 | struct vfsmount *parent; |
748 | struct dentry *old = nd->path.dentry; | 755 | struct dentry *old = nd->path.dentry; |
749 | 756 | ||
750 | read_lock(&fs->lock); | 757 | if (nd->path.dentry == nd->root.dentry && |
751 | if (nd->path.dentry == fs->root.dentry && | 758 | nd->path.mnt == nd->root.mnt) { |
752 | nd->path.mnt == fs->root.mnt) { | ||
753 | read_unlock(&fs->lock); | ||
754 | break; | 759 | break; |
755 | } | 760 | } |
756 | read_unlock(&fs->lock); | ||
757 | spin_lock(&dcache_lock); | 761 | spin_lock(&dcache_lock); |
758 | if (nd->path.dentry != nd->path.mnt->mnt_root) { | 762 | if (nd->path.dentry != nd->path.mnt->mnt_root) { |
759 | nd->path.dentry = dget(nd->path.dentry->d_parent); | 763 | nd->path.dentry = dget(nd->path.dentry->d_parent); |
@@ -1022,18 +1026,18 @@ static int path_init(int dfd, const char *name, unsigned int flags, struct namei | |||
1022 | int retval = 0; | 1026 | int retval = 0; |
1023 | int fput_needed; | 1027 | int fput_needed; |
1024 | struct file *file; | 1028 | struct file *file; |
1025 | struct fs_struct *fs = current->fs; | ||
1026 | 1029 | ||
1027 | nd->last_type = LAST_ROOT; /* if there are only slashes... */ | 1030 | nd->last_type = LAST_ROOT; /* if there are only slashes... */ |
1028 | nd->flags = flags; | 1031 | nd->flags = flags; |
1029 | nd->depth = 0; | 1032 | nd->depth = 0; |
1033 | nd->root.mnt = NULL; | ||
1030 | 1034 | ||
1031 | if (*name=='/') { | 1035 | if (*name=='/') { |
1032 | read_lock(&fs->lock); | 1036 | set_root(nd); |
1033 | nd->path = fs->root; | 1037 | nd->path = nd->root; |
1034 | path_get(&fs->root); | 1038 | path_get(&nd->root); |
1035 | read_unlock(&fs->lock); | ||
1036 | } else if (dfd == AT_FDCWD) { | 1039 | } else if (dfd == AT_FDCWD) { |
1040 | struct fs_struct *fs = current->fs; | ||
1037 | read_lock(&fs->lock); | 1041 | read_lock(&fs->lock); |
1038 | nd->path = fs->pwd; | 1042 | nd->path = fs->pwd; |
1039 | path_get(&fs->pwd); | 1043 | path_get(&fs->pwd); |
@@ -1079,6 +1083,10 @@ static int do_path_lookup(int dfd, const char *name, | |||
1079 | if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && | 1083 | if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && |
1080 | nd->path.dentry->d_inode)) | 1084 | nd->path.dentry->d_inode)) |
1081 | audit_inode(name, nd->path.dentry); | 1085 | audit_inode(name, nd->path.dentry); |
1086 | if (nd->root.mnt) { | ||
1087 | path_put(&nd->root); | ||
1088 | nd->root.mnt = NULL; | ||
1089 | } | ||
1082 | return retval; | 1090 | return retval; |
1083 | } | 1091 | } |
1084 | 1092 | ||
@@ -1115,6 +1123,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, | |||
1115 | nd->last_type = LAST_ROOT; | 1123 | nd->last_type = LAST_ROOT; |
1116 | nd->flags = flags; | 1124 | nd->flags = flags; |
1117 | nd->depth = 0; | 1125 | nd->depth = 0; |
1126 | nd->root.mnt = NULL; | ||
1118 | 1127 | ||
1119 | nd->path.dentry = dentry; | 1128 | nd->path.dentry = dentry; |
1120 | nd->path.mnt = mnt; | 1129 | nd->path.mnt = mnt; |
@@ -1125,8 +1134,12 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, | |||
1125 | nd->path.dentry->d_inode)) | 1134 | nd->path.dentry->d_inode)) |
1126 | audit_inode(name, nd->path.dentry); | 1135 | audit_inode(name, nd->path.dentry); |
1127 | 1136 | ||
1128 | return retval; | 1137 | if (nd->root.mnt) { |
1138 | path_put(&nd->root); | ||
1139 | nd->root.mnt = NULL; | ||
1140 | } | ||
1129 | 1141 | ||
1142 | return retval; | ||
1130 | } | 1143 | } |
1131 | 1144 | ||
1132 | /** | 1145 | /** |
@@ -1817,6 +1830,8 @@ exit: | |||
1817 | if (!IS_ERR(nd.intent.open.file)) | 1830 | if (!IS_ERR(nd.intent.open.file)) |
1818 | release_open_intent(&nd); | 1831 | release_open_intent(&nd); |
1819 | exit_parent: | 1832 | exit_parent: |
1833 | if (nd.root.mnt) | ||
1834 | path_put(&nd.root); | ||
1820 | path_put(&nd.path); | 1835 | path_put(&nd.path); |
1821 | return ERR_PTR(error); | 1836 | return ERR_PTR(error); |
1822 | 1837 | ||
diff --git a/include/linux/namei.h b/include/linux/namei.h index 518098fe63af..325dd3ad39a0 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h | |||
@@ -18,6 +18,7 @@ enum { MAX_NESTED_LINKS = 8 }; | |||
18 | struct nameidata { | 18 | struct nameidata { |
19 | struct path path; | 19 | struct path path; |
20 | struct qstr last; | 20 | struct qstr last; |
21 | struct path root; | ||
21 | unsigned int flags; | 22 | unsigned int flags; |
22 | int last_type; | 23 | int last_type; |
23 | unsigned depth; | 24 | unsigned depth; |