home *** CD-ROM | disk | FTP | other *** search
- /*
- * linux/fs/pipe.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file README.legal in the main directory of this archive
- * for more details.
- */
-
- #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>
-
- static int pipe_read(struct inode * inode, struct file * filp, char * buf, int count)
- {
- int chars, size, read = 0;
-
- if (!(filp->f_flags & O_NONBLOCK))
- while (!PIPE_SIZE(*inode)) {
- wake_up(& PIPE_WRITE_WAIT(*inode));
- if (!PIPE_WRITERS(*inode)) /* are there any writers? */
- return 0;
- if (current->signal & ~current->blocked)
- return -ERESTARTSYS;
- interruptible_sleep_on(& PIPE_READ_WAIT(*inode));
- }
- while (count>0 && (size = PIPE_SIZE(*inode))) {
- chars = PAGE_SIZE-PIPE_TAIL(*inode);
- if (chars > count)
- chars = count;
- if (chars > size)
- chars = size;
- memcpy_tofs(buf, PIPE_BASE(*inode)+PIPE_TAIL(*inode), chars );
- read += chars;
- PIPE_TAIL(*inode) += chars;
- PIPE_TAIL(*inode) &= (PAGE_SIZE-1);
- count -= chars;
- buf += chars;
- }
- wake_up(& PIPE_WRITE_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, size, written = 0;
-
- if (!PIPE_READERS(*inode)) { /* no readers */
- send_sig(SIGPIPE,current,0);
- return -EPIPE;
- }
- /* if count < PAGE_SIZE, we have to make it atomic */
- if (count < PAGE_SIZE)
- size = PAGE_SIZE-count;
- else
- size = PAGE_SIZE-1;
- while (count>0) {
- while (PIPE_SIZE(*inode) >= size) {
- if (!PIPE_READERS(*inode)) { /* no readers */
- send_sig(SIGPIPE,current,0);
- return written?written:-EPIPE;
- }
- if (current->signal & ~current->blocked)
- return written?written:-ERESTARTSYS;
- if (filp->f_flags & O_NONBLOCK)
- return written?written:-EAGAIN;
- else
- interruptible_sleep_on(&PIPE_WRITE_WAIT(*inode));
- }
- while (count>0 && (size = (PAGE_SIZE-1)-PIPE_SIZE(*inode))) {
- chars = PAGE_SIZE-PIPE_HEAD(*inode);
- if (chars > count)
- chars = count;
- if (chars > size)
- chars = size;
- memcpy_fromfs(PIPE_BASE(*inode)+PIPE_HEAD(*inode), buf, chars );
- written += chars;
- PIPE_HEAD(*inode) += chars;
- PIPE_HEAD(*inode) &= (PAGE_SIZE-1);
- count -= chars;
- buf += chars;
- }
- wake_up(& PIPE_READ_WAIT(*inode));
- size = PAGE_SIZE-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_READ_WAIT(*inode), wait);
- return 0;
- case SEL_OUT:
- if (!PIPE_FULL(*inode) || !PIPE_READERS(*inode))
- return 1;
- select_wait(&PIPE_WRITE_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_READ_WAIT(*inode), wait);
- return 0;
- case SEL_OUT:
- if (!PIPE_FULL(*inode) || !PIPE_READERS(*inode))
- return 1;
- select_wait(&PIPE_WRITE_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(& PIPE_WRITE_WAIT(*inode));
- if (current->signal & ~current->blocked)
- return -ERESTARTSYS;
- interruptible_sleep_on(& PIPE_READ_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_READ_WAIT(*inode), wait);
- return 0;
- case SEL_OUT:
- if (!PIPE_FULL(*inode))
- return 1;
- select_wait(&PIPE_WRITE_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(&PIPE_WRITE_WAIT(*inode));
- }
-
- static void pipe_write_release(struct inode * inode, struct file * filp)
- {
- PIPE_WRITERS(*inode)--;
- wake_up(&PIPE_READ_WAIT(*inode));
- }
-
- static void pipe_rdwr_release(struct inode * inode, struct file * filp)
- {
- PIPE_READERS(*inode)--;
- PIPE_WRITERS(*inode)--;
- wake_up(&PIPE_READ_WAIT(*inode));
- wake_up(&PIPE_WRITE_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->filp[i]) {
- current->filp[ fd[j]=i ] = f[j];
- j++;
- }
- if (j==1)
- current->filp[fd[0]]=NULL;
- if (j<2) {
- f[0]->f_count--;
- f[1]->f_count--;
- return -EMFILE;
- }
- if (!(inode=get_pipe_inode())) {
- current->filp[fd[0]] = NULL;
- current->filp[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;
- }
-