cregit-Linux how code gets into the kernel

Release 4.11 net/sunrpc/rpc_pipe.c

Directory: net/sunrpc
/*
 * net/sunrpc/rpc_pipe.c
 *
 * Userland/kernel interface for rpcauth_gss.
 * Code shamelessly plagiarized from fs/nfsd/nfsctl.c
 * and fs/sysfs/inode.c
 *
 * Copyright (c) 2002, Trond Myklebust <trond.myklebust@fys.uio.no>
 *
 */
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/fsnotify.h>
#include <linux/kernel.h>
#include <linux/rcupdate.h>
#include <linux/utsname.h>

#include <asm/ioctls.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/seq_file.h>

#include <linux/sunrpc/clnt.h>
#include <linux/workqueue.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/sunrpc/cache.h>
#include <linux/nsproxy.h>
#include <linux/notifier.h>

#include "netns.h"
#include "sunrpc.h"


#define RPCDBG_FACILITY RPCDBG_DEBUG


#define NET_NAME(net)	((net == &init_net) ? " (init_net)" : "")


static struct file_system_type rpc_pipe_fs_type;

static const struct rpc_pipe_ops gssd_dummy_pipe_ops;


static struct kmem_cache *rpc_inode_cachep __read_mostly;


#define RPC_UPCALL_TIMEOUT (30*HZ)

static BLOCKING_NOTIFIER_HEAD(rpc_pipefs_notifier_list);


int rpc_pipefs_notifier_register(struct notifier_block *nb) { return blocking_notifier_chain_cond_register(&rpc_pipefs_notifier_list, nb); }

Contributors

PersonTokensPropCommitsCommitProp
Stanislav Kinsbursky19100.00%1100.00%
Total19100.00%1100.00%

EXPORT_SYMBOL_GPL(rpc_pipefs_notifier_register);
void rpc_pipefs_notifier_unregister(struct notifier_block *nb) { blocking_notifier_chain_unregister(&rpc_pipefs_notifier_list, nb); }

Contributors

PersonTokensPropCommitsCommitProp
Stanislav Kinsbursky18100.00%1100.00%
Total18100.00%1100.00%

EXPORT_SYMBOL_GPL(rpc_pipefs_notifier_unregister);
static void rpc_purge_list(wait_queue_head_t *waitq, struct list_head *head, void (*destroy_msg)(struct rpc_pipe_msg *), int err) { struct rpc_pipe_msg *msg; if (list_empty(head)) return; do { msg = list_entry(head->next, struct rpc_pipe_msg, list); list_del_init(&msg->list); msg->errno = err; destroy_msg(msg); } while (!list_empty(head)); if (waitq) wake_up(waitq); }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust9093.75%571.43%
Jeff Layton44.17%114.29%
Stanislav Kinsbursky22.08%114.29%
Total96100.00%7100.00%


static void rpc_timeout_upcall_queue(struct work_struct *work) { LIST_HEAD(free_list); struct rpc_pipe *pipe = container_of(work, struct rpc_pipe, queue_timeout.work); void (*destroy_msg)(struct rpc_pipe_msg *); struct dentry *dentry; spin_lock(&pipe->lock); destroy_msg = pipe->ops->destroy_msg; if (pipe->nreaders == 0) { list_splice_init(&pipe->pipe, &free_list); pipe->pipelen = 0; } dentry = dget(pipe->dentry); spin_unlock(&pipe->lock); rpc_purge_list(dentry ? &RPC_I(d_inode(dentry))->waitq : NULL, &free_list, destroy_msg, -ETIMEDOUT); dput(dentry); }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust8359.29%333.33%
Stanislav Kinsbursky3827.14%333.33%
David Howells1510.71%222.22%
Jeff Layton42.86%111.11%
Total140100.00%9100.00%


ssize_t rpc_pipe_generic_upcall(struct file *filp, struct rpc_pipe_msg *msg, char __user *dst, size_t buflen) { char *data = (char *)msg->data + msg->copied; size_t mlen = min(msg->len - msg->copied, buflen); unsigned long left; left = copy_to_user(dst, data, mlen); if (left == mlen) { msg->errno = -EFAULT; return -EFAULT; } mlen -= left; msg->copied += mlen; msg->errno = 0; return mlen; }

Contributors

PersonTokensPropCommitsCommitProp
Peng Tao108100.00%1100.00%
Total108100.00%1100.00%

EXPORT_SYMBOL_GPL(rpc_pipe_generic_upcall); /** * rpc_queue_upcall - queue an upcall message to userspace * @pipe: upcall pipe on which to queue given message * @msg: message to queue * * Call with an @inode created by rpc_mkpipe() to queue an upcall. * A userspace process may then later read the upcall by performing a * read on an open file for this inode. It is up to the caller to * initialize the fields of @msg (other than @msg->list) appropriately. */
int rpc_queue_upcall(struct rpc_pipe *pipe, struct rpc_pipe_msg *msg) { int res = -EPIPE; struct dentry *dentry; spin_lock(&pipe->lock); if (pipe->nreaders) { list_add_tail(&msg->list, &pipe->pipe); pipe->pipelen += msg->len; res = 0; } else if (pipe->flags & RPC_PIPE_WAIT_FOR_OPEN) { if (list_empty(&pipe->pipe)) queue_delayed_work(rpciod_workqueue, &pipe->queue_timeout, RPC_UPCALL_TIMEOUT); list_add_tail(&msg->list, &pipe->pipe); pipe->pipelen += msg->len; res = 0; } dentry = dget(pipe->dentry); spin_unlock(&pipe->lock); if (dentry) { wake_up(&RPC_I(d_inode(dentry))->waitq); dput(dentry); } return res; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust12372.35%545.45%
Stanislav Kinsbursky4023.53%436.36%
J. Bruce Fields42.35%19.09%
David Howells31.76%19.09%
Total170100.00%11100.00%

EXPORT_SYMBOL_GPL(rpc_queue_upcall);
static inline void rpc_inode_setowner(struct inode *inode, void *private) { RPC_I(inode)->private = private; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust25100.00%2100.00%
Total25100.00%2100.00%


static void rpc_close_pipes(struct inode *inode) { struct rpc_pipe *pipe = RPC_I(inode)->pipe; int need_release; LIST_HEAD(free_list); inode_lock(inode); spin_lock(&pipe->lock); need_release = pipe->nreaders != 0 || pipe->nwriters != 0; pipe->nreaders = 0; list_splice_init(&pipe->in_upcall, &free_list); list_splice_init(&pipe->pipe, &free_list); pipe->pipelen = 0; pipe->dentry = NULL; spin_unlock(&pipe->lock); rpc_purge_list(&RPC_I(inode)->waitq, &free_list, pipe->ops->destroy_msg, -EPIPE); pipe->nwriters = 0; if (need_release && pipe->ops->release_pipe) pipe->ops->release_pipe(inode); cancel_delayed_work_sync(&pipe->queue_timeout); rpc_inode_setowner(inode, NULL); RPC_I(inode)->pipe = NULL; inode_unlock(inode); }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust11965.03%646.15%
Stanislav Kinsbursky4524.59%538.46%
J. Bruce Fields179.29%17.69%
Al Viro21.09%17.69%
Total183100.00%13100.00%


static struct inode * rpc_alloc_inode(struct super_block *sb) { struct rpc_inode *rpci; rpci = kmem_cache_alloc(rpc_inode_cachep, GFP_KERNEL); if (!rpci) return NULL; return &rpci->vfs_inode; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust4097.56%266.67%
Christoph Lameter12.44%133.33%
Total41100.00%3100.00%


static void rpc_i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); kmem_cache_free(rpc_inode_cachep, RPC_I(inode)); }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust1850.00%150.00%
Nicholas Piggin1850.00%150.00%
Total36100.00%2100.00%


static void rpc_destroy_inode(struct inode *inode) { call_rcu(&inode->i_rcu, rpc_i_callback); }

Contributors

PersonTokensPropCommitsCommitProp
Nicholas Piggin2095.24%150.00%
Trond Myklebust14.76%150.00%
Total21100.00%2100.00%


static int rpc_pipe_open(struct inode *inode, struct file *filp) { struct rpc_pipe *pipe; int first_open; int res = -ENXIO; inode_lock(inode); pipe = RPC_I(inode)->pipe; if (pipe == NULL) goto out; first_open = pipe->nreaders == 0 && pipe->nwriters == 0; if (first_open && pipe->ops->open_pipe) { res = pipe->ops->open_pipe(inode); if (res) goto out; } if (filp->f_mode & FMODE_READ) pipe->nreaders++; if (filp->f_mode & FMODE_WRITE) pipe->nwriters++; res = 0; out: inode_unlock(inode); return res; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust6849.64%225.00%
J. Bruce Fields4835.04%112.50%
Stanislav Kinsbursky1813.14%337.50%
Al Viro21.46%112.50%
Nicholas Piggin10.73%112.50%
Total137100.00%8100.00%


static int rpc_pipe_release(struct inode *inode, struct file *filp) { struct rpc_pipe *pipe; struct rpc_pipe_msg *msg; int last_close; inode_lock(inode); pipe = RPC_I(inode)->pipe; if (pipe == NULL) goto out; msg = filp->private_data; if (msg != NULL) { spin_lock(&pipe->lock); msg->errno = -EAGAIN; list_del_init(&msg->list); spin_unlock(&pipe->lock); pipe->ops->destroy_msg(msg); } if (filp->f_mode & FMODE_WRITE) pipe->nwriters --; if (filp->f_mode & FMODE_READ) { pipe->nreaders --; if (pipe->nreaders == 0) { LIST_HEAD(free_list); spin_lock(&pipe->lock); list_splice_init(&pipe->pipe, &free_list); pipe->pipelen = 0; spin_unlock(&pipe->lock); rpc_purge_list(&RPC_I(inode)->waitq, &free_list, pipe->ops->destroy_msg, -EAGAIN); } } last_close = pipe->nwriters == 0 && pipe->nreaders == 0; if (last_close && pipe->ops->release_pipe) pipe->ops->release_pipe(inode); out: inode_unlock(inode); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust19177.02%545.45%
Stanislav Kinsbursky3815.32%436.36%
J. Bruce Fields176.85%19.09%
Al Viro20.81%19.09%
Total248100.00%11100.00%


static ssize_t rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) { struct inode *inode = file_inode(filp); struct rpc_pipe *pipe; struct rpc_pipe_msg *msg; int res = 0; inode_lock(inode); pipe = RPC_I(inode)->pipe; if (pipe == NULL) { res = -EPIPE; goto out_unlock; } msg = filp->private_data; if (msg == NULL) { spin_lock(&pipe->lock); if (!list_empty(&pipe->pipe)) { msg = list_entry(pipe->pipe.next, struct rpc_pipe_msg, list); list_move(&msg->list, &pipe->in_upcall); pipe->pipelen -= msg->len; filp->private_data = msg; msg->copied = 0; } spin_unlock(&pipe->lock); if (msg == NULL) goto out_unlock; } /* NOTE: it is up to the callback to update msg->copied */ res = pipe->ops->upcall(filp, msg, buf, len); if (res < 0 || msg->len == msg->copied) { filp->private_data = NULL; spin_lock(&pipe->lock); list_del_init(&msg->list); spin_unlock(&pipe->lock); pipe->ops->destroy_msg(msg); } out_unlock: inode_unlock(inode); return res; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust23288.55%541.67%
Stanislav Kinsbursky249.16%433.33%
Al Viro51.91%216.67%
Linus Torvalds10.38%18.33%
Total262100.00%12100.00%


static ssize_t rpc_pipe_write(struct file *filp, const char __user *buf, size_t len, loff_t *offset) { struct inode *inode = file_inode(filp); int res; inode_lock(inode); res = -EPIPE; if (RPC_I(inode)->pipe != NULL) res = RPC_I(inode)->pipe->ops->downcall(filp, buf, len); inode_unlock(inode); return res; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust6980.23%116.67%
Stanislav Kinsbursky1112.79%233.33%
Al Viro55.81%233.33%
Linus Torvalds11.16%116.67%
Total86100.00%6100.00%


static unsigned int rpc_pipe_poll(struct file *filp, struct poll_table_struct *wait) { struct inode *inode = file_inode(filp); struct rpc_inode *rpci = RPC_I(inode); unsigned int mask = POLLOUT | POLLWRNORM; poll_wait(filp, &rpci->waitq, wait); inode_lock(inode); if (rpci->pipe == NULL) mask |= POLLERR | POLLHUP; else if (filp->private_data || !list_empty(&rpci->pipe->pipe)) mask |= POLLIN | POLLRDNORM; inode_unlock(inode); return mask; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust6862.96%114.29%
Stanislav Kinsbursky3128.70%342.86%
Al Viro54.63%228.57%
J. Bruce Fields43.70%114.29%
Total108100.00%7100.00%


static long rpc_pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); struct rpc_pipe *pipe; int len; switch (cmd) { case FIONREAD: inode_lock(inode); pipe = RPC_I(inode)->pipe; if (pipe == NULL) { inode_unlock(inode); return -EPIPE; } spin_lock(&pipe->lock); len = pipe->pipelen; if (filp->private_data) { struct rpc_pipe_msg *msg; msg = filp->private_data; len += msg->len - msg->copied; } spin_unlock(&pipe->lock); inode_unlock(inode); return put_user(len, (int __user *)arg); default: return -EINVAL; } }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust9462.25%220.00%
Arnd Bergmann2717.88%110.00%
Stanislav Kinsbursky2315.23%440.00%
Al Viro74.64%330.00%
Total151100.00%10100.00%

static const struct file_operations rpc_pipe_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = rpc_pipe_read, .write = rpc_pipe_write, .poll = rpc_pipe_poll, .unlocked_ioctl = rpc_pipe_ioctl, .open = rpc_pipe_open, .release = rpc_pipe_release, };
static int rpc_show_info(struct seq_file *m, void *v) { struct rpc_clnt *clnt = m->private; rcu_read_lock(); seq_printf(m, "RPC server: %s\n", rcu_dereference(clnt->cl_xprt)->servername); seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_program->name, clnt->cl_prog, clnt->cl_vers); seq_printf(m, "address: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR)); seq_printf(m, "protocol: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PROTO)); seq_printf(m, "port: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PORT)); rcu_read_unlock(); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust8071.43%457.14%
J. Bruce Fields2320.54%228.57%
Chuck Lever98.04%114.29%
Total112100.00%7100.00%


static int rpc_info_open(struct inode *inode, struct file *file) { struct rpc_clnt *clnt = NULL; int ret = single_open(file, rpc_show_info, NULL); if (!ret) { struct seq_file *m = file->private_data; spin_lock(&file->f_path.dentry->d_lock); if (!d_unhashed(file->f_path.dentry)) clnt = RPC_I(inode)->private; if (clnt != NULL && atomic_inc_not_zero(&clnt->cl_count)) { spin_unlock(&file->f_path.dentry->d_lock); m->private = clnt; } else { spin_unlock(&file->f_path.dentry->d_lock); single_release(inode, file); ret = -EINVAL; } } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust148100.00%2100.00%
Total148100.00%2100.00%


static int rpc_info_release(struct inode *inode, struct file *file) { struct seq_file *m = file->private_data; struct rpc_clnt *clnt = (struct rpc_clnt *)m->private; if (clnt) rpc_release_client(clnt); return single_release(inode, file); }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust56100.00%1100.00%
Total56100.00%1100.00%

static const struct file_operations rpc_info_operations = { .owner = THIS_MODULE, .open = rpc_info_open, .read = seq_read, .llseek = seq_lseek, .release = rpc_info_release, }; /* * Description of fs contents. */ struct rpc_filelist { const char *name; const struct file_operations *i_fop; umode_t mode; };
static struct inode * rpc_get_inode(struct super_block *sb, umode_t mode) { struct inode *inode = new_inode(sb); if (!inode) return NULL; inode->i_ino = get_next_ino(); inode->i_mode = mode; inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); switch (mode & S_IFMT) { case S_IFDIR: inode->i_fop = &simple_dir_operations; inode->i_op = &simple_dir_inode_operations; inc_nlink(inode); default: break; } return inode; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust8484.85%342.86%
Christoph Hellwig77.07%114.29%
Deepa Dinamani44.04%114.29%
Dave Hansen33.03%114.29%
Jeff Layton11.01%114.29%
Total99100.00%7100.00%


static int __rpc_create_common(struct inode *dir, struct dentry *dentry, umode_t mode, const struct file_operations *i_fop, void *private) { struct inode *inode; d_drop(dentry); inode = rpc_get_inode(dir->i_sb, mode); if (!inode) goto out_err; inode->i_ino = iunique(dir->i_sb, 100); if (i_fop) inode->i_fop = i_fop; if (private) rpc_inode_setowner(inode, private); d_add(dentry, inode); return 0; out_err: printk(KERN_WARNING "%s: %s failed to allocate inode for dentry %pd\n", __FILE__, __func__, dentry); dput(dentry); return -ENOMEM; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust12499.20%266.67%
Al Viro10.80%133.33%
Total125100.00%3100.00%


static int __rpc_create(struct inode *dir, struct dentry *dentry, umode_t mode, const struct file_operations *i_fop, void *private) { int err; err = __rpc_create_common(dir, dentry, S_IFREG | mode, i_fop, private); if (err) return err; fsnotify_create(dir, dentry); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust66100.00%1100.00%
Total66100.00%1100.00%


static int __rpc_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode, const struct file_operations *i_fop, void *private) { int err; err = __rpc_create_common(dir, dentry, S_IFDIR | mode, i_fop, private); if (err) return err; inc_nlink(dir); fsnotify_mkdir(dir, dentry); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust71100.00%1100.00%
Total71100.00%1100.00%


static void init_pipe(struct rpc_pipe *pipe) { pipe->nreaders = 0; pipe->nwriters = 0; INIT_LIST_HEAD(&pipe->in_upcall); INIT_LIST_HEAD(&pipe->in_downcall); INIT_LIST_HEAD(&pipe->pipe); pipe->pipelen = 0; INIT_DELAYED_WORK(&pipe->queue_timeout, rpc_timeout_upcall_queue); pipe->ops = NULL; spin_lock_init(&pipe->lock); pipe->dentry = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Stanislav Kinsbursky5768.67%266.67%
Trond Myklebust2631.33%133.33%
Total83100.00%3100.00%


void rpc_destroy_pipe_data(struct rpc_pipe *pipe) { kfree(pipe); }

Contributors

PersonTokensPropCommitsCommitProp
Stanislav Kinsbursky1280.00%150.00%
Trond Myklebust320.00%150.00%
Total15100.00%2100.00%

EXPORT_SYMBOL_GPL(rpc_destroy_pipe_data);
struct rpc_pipe *rpc_mkpipe_data(const struct rpc_pipe_ops *ops, int flags) { struct rpc_pipe *pipe; pipe = kzalloc(sizeof(struct rpc_pipe), GFP_KERNEL); if (!pipe) return ERR_PTR(-ENOMEM); init_pipe(pipe); pipe->ops = ops; pipe->flags = flags; return pipe; }

Contributors

PersonTokensPropCommitsCommitProp
Stanislav Kinsbursky4872.73%266.67%
Trond Myklebust1827.27%133.33%
Total66100.00%3100.00%

EXPORT_SYMBOL_GPL(rpc_mkpipe_data);
static int __rpc_mkpipe_dentry(struct inode *dir