diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-13 16:42:14 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-15 02:21:45 -0400 |
commit | bcda76524cd1fa32af748536f27f674a13e56700 (patch) | |
tree | 37dbc7fb50b515f3dff820e14d92e768fb1cda31 /fs/namei.c | |
parent | 1abf0c718f15a56a0a435588d1b104c7a37dc9bd (diff) |
Allow O_PATH for symlinks
At that point we can't do almost nothing with them. They can be opened
with O_PATH, we can manipulate such descriptors with dup(), etc. and
we can see them in /proc/*/{fd,fdinfo}/*.
We can't (and won't be able to) follow /proc/*/fd/* symlinks for those;
there's simply not enough information for pathname resolution to go on
from such point - to resolve a symlink we need to know which directory
does it live in.
We will be able to do useful things with them after the next commit, though -
readlinkat() and fchownat() will be possible to use with dfd being an
O_PATH-opened symlink and empty relative pathname. Combined with
open_by_handle() it'll give us a way to do realink-by-handle and
lchown-by-handle without messing with more redundant syscalls.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 25 |
1 files changed, 19 insertions, 6 deletions
diff --git a/fs/namei.c b/fs/namei.c index e1d9f90d9776..9d4f32700179 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -766,8 +766,14 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p) | |||
766 | error = 0; | 766 | error = 0; |
767 | if (s) | 767 | if (s) |
768 | error = __vfs_follow_link(nd, s); | 768 | error = __vfs_follow_link(nd, s); |
769 | else if (nd->last_type == LAST_BIND) | 769 | else if (nd->last_type == LAST_BIND) { |
770 | nd->flags |= LOOKUP_JUMPED; | 770 | nd->flags |= LOOKUP_JUMPED; |
771 | if (nd->path.dentry->d_inode->i_op->follow_link) { | ||
772 | /* stepped on a _really_ weird one */ | ||
773 | path_put(&nd->path); | ||
774 | error = -ELOOP; | ||
775 | } | ||
776 | } | ||
771 | } | 777 | } |
772 | return error; | 778 | return error; |
773 | } | 779 | } |
@@ -1954,6 +1960,10 @@ static int may_open(struct path *path, int acc_mode, int flag) | |||
1954 | struct inode *inode = dentry->d_inode; | 1960 | struct inode *inode = dentry->d_inode; |
1955 | int error; | 1961 | int error; |
1956 | 1962 | ||
1963 | /* O_PATH? */ | ||
1964 | if (!acc_mode) | ||
1965 | return 0; | ||
1966 | |||
1957 | if (!inode) | 1967 | if (!inode) |
1958 | return -ENOENT; | 1968 | return -ENOENT; |
1959 | 1969 | ||
@@ -2056,7 +2066,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2056 | int open_flag = op->open_flag; | 2066 | int open_flag = op->open_flag; |
2057 | int will_truncate = open_flag & O_TRUNC; | 2067 | int will_truncate = open_flag & O_TRUNC; |
2058 | int want_write = 0; | 2068 | int want_write = 0; |
2059 | int skip_perm = 0; | 2069 | int acc_mode = op->acc_mode; |
2060 | struct file *filp; | 2070 | struct file *filp; |
2061 | struct inode *inode; | 2071 | struct inode *inode; |
2062 | int error; | 2072 | int error; |
@@ -2095,8 +2105,11 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2095 | } | 2105 | } |
2096 | 2106 | ||
2097 | if (!(open_flag & O_CREAT)) { | 2107 | if (!(open_flag & O_CREAT)) { |
2108 | int symlink_ok = 0; | ||
2098 | if (nd->last.name[nd->last.len]) | 2109 | if (nd->last.name[nd->last.len]) |
2099 | nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; | 2110 | nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; |
2111 | if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW)) | ||
2112 | symlink_ok = 1; | ||
2100 | /* we _can_ be in RCU mode here */ | 2113 | /* we _can_ be in RCU mode here */ |
2101 | error = do_lookup(nd, &nd->last, path, &inode); | 2114 | error = do_lookup(nd, &nd->last, path, &inode); |
2102 | if (error) { | 2115 | if (error) { |
@@ -2108,7 +2121,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2108 | terminate_walk(nd); | 2121 | terminate_walk(nd); |
2109 | return ERR_PTR(-ENOENT); | 2122 | return ERR_PTR(-ENOENT); |
2110 | } | 2123 | } |
2111 | if (unlikely(inode->i_op->follow_link)) { | 2124 | if (unlikely(inode->i_op->follow_link && !symlink_ok)) { |
2112 | /* We drop rcu-walk here */ | 2125 | /* We drop rcu-walk here */ |
2113 | if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry)) | 2126 | if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry)) |
2114 | return ERR_PTR(-ECHILD); | 2127 | return ERR_PTR(-ECHILD); |
@@ -2175,7 +2188,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2175 | /* Don't check for write permission, don't truncate */ | 2188 | /* Don't check for write permission, don't truncate */ |
2176 | open_flag &= ~O_TRUNC; | 2189 | open_flag &= ~O_TRUNC; |
2177 | will_truncate = 0; | 2190 | will_truncate = 0; |
2178 | skip_perm = 1; | 2191 | acc_mode = MAY_OPEN; |
2179 | error = security_path_mknod(&nd->path, dentry, mode, 0); | 2192 | error = security_path_mknod(&nd->path, dentry, mode, 0); |
2180 | if (error) | 2193 | if (error) |
2181 | goto exit_mutex_unlock; | 2194 | goto exit_mutex_unlock; |
@@ -2225,7 +2238,7 @@ ok: | |||
2225 | want_write = 1; | 2238 | want_write = 1; |
2226 | } | 2239 | } |
2227 | common: | 2240 | common: |
2228 | error = may_open(&nd->path, skip_perm ? 0 : op->acc_mode, open_flag); | 2241 | error = may_open(&nd->path, acc_mode, open_flag); |
2229 | if (error) | 2242 | if (error) |
2230 | goto exit; | 2243 | goto exit; |
2231 | filp = nameidata_to_filp(nd); | 2244 | filp = nameidata_to_filp(nd); |
@@ -2358,7 +2371,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, | |||
2358 | 2371 | ||
2359 | flags |= LOOKUP_ROOT; | 2372 | flags |= LOOKUP_ROOT; |
2360 | 2373 | ||
2361 | if (dentry->d_inode->i_op->follow_link) | 2374 | if (dentry->d_inode->i_op->follow_link && op->intent & LOOKUP_OPEN) |
2362 | return ERR_PTR(-ELOOP); | 2375 | return ERR_PTR(-ELOOP); |
2363 | 2376 | ||
2364 | file = path_openat(-1, name, &nd, op, flags | LOOKUP_RCU); | 2377 | file = path_openat(-1, name, &nd, op, flags | LOOKUP_RCU); |