summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2016-12-20 10:12:19 +1100
committerStephen Rothwell <sfr@canb.auug.org.au>2016-12-20 10:12:19 +1100
commitae1177209326d8be0e563827fd5bc811a02f9578 (patch)
tree70bd6651efe0fd275020b73a580345317027dd0d /fs
parentae4befe4f605d6d206cbb74f15f3ff62b707f943 (diff)
parentb12826c5188e1a964a64eedd37a537dc202d6bb8 (diff)
Merge remote-tracking branch 'vfs-miklos/next'
Diffstat (limited to 'fs')
-rw-r--r--fs/afs/mntpt.c2
-rw-r--r--fs/bad_inode.c8
-rw-r--r--fs/namei.c65
-rw-r--r--fs/nsfs.c17
-rw-r--r--fs/overlayfs/dir.c3
-rw-r--r--fs/proc/base.c57
-rw-r--r--fs/proc/namespaces.c13
-rw-r--r--fs/read_write.c17
8 files changed, 78 insertions, 104 deletions
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c
index 81dd075356b9..85895b4df2ea 100644
--- a/fs/afs/mntpt.c
+++ b/fs/afs/mntpt.c
@@ -33,7 +33,7 @@ const struct file_operations afs_mntpt_file_operations = {
const struct inode_operations afs_mntpt_inode_operations = {
.lookup = afs_mntpt_lookup,
- .readlink = page_readlink,
+ .readlink = page_get_link,
.getattr = afs_getattr,
};
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
index 5f685c819298..408243786813 100644
--- a/fs/bad_inode.c
+++ b/fs/bad_inode.c
@@ -78,12 +78,6 @@ static int bad_inode_rename2(struct inode *old_dir, struct dentry *old_dentry,
return -EIO;
}
-static int bad_inode_readlink(struct dentry *dentry, char __user *buffer,
- int buflen)
-{
- return -EIO;
-}
-
static int bad_inode_permission(struct inode *inode, int mask)
{
return -EIO;
@@ -161,7 +155,7 @@ static const struct inode_operations bad_inode_ops =
.rmdir = bad_inode_rmdir,
.mknod = bad_inode_mknod,
.rename = bad_inode_rename2,
- .readlink = bad_inode_readlink,
+ .readlink = bad_inode_get_link,
.permission = bad_inode_permission,
.getattr = bad_inode_getattr,
.setattr = bad_inode_setattr,
diff --git a/fs/namei.c b/fs/namei.c
index d9fc7617b9e4..e2318b954276 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1003,6 +1003,9 @@ static int may_linkat(struct path *link)
return -EPERM;
}
+typedef const char * (*get_link_t)(struct dentry *, struct inode *,
+ struct delayed_call *);
+
static __always_inline
const char *get_link(struct nameidata *nd)
{
@@ -1029,9 +1032,8 @@ const char *get_link(struct nameidata *nd)
nd->last_type = LAST_BIND;
res = inode->i_link;
if (!res) {
- const char * (*get)(struct dentry *, struct inode *,
- struct delayed_call *);
- get = inode->i_op->get_link;
+ get_link_t get = inode->i_op->get_link;
+
if (nd->flags & LOOKUP_RCU) {
res = get(NULL, inode, &last->done);
if (res == ERR_PTR(-ECHILD)) {
@@ -4586,7 +4588,7 @@ int vfs_whiteout(struct inode *dir, struct dentry *dentry)
}
EXPORT_SYMBOL(vfs_whiteout);
-int readlink_copy(char __user *buffer, int buflen, const char *link)
+static int readlink_copy(char __user *buffer, int buflen, const char *link)
{
int len = PTR_ERR(link);
if (IS_ERR(link))
@@ -4601,29 +4603,6 @@ out:
return len;
}
-/*
- * A helper for ->readlink(). This should be used *ONLY* for symlinks that
- * have ->get_link() not calling nd_jump_link(). Using (or not using) it
- * for any given inode is up to filesystem.
- */
-static int generic_readlink(struct dentry *dentry, char __user *buffer,
- int buflen)
-{
- DEFINE_DELAYED_CALL(done);
- struct inode *inode = d_inode(dentry);
- const char *link = inode->i_link;
- int res;
-
- if (!link) {
- link = inode->i_op->get_link(dentry, inode, &done);
- if (IS_ERR(link))
- return PTR_ERR(link);
- }
- res = readlink_copy(buffer, buflen, link);
- do_delayed_call(&done);
- return res;
-}
-
/**
* vfs_readlink - copy symlink body into userspace buffer
* @dentry: dentry on which to get symbolic link
@@ -4636,11 +4615,17 @@ static int generic_readlink(struct dentry *dentry, char __user *buffer,
*/
int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
+ DEFINE_DELAYED_CALL(done);
struct inode *inode = d_inode(dentry);
+ const char *link = inode->i_link;
+ get_link_t get;
+ int res;
if (unlikely(!(inode->i_opflags & IOP_DEFAULT_READLINK))) {
- if (unlikely(inode->i_op->readlink))
- return inode->i_op->readlink(dentry, buffer, buflen);
+ if (unlikely(inode->i_op->readlink)) {
+ get = inode->i_op->readlink;
+ goto get;
+ }
if (!d_is_symlink(dentry))
return -EINVAL;
@@ -4649,8 +4634,17 @@ int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
inode->i_opflags |= IOP_DEFAULT_READLINK;
spin_unlock(&inode->i_lock);
}
+ if (!link) {
+ get = inode->i_op->get_link;
+get:
+ link = get(dentry, inode, &done);
+ if (IS_ERR(link))
+ return PTR_ERR(link);
+ }
+ res = readlink_copy(buffer, buflen, link);
+ do_delayed_call(&done);
- return generic_readlink(dentry, buffer, buflen);
+ return res;
}
EXPORT_SYMBOL(vfs_readlink);
@@ -4715,17 +4709,6 @@ void page_put_link(void *arg)
}
EXPORT_SYMBOL(page_put_link);
-int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
-{
- DEFINE_DELAYED_CALL(done);
- int res = readlink_copy(buffer, buflen,
- page_get_link(dentry, d_inode(dentry),
- &done));
- do_delayed_call(&done);
- return res;
-}
-EXPORT_SYMBOL(page_readlink);
-
/*
* The nofs argument instructs pagecache_write_begin to pass AOP_FLAG_NOFS
*/
diff --git a/fs/nsfs.c b/fs/nsfs.c
index 8c9fb29c6673..1d7734958b82 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -5,6 +5,7 @@
#include <linux/magic.h>
#include <linux/ktime.h>
#include <linux/seq_file.h>
+#include <linux/slab.h>
#include <linux/user_namespace.h>
#include <linux/nsfs.h>
@@ -177,14 +178,22 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,
}
}
-int ns_get_name(char *buf, size_t size, struct task_struct *task,
- const struct proc_ns_operations *ns_ops)
+char *ns_get_name(struct task_struct *task,
+ const struct proc_ns_operations *ns_ops)
{
struct ns_common *ns;
- int res = -ENOENT;
+ char *res = ERR_PTR(-ENOENT);
+
ns = ns_ops->get(task);
if (ns) {
- res = snprintf(buf, size, "%s:[%u]", ns_ops->name, ns->inum);
+ /* 10 for unsigned int in decimal + 3 extra chars + term null */
+ size_t size = strlen(ns_ops->name) + 14;
+
+ res = kmalloc(size, GFP_KERNEL);
+ if (!res)
+ res = ERR_PTR(-ENOMEM);
+ else
+ snprintf(res, size, "%s:[%u]", ns_ops->name, ns->inum);
ns_ops->put(ns);
}
return res;
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index 16e06dd89457..1f18ee924d5f 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -381,8 +381,7 @@ out_free:
}
static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
- struct cattr *cattr,
- struct dentry *hardlink)
+ struct cattr *cattr, struct dentry *hardlink)
{
struct dentry *workdir = ovl_workdir(dentry);
struct inode *wdir = workdir->d_inode;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 5ea836362870..027cf2d9d60a 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1614,48 +1614,31 @@ out:
return ERR_PTR(error);
}
-static int do_proc_readlink(struct path *path, char __user *buffer, int buflen)
-{
- char *tmp = (char*)__get_free_page(GFP_TEMPORARY);
- char *pathname;
- int len;
-
- if (!tmp)
- return -ENOMEM;
-
- pathname = d_path(path, tmp, PAGE_SIZE);
- len = PTR_ERR(pathname);
- if (IS_ERR(pathname))
- goto out;
- len = tmp + PAGE_SIZE - 1 - pathname;
-
- if (len > buflen)
- len = buflen;
- if (copy_to_user(buffer, pathname, len))
- len = -EFAULT;
- out:
- free_page((unsigned long)tmp);
- return len;
-}
-
-static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int buflen)
+static const char *proc_pid_readlink(struct dentry *dentry, struct inode *inode,
+ struct delayed_call *done)
{
- int error = -EACCES;
- struct inode *inode = d_inode(dentry);
+ const char *res;
struct path path;
/* Are we allowed to snoop on the tasks file descriptors? */
if (!proc_fd_access_allowed(inode))
- goto out;
-
- error = PROC_I(inode)->op.proc_get_link(dentry, &path);
- if (error)
- goto out;
-
- error = do_proc_readlink(&path, buffer, buflen);
- path_put(&path);
-out:
- return error;
+ return ERR_PTR(-EACCES);
+
+ res = ERR_PTR(PROC_I(inode)->op.proc_get_link(dentry, &path));
+ if (!res) {
+ char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+
+ res = ERR_PTR(-ENOMEM);
+ if (buf) {
+ res = d_path(&path, buf, PAGE_SIZE);
+ if (IS_ERR(res))
+ kfree(buf);
+ else
+ set_delayed_call(done, kfree_link, buf);
+ }
+ path_put(&path);
+ }
+ return res;
}
const struct inode_operations proc_pid_link_inode_operations = {
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index 766f0c637ad1..3a2232d41c6d 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -58,22 +58,21 @@ static const char *proc_ns_get_link(struct dentry *dentry,
return error;
}
-static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen)
+static const char *proc_ns_readlink(struct dentry *dentry, struct inode *inode,
+ struct delayed_call *done)
{
- struct inode *inode = d_inode(dentry);
const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
struct task_struct *task;
- char name[50];
- int res = -EACCES;
+ char *res = ERR_PTR(-EACCES);
task = get_proc_task(inode);
if (!task)
return res;
if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
- res = ns_get_name(name, sizeof(name), task, ns_ops);
- if (res >= 0)
- res = readlink_copy(buffer, buflen, name);
+ res = ns_get_name(task, ns_ops);
+ if (!IS_ERR(res))
+ set_delayed_call(done, kfree_link, res);
}
put_task_struct(task);
return res;
diff --git a/fs/read_write.c b/fs/read_write.c
index da6de12b5c46..8d220b311e3e 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -1515,9 +1515,21 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
struct inode *inode_out = file_inode(file_out);
ssize_t ret;
+ if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
+ return -EISDIR;
+
if (flags != 0)
return -EINVAL;
+ if (!(file_in->f_mode & FMODE_PREAD) ||
+ !(file_out->f_mode & FMODE_PWRITE))
+ return -ESPIPE;
+
+ if (!(file_in->f_mode & FMODE_READ) ||
+ !(file_out->f_mode & FMODE_WRITE) ||
+ (file_out->f_flags & O_APPEND))
+ return -EBADF;
+
ret = rw_verify_area(READ, file_in, &pos_in, len);
if (unlikely(ret))
return ret;
@@ -1526,11 +1538,6 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
if (unlikely(ret))
return ret;
- if (!(file_in->f_mode & FMODE_READ) ||
- !(file_out->f_mode & FMODE_WRITE) ||
- (file_out->f_flags & O_APPEND))
- return -EBADF;
-
/* this could be relaxed once a method supports cross-fs copies */
if (inode_in->i_sb != inode_out->i_sb)
return -EXDEV;