diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2016-12-20 10:12:19 +1100 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2016-12-20 10:12:19 +1100 |
commit | ae1177209326d8be0e563827fd5bc811a02f9578 (patch) | |
tree | 70bd6651efe0fd275020b73a580345317027dd0d /fs | |
parent | ae4befe4f605d6d206cbb74f15f3ff62b707f943 (diff) | |
parent | b12826c5188e1a964a64eedd37a537dc202d6bb8 (diff) |
Merge remote-tracking branch 'vfs-miklos/next'
Diffstat (limited to 'fs')
-rw-r--r-- | fs/afs/mntpt.c | 2 | ||||
-rw-r--r-- | fs/bad_inode.c | 8 | ||||
-rw-r--r-- | fs/namei.c | 65 | ||||
-rw-r--r-- | fs/nsfs.c | 17 | ||||
-rw-r--r-- | fs/overlayfs/dir.c | 3 | ||||
-rw-r--r-- | fs/proc/base.c | 57 | ||||
-rw-r--r-- | fs/proc/namespaces.c | 13 | ||||
-rw-r--r-- | fs/read_write.c | 17 |
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; |