| /* |
| * 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*/ |
| /* contruction 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->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; |
| } |