Commit bcda7652 authored by Al Viro's avatar Al Viro

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 1abf0c71
...@@ -766,8 +766,14 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p) ...@@ -766,8 +766,14 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p)
error = 0; error = 0;
if (s) if (s)
error = __vfs_follow_link(nd, s); error = __vfs_follow_link(nd, s);
else if (nd->last_type == LAST_BIND) else if (nd->last_type == LAST_BIND) {
nd->flags |= LOOKUP_JUMPED; nd->flags |= LOOKUP_JUMPED;
if (nd->path.dentry->d_inode->i_op->follow_link) {
/* stepped on a _really_ weird one */
path_put(&nd->path);
error = -ELOOP;
}
}
} }
return error; return error;
} }
...@@ -1954,6 +1960,10 @@ static int may_open(struct path *path, int acc_mode, int flag) ...@@ -1954,6 +1960,10 @@ static int may_open(struct path *path, int acc_mode, int flag)
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int error; int error;
/* O_PATH? */
if (!acc_mode)
return 0;
if (!inode) if (!inode)
return -ENOENT; return -ENOENT;
...@@ -2056,7 +2066,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, ...@@ -2056,7 +2066,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
int open_flag = op->open_flag; int open_flag = op->open_flag;
int will_truncate = open_flag & O_TRUNC; int will_truncate = open_flag & O_TRUNC;
int want_write = 0; int want_write = 0;
int skip_perm = 0; int acc_mode = op->acc_mode;
struct file *filp; struct file *filp;
struct inode *inode; struct inode *inode;
int error; int error;
...@@ -2095,8 +2105,11 @@ static struct file *do_last(struct nameidata *nd, struct path *path, ...@@ -2095,8 +2105,11 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
} }
if (!(open_flag & O_CREAT)) { if (!(open_flag & O_CREAT)) {
int symlink_ok = 0;
if (nd->last.name[nd->last.len]) if (nd->last.name[nd->last.len])
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW))
symlink_ok = 1;
/* we _can_ be in RCU mode here */ /* we _can_ be in RCU mode here */
error = do_lookup(nd, &nd->last, path, &inode); error = do_lookup(nd, &nd->last, path, &inode);
if (error) { if (error) {
...@@ -2108,7 +2121,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, ...@@ -2108,7 +2121,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
terminate_walk(nd); terminate_walk(nd);
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
} }
if (unlikely(inode->i_op->follow_link)) { if (unlikely(inode->i_op->follow_link && !symlink_ok)) {
/* We drop rcu-walk here */ /* We drop rcu-walk here */
if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry)) if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry))
return ERR_PTR(-ECHILD); return ERR_PTR(-ECHILD);
...@@ -2175,7 +2188,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, ...@@ -2175,7 +2188,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
/* Don't check for write permission, don't truncate */ /* Don't check for write permission, don't truncate */
open_flag &= ~O_TRUNC; open_flag &= ~O_TRUNC;
will_truncate = 0; will_truncate = 0;
skip_perm = 1; acc_mode = MAY_OPEN;
error = security_path_mknod(&nd->path, dentry, mode, 0); error = security_path_mknod(&nd->path, dentry, mode, 0);
if (error) if (error)
goto exit_mutex_unlock; goto exit_mutex_unlock;
...@@ -2225,7 +2238,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, ...@@ -2225,7 +2238,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
want_write = 1; want_write = 1;
} }
common: common:
error = may_open(&nd->path, skip_perm ? 0 : op->acc_mode, open_flag); error = may_open(&nd->path, acc_mode, open_flag);
if (error) if (error)
goto exit; goto exit;
filp = nameidata_to_filp(nd); filp = nameidata_to_filp(nd);
...@@ -2358,7 +2371,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, ...@@ -2358,7 +2371,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
flags |= LOOKUP_ROOT; flags |= LOOKUP_ROOT;
if (dentry->d_inode->i_op->follow_link) if (dentry->d_inode->i_op->follow_link && op->intent & LOOKUP_OPEN)
return ERR_PTR(-ELOOP); return ERR_PTR(-ELOOP);
file = path_openat(-1, name, &nd, op, flags | LOOKUP_RCU); file = path_openat(-1, name, &nd, op, flags | LOOKUP_RCU);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment