diff options
Diffstat (limited to 'fs/pipe.c')
-rw-r--r-- | fs/pipe.c | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/fs/pipe.c b/fs/pipe.c new file mode 100644 index 000000000..bc557888e --- /dev/null +++ b/fs/pipe.c @@ -0,0 +1,426 @@ +/* + * linux/fs/pipe.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include <asm/segment.h> + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/fcntl.h> +#include <linux/termios.h> + + +/* We don't use the head/tail construction any more. Now we use the start/len*/ +/* construction providing full use of PIPE_BUF (multiple of PAGE_SIZE) */ +/* Florian Coosmann (FGC) ^ current = 1 */ +/* Additionally, we now use locking technique. This prevents race condition */ +/* in case of paging and multiple read/write on the same pipe. (FGC) */ + + +static int pipe_read(struct inode * inode, struct file * filp, char * buf, int count) +{ + int chars = 0, size = 0, read = 0; + char *pipebuf; + + if (filp->f_flags & O_NONBLOCK) { + if (PIPE_LOCK(*inode)) + return -EAGAIN; + if (PIPE_EMPTY(*inode)) + if (PIPE_WRITERS(*inode)) + return -EAGAIN; + else + return 0; + } else while (PIPE_EMPTY(*inode) || PIPE_LOCK(*inode)) { + if (PIPE_EMPTY(*inode)) { + if (!PIPE_WRITERS(*inode)) + return 0; + } + if (current->signal & ~current->blocked) + return -ERESTARTSYS; + interruptible_sleep_on(&PIPE_WAIT(*inode)); + } + PIPE_LOCK(*inode)++; + while (count>0 && (size = PIPE_SIZE(*inode))) { + chars = PIPE_MAX_RCHUNK(*inode); + if (chars > count) + chars = count; + if (chars > size) + chars = size; + read += chars; + pipebuf = PIPE_BASE(*inode)+PIPE_START(*inode); + PIPE_START(*inode) += chars; + PIPE_START(*inode) &= (PIPE_BUF-1); + PIPE_LEN(*inode) -= chars; + count -= chars; + memcpy_tofs(buf, pipebuf, chars ); + buf += chars; + } + PIPE_LOCK(*inode)--; + wake_up_interruptible(&PIPE_WAIT(*inode)); + if (read) + return read; + if (PIPE_WRITERS(*inode)) + return -EAGAIN; + return 0; +} + +static int pipe_write(struct inode * inode, struct file * filp, char * buf, int count) +{ + int chars = 0, free = 0, written = 0; + char *pipebuf; + + if (!PIPE_READERS(*inode)) { /* no readers */ + send_sig(SIGPIPE,current,0); + return -EPIPE; + } +/* if count <= PIPE_BUF, we have to make it atomic */ + if (count <= PIPE_BUF) + free = count; + else + free = 1; /* can't do it atomically, wait for any free space */ + while (count>0) { + while ((PIPE_FREE(*inode) < free) || PIPE_LOCK(*inode)) { + if (!PIPE_READERS(*inode)) { /* no readers */ + send_sig(SIGPIPE,current,0); + return written? :-EPIPE; + } + if (current->signal & ~current->blocked) + return written? :-ERESTARTSYS; + if (filp->f_flags & O_NONBLOCK) + return written? :-EAGAIN; + interruptible_sleep_on(&PIPE_WAIT(*inode)); + } + PIPE_LOCK(*inode)++; + while (count>0 && (free = PIPE_FREE(*inode))) { + chars = PIPE_MAX_WCHUNK(*inode); + if (chars > count) + chars = count; + if (chars > free) + chars = free; + pipebuf = PIPE_BASE(*inode)+PIPE_END(*inode); + written += chars; + PIPE_LEN(*inode) += chars; + count -= chars; + memcpy_fromfs(pipebuf, buf, chars ); + buf += chars; + } + PIPE_LOCK(*inode)--; + wake_up_interruptible(&PIPE_WAIT(*inode)); + free = 1; + } + return written; +} + +static int pipe_lseek(struct inode * inode, struct file * file, off_t offset, int orig) +{ + return -ESPIPE; +} + +static int pipe_readdir(struct inode * inode, struct file * file, struct dirent * de, int count) +{ + return -ENOTDIR; +} + +static int bad_pipe_rw(struct inode * inode, struct file * filp, char * buf, int count) +{ + return -EBADF; +} + +static int pipe_ioctl(struct inode *pino, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + int error; + + switch (cmd) { + case FIONREAD: + error = verify_area(VERIFY_WRITE, (void *) arg,4); + if (!error) + put_fs_long(PIPE_SIZE(*pino),(unsigned long *) arg); + return error; + default: + return -EINVAL; + } +} + +static int pipe_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait) +{ + switch (sel_type) { + case SEL_IN: + if (!PIPE_EMPTY(*inode) || !PIPE_WRITERS(*inode)) + return 1; + select_wait(&PIPE_WAIT(*inode), wait); + return 0; + case SEL_OUT: + if (!PIPE_FULL(*inode) || !PIPE_READERS(*inode)) + return 1; + select_wait(&PIPE_WAIT(*inode), wait); + return 0; + case SEL_EX: + if (!PIPE_READERS(*inode) || !PIPE_WRITERS(*inode)) + return 1; + select_wait(&inode->i_wait,wait); + return 0; + } + return 0; +} + +/* + * Arggh. Why does SunOS have to have different select() behaviour + * for pipes and fifos? Hate-Hate-Hate. See difference in SEL_IN.. + */ +static int fifo_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait) +{ + switch (sel_type) { + case SEL_IN: + if (!PIPE_EMPTY(*inode)) + return 1; + select_wait(&PIPE_WAIT(*inode), wait); + return 0; + case SEL_OUT: + if (!PIPE_FULL(*inode) || !PIPE_READERS(*inode)) + return 1; + select_wait(&PIPE_WAIT(*inode), wait); + return 0; + case SEL_EX: + if (!PIPE_READERS(*inode) || !PIPE_WRITERS(*inode)) + return 1; + select_wait(&inode->i_wait,wait); + return 0; + } + return 0; +} + +/* + * The 'connect_xxx()' functions are needed for named pipes when + * the open() code hasn't guaranteed a connection (O_NONBLOCK), + * and we need to act differently until we do get a writer.. + */ +static int connect_read(struct inode * inode, struct file * filp, char * buf, int count) +{ + while (!PIPE_SIZE(*inode)) { + if (PIPE_WRITERS(*inode)) + break; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + wake_up_interruptible(& PIPE_WAIT(*inode)); + if (current->signal & ~current->blocked) + return -ERESTARTSYS; + interruptible_sleep_on(& PIPE_WAIT(*inode)); + } + filp->f_op = &read_fifo_fops; + return pipe_read(inode,filp,buf,count); +} + +static int connect_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait) +{ + switch (sel_type) { + case SEL_IN: + if (!PIPE_EMPTY(*inode)) { + filp->f_op = &read_fifo_fops; + return 1; + } + select_wait(&PIPE_WAIT(*inode), wait); + return 0; + case SEL_OUT: + if (!PIPE_FULL(*inode)) + return 1; + select_wait(&PIPE_WAIT(*inode), wait); + return 0; + case SEL_EX: + if (!PIPE_READERS(*inode) || !PIPE_WRITERS(*inode)) + return 1; + select_wait(&inode->i_wait,wait); + return 0; + } + return 0; +} + +/* + * Ok, these three routines NOW keep track of readers/writers, + * Linus previously did it with inode->i_count checking. + */ +static void pipe_read_release(struct inode * inode, struct file * filp) +{ + PIPE_READERS(*inode)--; + wake_up_interruptible(&PIPE_WAIT(*inode)); +} + +static void pipe_write_release(struct inode * inode, struct file * filp) +{ + PIPE_WRITERS(*inode)--; + wake_up_interruptible(&PIPE_WAIT(*inode)); +} + +static void pipe_rdwr_release(struct inode * inode, struct file * filp) +{ + PIPE_READERS(*inode)--; + PIPE_WRITERS(*inode)--; + wake_up_interruptible(&PIPE_WAIT(*inode)); +} + +/* + * The file_operations structs are not static because they + * are also used in linux/fs/fifo.c to do operations on fifo's. + */ +struct file_operations connecting_fifo_fops = { + pipe_lseek, + connect_read, + bad_pipe_rw, + pipe_readdir, + connect_select, + pipe_ioctl, + NULL, /* no mmap on pipes.. surprise */ + NULL, /* no special open code */ + pipe_read_release, + NULL +}; + +struct file_operations read_fifo_fops = { + pipe_lseek, + pipe_read, + bad_pipe_rw, + pipe_readdir, + fifo_select, + pipe_ioctl, + NULL, /* no mmap on pipes.. surprise */ + NULL, /* no special open code */ + pipe_read_release, + NULL +}; + +struct file_operations write_fifo_fops = { + pipe_lseek, + bad_pipe_rw, + pipe_write, + pipe_readdir, + fifo_select, + pipe_ioctl, + NULL, /* mmap */ + NULL, /* no special open code */ + pipe_write_release, + NULL +}; + +struct file_operations rdwr_fifo_fops = { + pipe_lseek, + pipe_read, + pipe_write, + pipe_readdir, + fifo_select, + pipe_ioctl, + NULL, /* mmap */ + NULL, /* no special open code */ + pipe_rdwr_release, + NULL +}; + +struct file_operations read_pipe_fops = { + pipe_lseek, + pipe_read, + bad_pipe_rw, + pipe_readdir, + pipe_select, + pipe_ioctl, + NULL, /* no mmap on pipes.. surprise */ + NULL, /* no special open code */ + pipe_read_release, + NULL +}; + +struct file_operations write_pipe_fops = { + pipe_lseek, + bad_pipe_rw, + pipe_write, + pipe_readdir, + pipe_select, + pipe_ioctl, + NULL, /* mmap */ + NULL, /* no special open code */ + pipe_write_release, + NULL +}; + +struct file_operations rdwr_pipe_fops = { + pipe_lseek, + pipe_read, + pipe_write, + pipe_readdir, + pipe_select, + pipe_ioctl, + NULL, /* mmap */ + NULL, /* no special open code */ + pipe_rdwr_release, + NULL +}; + +struct inode_operations pipe_inode_operations = { + &rdwr_pipe_fops, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +asmlinkage int sys_pipe(unsigned long * fildes) +{ + struct inode * inode; + struct file * f[2]; + int fd[2]; + int i,j; + + j = verify_area(VERIFY_WRITE,fildes,8); + if (j) + return j; + for(j=0 ; j<2 ; j++) + if (!(f[j] = get_empty_filp())) + break; + if (j==1) + f[0]->f_count--; + if (j<2) + return -ENFILE; + j=0; + for(i=0;j<2 && i<NR_OPEN;i++) + if (!current->files->fd[i]) { + current->files->fd[ fd[j]=i ] = f[j]; + j++; + } + if (j==1) + current->files->fd[fd[0]]=NULL; + if (j<2) { + f[0]->f_count--; + f[1]->f_count--; + return -EMFILE; + } + if (!(inode=get_pipe_inode())) { + current->files->fd[fd[0]] = NULL; + current->files->fd[fd[1]] = NULL; + f[0]->f_count--; + f[1]->f_count--; + return -ENFILE; + } + f[0]->f_inode = f[1]->f_inode = inode; + f[0]->f_pos = f[1]->f_pos = 0; + f[0]->f_flags = O_RDONLY; + f[0]->f_op = &read_pipe_fops; + f[0]->f_mode = 1; /* read */ + f[1]->f_flags = O_WRONLY; + f[1]->f_op = &write_pipe_fops; + f[1]->f_mode = 2; /* write */ + put_fs_long(fd[0],0+fildes); + put_fs_long(fd[1],1+fildes); + return 0; +} |