mirror of
https://github.com/adulau/aha.git
synced 2024-12-29 12:16:20 +00:00
454e2398be
Extend the get_sb() filesystem operation to take an extra argument that permits the VFS to pass in the target vfsmount that defines the mountpoint. The filesystem is then required to manually set the superblock and root dentry pointers. For most filesystems, this should be done with simple_set_mnt() which will set the superblock pointer and then set the root dentry to the superblock's s_root (as per the old default behaviour). The get_sb() op now returns an integer as there's now no need to return the superblock pointer. This patch permits a superblock to be implicitly shared amongst several mount points, such as can be done with NFS to avoid potential inode aliasing. In such a case, simple_set_mnt() would not be called, and instead the mnt_root and mnt_sb would be set directly. The patch also makes the following changes: (*) the get_sb_*() convenience functions in the core kernel now take a vfsmount pointer argument and return an integer, so most filesystems have to change very little. (*) If one of the convenience function is not used, then get_sb() should normally call simple_set_mnt() to instantiate the vfsmount. This will always return 0, and so can be tail-called from get_sb(). (*) generic_shutdown_super() now calls shrink_dcache_sb() to clean up the dcache upon superblock destruction rather than shrink_dcache_anon(). This is required because the superblock may now have multiple trees that aren't actually bound to s_root, but that still need to be cleaned up. The currently called functions assume that the whole tree is rooted at s_root, and that anonymous dentries are not the roots of trees which results in dentries being left unculled. However, with the way NFS superblock sharing are currently set to be implemented, these assumptions are violated: the root of the filesystem is simply a dummy dentry and inode (the real inode for '/' may well be inaccessible), and all the vfsmounts are rooted on anonymous[*] dentries with child trees. [*] Anonymous until discovered from another tree. (*) The documentation has been adjusted, including the additional bit of changing ext2_* into foo_* in the documentation. [akpm@osdl.org: convert ipath_fs, do other stuff] Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: Al Viro <viro@zeniv.linux.org.uk> Cc: Nathan Scott <nathans@sgi.com> Cc: Roland Dreier <rolandd@cisco.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
1016 lines
21 KiB
C
1016 lines
21 KiB
C
/*
|
|
* linux/fs/pipe.c
|
|
*
|
|
* Copyright (C) 1991, 1992, 1999 Linus Torvalds
|
|
*/
|
|
|
|
#include <linux/mm.h>
|
|
#include <linux/file.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/pipe_fs_i.h>
|
|
#include <linux/uio.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/pagemap.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
#include <asm/ioctls.h>
|
|
|
|
/*
|
|
* We use a start+len construction, which provides full use of the
|
|
* allocated memory.
|
|
* -- Florian Coosmann (FGC)
|
|
*
|
|
* Reads with count = 0 should always return 0.
|
|
* -- Julian Bradfield 1999-06-07.
|
|
*
|
|
* FIFOs and Pipes now generate SIGIO for both readers and writers.
|
|
* -- Jeremy Elson <jelson@circlemud.org> 2001-08-16
|
|
*
|
|
* pipe_read & write cleanup
|
|
* -- Manfred Spraul <manfred@colorfullife.com> 2002-05-09
|
|
*/
|
|
|
|
/* Drop the inode semaphore and wait for a pipe event, atomically */
|
|
void pipe_wait(struct pipe_inode_info *pipe)
|
|
{
|
|
DEFINE_WAIT(wait);
|
|
|
|
/*
|
|
* Pipes are system-local resources, so sleeping on them
|
|
* is considered a noninteractive wait:
|
|
*/
|
|
prepare_to_wait(&pipe->wait, &wait,
|
|
TASK_INTERRUPTIBLE | TASK_NONINTERACTIVE);
|
|
if (pipe->inode)
|
|
mutex_unlock(&pipe->inode->i_mutex);
|
|
schedule();
|
|
finish_wait(&pipe->wait, &wait);
|
|
if (pipe->inode)
|
|
mutex_lock(&pipe->inode->i_mutex);
|
|
}
|
|
|
|
static int
|
|
pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len,
|
|
int atomic)
|
|
{
|
|
unsigned long copy;
|
|
|
|
while (len > 0) {
|
|
while (!iov->iov_len)
|
|
iov++;
|
|
copy = min_t(unsigned long, len, iov->iov_len);
|
|
|
|
if (atomic) {
|
|
if (__copy_from_user_inatomic(to, iov->iov_base, copy))
|
|
return -EFAULT;
|
|
} else {
|
|
if (copy_from_user(to, iov->iov_base, copy))
|
|
return -EFAULT;
|
|
}
|
|
to += copy;
|
|
len -= copy;
|
|
iov->iov_base += copy;
|
|
iov->iov_len -= copy;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len,
|
|
int atomic)
|
|
{
|
|
unsigned long copy;
|
|
|
|
while (len > 0) {
|
|
while (!iov->iov_len)
|
|
iov++;
|
|
copy = min_t(unsigned long, len, iov->iov_len);
|
|
|
|
if (atomic) {
|
|
if (__copy_to_user_inatomic(iov->iov_base, from, copy))
|
|
return -EFAULT;
|
|
} else {
|
|
if (copy_to_user(iov->iov_base, from, copy))
|
|
return -EFAULT;
|
|
}
|
|
from += copy;
|
|
len -= copy;
|
|
iov->iov_base += copy;
|
|
iov->iov_len -= copy;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Attempt to pre-fault in the user memory, so we can use atomic copies.
|
|
* Returns the number of bytes not faulted in.
|
|
*/
|
|
static int iov_fault_in_pages_write(struct iovec *iov, unsigned long len)
|
|
{
|
|
while (!iov->iov_len)
|
|
iov++;
|
|
|
|
while (len > 0) {
|
|
unsigned long this_len;
|
|
|
|
this_len = min_t(unsigned long, len, iov->iov_len);
|
|
if (fault_in_pages_writeable(iov->iov_base, this_len))
|
|
break;
|
|
|
|
len -= this_len;
|
|
iov++;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/*
|
|
* Pre-fault in the user memory, so we can use atomic copies.
|
|
*/
|
|
static void iov_fault_in_pages_read(struct iovec *iov, unsigned long len)
|
|
{
|
|
while (!iov->iov_len)
|
|
iov++;
|
|
|
|
while (len > 0) {
|
|
unsigned long this_len;
|
|
|
|
this_len = min_t(unsigned long, len, iov->iov_len);
|
|
fault_in_pages_readable(iov->iov_base, this_len);
|
|
len -= this_len;
|
|
iov++;
|
|
}
|
|
}
|
|
|
|
static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
|
|
struct pipe_buffer *buf)
|
|
{
|
|
struct page *page = buf->page;
|
|
|
|
/*
|
|
* If nobody else uses this page, and we don't already have a
|
|
* temporary page, let's keep track of it as a one-deep
|
|
* allocation cache. (Otherwise just release our reference to it)
|
|
*/
|
|
if (page_count(page) == 1 && !pipe->tmp_page)
|
|
pipe->tmp_page = page;
|
|
else
|
|
page_cache_release(page);
|
|
}
|
|
|
|
void *generic_pipe_buf_map(struct pipe_inode_info *pipe,
|
|
struct pipe_buffer *buf, int atomic)
|
|
{
|
|
if (atomic) {
|
|
buf->flags |= PIPE_BUF_FLAG_ATOMIC;
|
|
return kmap_atomic(buf->page, KM_USER0);
|
|
}
|
|
|
|
return kmap(buf->page);
|
|
}
|
|
|
|
void generic_pipe_buf_unmap(struct pipe_inode_info *pipe,
|
|
struct pipe_buffer *buf, void *map_data)
|
|
{
|
|
if (buf->flags & PIPE_BUF_FLAG_ATOMIC) {
|
|
buf->flags &= ~PIPE_BUF_FLAG_ATOMIC;
|
|
kunmap_atomic(map_data, KM_USER0);
|
|
} else
|
|
kunmap(buf->page);
|
|
}
|
|
|
|
int generic_pipe_buf_steal(struct pipe_inode_info *pipe,
|
|
struct pipe_buffer *buf)
|
|
{
|
|
struct page *page = buf->page;
|
|
|
|
if (page_count(page) == 1) {
|
|
lock_page(page);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void generic_pipe_buf_get(struct pipe_inode_info *info, struct pipe_buffer *buf)
|
|
{
|
|
page_cache_get(buf->page);
|
|
}
|
|
|
|
int generic_pipe_buf_pin(struct pipe_inode_info *info, struct pipe_buffer *buf)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static struct pipe_buf_operations anon_pipe_buf_ops = {
|
|
.can_merge = 1,
|
|
.map = generic_pipe_buf_map,
|
|
.unmap = generic_pipe_buf_unmap,
|
|
.pin = generic_pipe_buf_pin,
|
|
.release = anon_pipe_buf_release,
|
|
.steal = generic_pipe_buf_steal,
|
|
.get = generic_pipe_buf_get,
|
|
};
|
|
|
|
static ssize_t
|
|
pipe_readv(struct file *filp, const struct iovec *_iov,
|
|
unsigned long nr_segs, loff_t *ppos)
|
|
{
|
|
struct inode *inode = filp->f_dentry->d_inode;
|
|
struct pipe_inode_info *pipe;
|
|
int do_wakeup;
|
|
ssize_t ret;
|
|
struct iovec *iov = (struct iovec *)_iov;
|
|
size_t total_len;
|
|
|
|
total_len = iov_length(iov, nr_segs);
|
|
/* Null read succeeds. */
|
|
if (unlikely(total_len == 0))
|
|
return 0;
|
|
|
|
do_wakeup = 0;
|
|
ret = 0;
|
|
mutex_lock(&inode->i_mutex);
|
|
pipe = inode->i_pipe;
|
|
for (;;) {
|
|
int bufs = pipe->nrbufs;
|
|
if (bufs) {
|
|
int curbuf = pipe->curbuf;
|
|
struct pipe_buffer *buf = pipe->bufs + curbuf;
|
|
struct pipe_buf_operations *ops = buf->ops;
|
|
void *addr;
|
|
size_t chars = buf->len;
|
|
int error, atomic;
|
|
|
|
if (chars > total_len)
|
|
chars = total_len;
|
|
|
|
error = ops->pin(pipe, buf);
|
|
if (error) {
|
|
if (!ret)
|
|
error = ret;
|
|
break;
|
|
}
|
|
|
|
atomic = !iov_fault_in_pages_write(iov, chars);
|
|
redo:
|
|
addr = ops->map(pipe, buf, atomic);
|
|
error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars, atomic);
|
|
ops->unmap(pipe, buf, addr);
|
|
if (unlikely(error)) {
|
|
/*
|
|
* Just retry with the slow path if we failed.
|
|
*/
|
|
if (atomic) {
|
|
atomic = 0;
|
|
goto redo;
|
|
}
|
|
if (!ret)
|
|
ret = error;
|
|
break;
|
|
}
|
|
ret += chars;
|
|
buf->offset += chars;
|
|
buf->len -= chars;
|
|
if (!buf->len) {
|
|
buf->ops = NULL;
|
|
ops->release(pipe, buf);
|
|
curbuf = (curbuf + 1) & (PIPE_BUFFERS-1);
|
|
pipe->curbuf = curbuf;
|
|
pipe->nrbufs = --bufs;
|
|
do_wakeup = 1;
|
|
}
|
|
total_len -= chars;
|
|
if (!total_len)
|
|
break; /* common path: read succeeded */
|
|
}
|
|
if (bufs) /* More to do? */
|
|
continue;
|
|
if (!pipe->writers)
|
|
break;
|
|
if (!pipe->waiting_writers) {
|
|
/* syscall merging: Usually we must not sleep
|
|
* if O_NONBLOCK is set, or if we got some data.
|
|
* But if a writer sleeps in kernel space, then
|
|
* we can wait for that data without violating POSIX.
|
|
*/
|
|
if (ret)
|
|
break;
|
|
if (filp->f_flags & O_NONBLOCK) {
|
|
ret = -EAGAIN;
|
|
break;
|
|
}
|
|
}
|
|
if (signal_pending(current)) {
|
|
if (!ret)
|
|
ret = -ERESTARTSYS;
|
|
break;
|
|
}
|
|
if (do_wakeup) {
|
|
wake_up_interruptible_sync(&pipe->wait);
|
|
kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
|
|
}
|
|
pipe_wait(pipe);
|
|
}
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
/* Signal writers asynchronously that there is more room. */
|
|
if (do_wakeup) {
|
|
wake_up_interruptible(&pipe->wait);
|
|
kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
|
|
}
|
|
if (ret > 0)
|
|
file_accessed(filp);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t
|
|
pipe_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
|
|
{
|
|
struct iovec iov = { .iov_base = buf, .iov_len = count };
|
|
|
|
return pipe_readv(filp, &iov, 1, ppos);
|
|
}
|
|
|
|
static ssize_t
|
|
pipe_writev(struct file *filp, const struct iovec *_iov,
|
|
unsigned long nr_segs, loff_t *ppos)
|
|
{
|
|
struct inode *inode = filp->f_dentry->d_inode;
|
|
struct pipe_inode_info *pipe;
|
|
ssize_t ret;
|
|
int do_wakeup;
|
|
struct iovec *iov = (struct iovec *)_iov;
|
|
size_t total_len;
|
|
ssize_t chars;
|
|
|
|
total_len = iov_length(iov, nr_segs);
|
|
/* Null write succeeds. */
|
|
if (unlikely(total_len == 0))
|
|
return 0;
|
|
|
|
do_wakeup = 0;
|
|
ret = 0;
|
|
mutex_lock(&inode->i_mutex);
|
|
pipe = inode->i_pipe;
|
|
|
|
if (!pipe->readers) {
|
|
send_sig(SIGPIPE, current, 0);
|
|
ret = -EPIPE;
|
|
goto out;
|
|
}
|
|
|
|
/* We try to merge small writes */
|
|
chars = total_len & (PAGE_SIZE-1); /* size of the last buffer */
|
|
if (pipe->nrbufs && chars != 0) {
|
|
int lastbuf = (pipe->curbuf + pipe->nrbufs - 1) &
|
|
(PIPE_BUFFERS-1);
|
|
struct pipe_buffer *buf = pipe->bufs + lastbuf;
|
|
struct pipe_buf_operations *ops = buf->ops;
|
|
int offset = buf->offset + buf->len;
|
|
|
|
if (ops->can_merge && offset + chars <= PAGE_SIZE) {
|
|
int error, atomic = 1;
|
|
void *addr;
|
|
|
|
error = ops->pin(pipe, buf);
|
|
if (error)
|
|
goto out;
|
|
|
|
iov_fault_in_pages_read(iov, chars);
|
|
redo1:
|
|
addr = ops->map(pipe, buf, atomic);
|
|
error = pipe_iov_copy_from_user(offset + addr, iov,
|
|
chars, atomic);
|
|
ops->unmap(pipe, buf, addr);
|
|
ret = error;
|
|
do_wakeup = 1;
|
|
if (error) {
|
|
if (atomic) {
|
|
atomic = 0;
|
|
goto redo1;
|
|
}
|
|
goto out;
|
|
}
|
|
buf->len += chars;
|
|
total_len -= chars;
|
|
ret = chars;
|
|
if (!total_len)
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
for (;;) {
|
|
int bufs;
|
|
|
|
if (!pipe->readers) {
|
|
send_sig(SIGPIPE, current, 0);
|
|
if (!ret)
|
|
ret = -EPIPE;
|
|
break;
|
|
}
|
|
bufs = pipe->nrbufs;
|
|
if (bufs < PIPE_BUFFERS) {
|
|
int newbuf = (pipe->curbuf + bufs) & (PIPE_BUFFERS-1);
|
|
struct pipe_buffer *buf = pipe->bufs + newbuf;
|
|
struct page *page = pipe->tmp_page;
|
|
char *src;
|
|
int error, atomic = 1;
|
|
|
|
if (!page) {
|
|
page = alloc_page(GFP_HIGHUSER);
|
|
if (unlikely(!page)) {
|
|
ret = ret ? : -ENOMEM;
|
|
break;
|
|
}
|
|
pipe->tmp_page = page;
|
|
}
|
|
/* Always wake up, even if the copy fails. Otherwise
|
|
* we lock up (O_NONBLOCK-)readers that sleep due to
|
|
* syscall merging.
|
|
* FIXME! Is this really true?
|
|
*/
|
|
do_wakeup = 1;
|
|
chars = PAGE_SIZE;
|
|
if (chars > total_len)
|
|
chars = total_len;
|
|
|
|
iov_fault_in_pages_read(iov, chars);
|
|
redo2:
|
|
if (atomic)
|
|
src = kmap_atomic(page, KM_USER0);
|
|
else
|
|
src = kmap(page);
|
|
|
|
error = pipe_iov_copy_from_user(src, iov, chars,
|
|
atomic);
|
|
if (atomic)
|
|
kunmap_atomic(src, KM_USER0);
|
|
else
|
|
kunmap(page);
|
|
|
|
if (unlikely(error)) {
|
|
if (atomic) {
|
|
atomic = 0;
|
|
goto redo2;
|
|
}
|
|
if (!ret)
|
|
ret = error;
|
|
break;
|
|
}
|
|
ret += chars;
|
|
|
|
/* Insert it into the buffer array */
|
|
buf->page = page;
|
|
buf->ops = &anon_pipe_buf_ops;
|
|
buf->offset = 0;
|
|
buf->len = chars;
|
|
pipe->nrbufs = ++bufs;
|
|
pipe->tmp_page = NULL;
|
|
|
|
total_len -= chars;
|
|
if (!total_len)
|
|
break;
|
|
}
|
|
if (bufs < PIPE_BUFFERS)
|
|
continue;
|
|
if (filp->f_flags & O_NONBLOCK) {
|
|
if (!ret)
|
|
ret = -EAGAIN;
|
|
break;
|
|
}
|
|
if (signal_pending(current)) {
|
|
if (!ret)
|
|
ret = -ERESTARTSYS;
|
|
break;
|
|
}
|
|
if (do_wakeup) {
|
|
wake_up_interruptible_sync(&pipe->wait);
|
|
kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
|
|
do_wakeup = 0;
|
|
}
|
|
pipe->waiting_writers++;
|
|
pipe_wait(pipe);
|
|
pipe->waiting_writers--;
|
|
}
|
|
out:
|
|
mutex_unlock(&inode->i_mutex);
|
|
if (do_wakeup) {
|
|
wake_up_interruptible(&pipe->wait);
|
|
kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
|
|
}
|
|
if (ret > 0)
|
|
file_update_time(filp);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t
|
|
pipe_write(struct file *filp, const char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count };
|
|
|
|
return pipe_writev(filp, &iov, 1, ppos);
|
|
}
|
|
|
|
static ssize_t
|
|
bad_pipe_r(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
|
|
{
|
|
return -EBADF;
|
|
}
|
|
|
|
static ssize_t
|
|
bad_pipe_w(struct file *filp, const char __user *buf, size_t count,
|
|
loff_t *ppos)
|
|
{
|
|
return -EBADF;
|
|
}
|
|
|
|
static int
|
|
pipe_ioctl(struct inode *pino, struct file *filp,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct inode *inode = filp->f_dentry->d_inode;
|
|
struct pipe_inode_info *pipe;
|
|
int count, buf, nrbufs;
|
|
|
|
switch (cmd) {
|
|
case FIONREAD:
|
|
mutex_lock(&inode->i_mutex);
|
|
pipe = inode->i_pipe;
|
|
count = 0;
|
|
buf = pipe->curbuf;
|
|
nrbufs = pipe->nrbufs;
|
|
while (--nrbufs >= 0) {
|
|
count += pipe->bufs[buf].len;
|
|
buf = (buf+1) & (PIPE_BUFFERS-1);
|
|
}
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
return put_user(count, (int __user *)arg);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* No kernel lock held - fine */
|
|
static unsigned int
|
|
pipe_poll(struct file *filp, poll_table *wait)
|
|
{
|
|
unsigned int mask;
|
|
struct inode *inode = filp->f_dentry->d_inode;
|
|
struct pipe_inode_info *pipe = inode->i_pipe;
|
|
int nrbufs;
|
|
|
|
poll_wait(filp, &pipe->wait, wait);
|
|
|
|
/* Reading only -- no need for acquiring the semaphore. */
|
|
nrbufs = pipe->nrbufs;
|
|
mask = 0;
|
|
if (filp->f_mode & FMODE_READ) {
|
|
mask = (nrbufs > 0) ? POLLIN | POLLRDNORM : 0;
|
|
if (!pipe->writers && filp->f_version != pipe->w_counter)
|
|
mask |= POLLHUP;
|
|
}
|
|
|
|
if (filp->f_mode & FMODE_WRITE) {
|
|
mask |= (nrbufs < PIPE_BUFFERS) ? POLLOUT | POLLWRNORM : 0;
|
|
/*
|
|
* Most Unices do not set POLLERR for FIFOs but on Linux they
|
|
* behave exactly like pipes for poll().
|
|
*/
|
|
if (!pipe->readers)
|
|
mask |= POLLERR;
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
static int
|
|
pipe_release(struct inode *inode, int decr, int decw)
|
|
{
|
|
struct pipe_inode_info *pipe;
|
|
|
|
mutex_lock(&inode->i_mutex);
|
|
pipe = inode->i_pipe;
|
|
pipe->readers -= decr;
|
|
pipe->writers -= decw;
|
|
|
|
if (!pipe->readers && !pipe->writers) {
|
|
free_pipe_info(inode);
|
|
} else {
|
|
wake_up_interruptible(&pipe->wait);
|
|
kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
|
|
kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
|
|
}
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pipe_read_fasync(int fd, struct file *filp, int on)
|
|
{
|
|
struct inode *inode = filp->f_dentry->d_inode;
|
|
int retval;
|
|
|
|
mutex_lock(&inode->i_mutex);
|
|
retval = fasync_helper(fd, filp, on, &inode->i_pipe->fasync_readers);
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
pipe_write_fasync(int fd, struct file *filp, int on)
|
|
{
|
|
struct inode *inode = filp->f_dentry->d_inode;
|
|
int retval;
|
|
|
|
mutex_lock(&inode->i_mutex);
|
|
retval = fasync_helper(fd, filp, on, &inode->i_pipe->fasync_writers);
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
pipe_rdwr_fasync(int fd, struct file *filp, int on)
|
|
{
|
|
struct inode *inode = filp->f_dentry->d_inode;
|
|
struct pipe_inode_info *pipe = inode->i_pipe;
|
|
int retval;
|
|
|
|
mutex_lock(&inode->i_mutex);
|
|
|
|
retval = fasync_helper(fd, filp, on, &pipe->fasync_readers);
|
|
|
|
if (retval >= 0)
|
|
retval = fasync_helper(fd, filp, on, &pipe->fasync_writers);
|
|
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
pipe_read_release(struct inode *inode, struct file *filp)
|
|
{
|
|
pipe_read_fasync(-1, filp, 0);
|
|
return pipe_release(inode, 1, 0);
|
|
}
|
|
|
|
static int
|
|
pipe_write_release(struct inode *inode, struct file *filp)
|
|
{
|
|
pipe_write_fasync(-1, filp, 0);
|
|
return pipe_release(inode, 0, 1);
|
|
}
|
|
|
|
static int
|
|
pipe_rdwr_release(struct inode *inode, struct file *filp)
|
|
{
|
|
int decr, decw;
|
|
|
|
pipe_rdwr_fasync(-1, filp, 0);
|
|
decr = (filp->f_mode & FMODE_READ) != 0;
|
|
decw = (filp->f_mode & FMODE_WRITE) != 0;
|
|
return pipe_release(inode, decr, decw);
|
|
}
|
|
|
|
static int
|
|
pipe_read_open(struct inode *inode, struct file *filp)
|
|
{
|
|
/* We could have perhaps used atomic_t, but this and friends
|
|
below are the only places. So it doesn't seem worthwhile. */
|
|
mutex_lock(&inode->i_mutex);
|
|
inode->i_pipe->readers++;
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pipe_write_open(struct inode *inode, struct file *filp)
|
|
{
|
|
mutex_lock(&inode->i_mutex);
|
|
inode->i_pipe->writers++;
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pipe_rdwr_open(struct inode *inode, struct file *filp)
|
|
{
|
|
mutex_lock(&inode->i_mutex);
|
|
if (filp->f_mode & FMODE_READ)
|
|
inode->i_pipe->readers++;
|
|
if (filp->f_mode & FMODE_WRITE)
|
|
inode->i_pipe->writers++;
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The file_operations structs are not static because they
|
|
* are also used in linux/fs/fifo.c to do operations on FIFOs.
|
|
*/
|
|
const struct file_operations read_fifo_fops = {
|
|
.llseek = no_llseek,
|
|
.read = pipe_read,
|
|
.readv = pipe_readv,
|
|
.write = bad_pipe_w,
|
|
.poll = pipe_poll,
|
|
.ioctl = pipe_ioctl,
|
|
.open = pipe_read_open,
|
|
.release = pipe_read_release,
|
|
.fasync = pipe_read_fasync,
|
|
};
|
|
|
|
const struct file_operations write_fifo_fops = {
|
|
.llseek = no_llseek,
|
|
.read = bad_pipe_r,
|
|
.write = pipe_write,
|
|
.writev = pipe_writev,
|
|
.poll = pipe_poll,
|
|
.ioctl = pipe_ioctl,
|
|
.open = pipe_write_open,
|
|
.release = pipe_write_release,
|
|
.fasync = pipe_write_fasync,
|
|
};
|
|
|
|
const struct file_operations rdwr_fifo_fops = {
|
|
.llseek = no_llseek,
|
|
.read = pipe_read,
|
|
.readv = pipe_readv,
|
|
.write = pipe_write,
|
|
.writev = pipe_writev,
|
|
.poll = pipe_poll,
|
|
.ioctl = pipe_ioctl,
|
|
.open = pipe_rdwr_open,
|
|
.release = pipe_rdwr_release,
|
|
.fasync = pipe_rdwr_fasync,
|
|
};
|
|
|
|
static struct file_operations read_pipe_fops = {
|
|
.llseek = no_llseek,
|
|
.read = pipe_read,
|
|
.readv = pipe_readv,
|
|
.write = bad_pipe_w,
|
|
.poll = pipe_poll,
|
|
.ioctl = pipe_ioctl,
|
|
.open = pipe_read_open,
|
|
.release = pipe_read_release,
|
|
.fasync = pipe_read_fasync,
|
|
};
|
|
|
|
static struct file_operations write_pipe_fops = {
|
|
.llseek = no_llseek,
|
|
.read = bad_pipe_r,
|
|
.write = pipe_write,
|
|
.writev = pipe_writev,
|
|
.poll = pipe_poll,
|
|
.ioctl = pipe_ioctl,
|
|
.open = pipe_write_open,
|
|
.release = pipe_write_release,
|
|
.fasync = pipe_write_fasync,
|
|
};
|
|
|
|
static struct file_operations rdwr_pipe_fops = {
|
|
.llseek = no_llseek,
|
|
.read = pipe_read,
|
|
.readv = pipe_readv,
|
|
.write = pipe_write,
|
|
.writev = pipe_writev,
|
|
.poll = pipe_poll,
|
|
.ioctl = pipe_ioctl,
|
|
.open = pipe_rdwr_open,
|
|
.release = pipe_rdwr_release,
|
|
.fasync = pipe_rdwr_fasync,
|
|
};
|
|
|
|
struct pipe_inode_info * alloc_pipe_info(struct inode *inode)
|
|
{
|
|
struct pipe_inode_info *pipe;
|
|
|
|
pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
|
|
if (pipe) {
|
|
init_waitqueue_head(&pipe->wait);
|
|
pipe->r_counter = pipe->w_counter = 1;
|
|
pipe->inode = inode;
|
|
}
|
|
|
|
return pipe;
|
|
}
|
|
|
|
void __free_pipe_info(struct pipe_inode_info *pipe)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < PIPE_BUFFERS; i++) {
|
|
struct pipe_buffer *buf = pipe->bufs + i;
|
|
if (buf->ops)
|
|
buf->ops->release(pipe, buf);
|
|
}
|
|
if (pipe->tmp_page)
|
|
__free_page(pipe->tmp_page);
|
|
kfree(pipe);
|
|
}
|
|
|
|
void free_pipe_info(struct inode *inode)
|
|
{
|
|
__free_pipe_info(inode->i_pipe);
|
|
inode->i_pipe = NULL;
|
|
}
|
|
|
|
static struct vfsmount *pipe_mnt __read_mostly;
|
|
static int pipefs_delete_dentry(struct dentry *dentry)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static struct dentry_operations pipefs_dentry_operations = {
|
|
.d_delete = pipefs_delete_dentry,
|
|
};
|
|
|
|
static struct inode * get_pipe_inode(void)
|
|
{
|
|
struct inode *inode = new_inode(pipe_mnt->mnt_sb);
|
|
struct pipe_inode_info *pipe;
|
|
|
|
if (!inode)
|
|
goto fail_inode;
|
|
|
|
pipe = alloc_pipe_info(inode);
|
|
if (!pipe)
|
|
goto fail_iput;
|
|
inode->i_pipe = pipe;
|
|
|
|
pipe->readers = pipe->writers = 1;
|
|
inode->i_fop = &rdwr_pipe_fops;
|
|
|
|
/*
|
|
* Mark the inode dirty from the very beginning,
|
|
* that way it will never be moved to the dirty
|
|
* list because "mark_inode_dirty()" will think
|
|
* that it already _is_ on the dirty list.
|
|
*/
|
|
inode->i_state = I_DIRTY;
|
|
inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR;
|
|
inode->i_uid = current->fsuid;
|
|
inode->i_gid = current->fsgid;
|
|
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
|
inode->i_blksize = PAGE_SIZE;
|
|
|
|
return inode;
|
|
|
|
fail_iput:
|
|
iput(inode);
|
|
|
|
fail_inode:
|
|
return NULL;
|
|
}
|
|
|
|
int do_pipe(int *fd)
|
|
{
|
|
struct qstr this;
|
|
char name[32];
|
|
struct dentry *dentry;
|
|
struct inode * inode;
|
|
struct file *f1, *f2;
|
|
int error;
|
|
int i, j;
|
|
|
|
error = -ENFILE;
|
|
f1 = get_empty_filp();
|
|
if (!f1)
|
|
goto no_files;
|
|
|
|
f2 = get_empty_filp();
|
|
if (!f2)
|
|
goto close_f1;
|
|
|
|
inode = get_pipe_inode();
|
|
if (!inode)
|
|
goto close_f12;
|
|
|
|
error = get_unused_fd();
|
|
if (error < 0)
|
|
goto close_f12_inode;
|
|
i = error;
|
|
|
|
error = get_unused_fd();
|
|
if (error < 0)
|
|
goto close_f12_inode_i;
|
|
j = error;
|
|
|
|
error = -ENOMEM;
|
|
sprintf(name, "[%lu]", inode->i_ino);
|
|
this.name = name;
|
|
this.len = strlen(name);
|
|
this.hash = inode->i_ino; /* will go */
|
|
dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &this);
|
|
if (!dentry)
|
|
goto close_f12_inode_i_j;
|
|
|
|
dentry->d_op = &pipefs_dentry_operations;
|
|
d_add(dentry, inode);
|
|
f1->f_vfsmnt = f2->f_vfsmnt = mntget(mntget(pipe_mnt));
|
|
f1->f_dentry = f2->f_dentry = dget(dentry);
|
|
f1->f_mapping = f2->f_mapping = inode->i_mapping;
|
|
|
|
/* read file */
|
|
f1->f_pos = f2->f_pos = 0;
|
|
f1->f_flags = O_RDONLY;
|
|
f1->f_op = &read_pipe_fops;
|
|
f1->f_mode = FMODE_READ;
|
|
f1->f_version = 0;
|
|
|
|
/* write file */
|
|
f2->f_flags = O_WRONLY;
|
|
f2->f_op = &write_pipe_fops;
|
|
f2->f_mode = FMODE_WRITE;
|
|
f2->f_version = 0;
|
|
|
|
fd_install(i, f1);
|
|
fd_install(j, f2);
|
|
fd[0] = i;
|
|
fd[1] = j;
|
|
|
|
return 0;
|
|
|
|
close_f12_inode_i_j:
|
|
put_unused_fd(j);
|
|
close_f12_inode_i:
|
|
put_unused_fd(i);
|
|
close_f12_inode:
|
|
free_pipe_info(inode);
|
|
iput(inode);
|
|
close_f12:
|
|
put_filp(f2);
|
|
close_f1:
|
|
put_filp(f1);
|
|
no_files:
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* pipefs should _never_ be mounted by userland - too much of security hassle,
|
|
* no real gain from having the whole whorehouse mounted. So we don't need
|
|
* any operations on the root directory. However, we need a non-trivial
|
|
* d_name - pipe: will go nicely and kill the special-casing in procfs.
|
|
*/
|
|
static int pipefs_get_sb(struct file_system_type *fs_type,
|
|
int flags, const char *dev_name, void *data,
|
|
struct vfsmount *mnt)
|
|
{
|
|
return get_sb_pseudo(fs_type, "pipe:", NULL, PIPEFS_MAGIC, mnt);
|
|
}
|
|
|
|
static struct file_system_type pipe_fs_type = {
|
|
.name = "pipefs",
|
|
.get_sb = pipefs_get_sb,
|
|
.kill_sb = kill_anon_super,
|
|
};
|
|
|
|
static int __init init_pipe_fs(void)
|
|
{
|
|
int err = register_filesystem(&pipe_fs_type);
|
|
|
|
if (!err) {
|
|
pipe_mnt = kern_mount(&pipe_fs_type);
|
|
if (IS_ERR(pipe_mnt)) {
|
|
err = PTR_ERR(pipe_mnt);
|
|
unregister_filesystem(&pipe_fs_type);
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static void __exit exit_pipe_fs(void)
|
|
{
|
|
unregister_filesystem(&pipe_fs_type);
|
|
mntput(pipe_mnt);
|
|
}
|
|
|
|
fs_initcall(init_pipe_fs);
|
|
module_exit(exit_pipe_fs);
|