Welcome to The Fiwix Project
A UNIX-like kernel for the i386 architecture
1 /* 2 * fiwix/fs/pipefs/pipe.c 3 * 4 * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved. 5 * Distributed under the terms of the Fiwix License. 6 */ 7 8 #include <fiwix/types.h> 9 #include <fiwix/errno.h> 10 #include <fiwix/fs.h> 11 #include <fiwix/filesystems.h> 12 #include <fiwix/fs_pipe.h> 13 #include <fiwix/stat.h> 14 #include <fiwix/fcntl.h> 15 #include <fiwix/ioctl.h> 16 #include <fiwix/sleep.h> 17 #include <fiwix/sched.h> 18 #include <fiwix/stdio.h> 19 #include <fiwix/string.h> 20 21 static struct resource pipe_resource = { NULL, NULL }; 22 23 int pipefs_close(struct inode *i, struct fd *fd_table) 24 { 25 if((fd_table->flags & O_ACCMODE) == O_RDONLY) { 26 if(!--i->u.pipefs.i_readers) { 27 wakeup(&do_select); 28 wakeup(&pipefs_write); 29 } 30 } 31 if((fd_table->flags & O_ACCMODE) == O_WRONLY) { 32 if(!--i->u.pipefs.i_writers) { 33 wakeup(&do_select); 34 wakeup(&pipefs_read); 35 } 36 } 37 if((fd_table->flags & O_ACCMODE) == O_RDWR) { 38 if(!--i->u.pipefs.i_readers) { 39 wakeup(&do_select); 40 wakeup(&pipefs_write); 41 } 42 if(!--i->u.pipefs.i_writers) { 43 wakeup(&do_select); 44 wakeup(&pipefs_read); 45 } 46 } 47 return 0; 48 } 49 50 int pipefs_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) 51 { 52 __off_t bytes_read; 53 __size_t n, limit; 54 char *data; 55 56 bytes_read = 0; 57 data = i->u.pipefs.i_data; 58 59 while(count) { 60 if(i->u.pipefs.i_writeoff) { 61 if(i->u.pipefs.i_readoff >= i->u.pipefs.i_writeoff) { 62 limit = PIPE_BUF - i->u.pipefs.i_readoff; 63 } else { 64 limit = i->u.pipefs.i_writeoff - i->u.pipefs.i_readoff; 65 } 66 } else { 67 limit = PIPE_BUF - i->u.pipefs.i_readoff; 68 } 69 n = MIN(limit, count); 70 if(i->i_size && n) { 71 lock_resource(&pipe_resource); 72 memcpy_b(buffer + bytes_read, data + i->u.pipefs.i_readoff, n); 73 bytes_read += n; 74 i->u.pipefs.i_readoff += n; 75 i->i_size -= n; 76 if(i->u.pipefs.i_writeoff >= PIPE_BUF) { 77 i->u.pipefs.i_writeoff = 0; 78 } 79 unlock_resource(&pipe_resource); 80 wakeup(&do_select); 81 wakeup(&pipefs_write); 82 break; 83 } else { 84 if(i->u.pipefs.i_writers) { 85 if(fd_table->flags & O_NONBLOCK) { 86 return -EAGAIN; 87 } 88 if(sleep(&pipefs_read, PROC_INTERRUPTIBLE)) { 89 return -EINTR; 90 } 91 } else { 92 if(i->i_size) { 93 if(i->u.pipefs.i_readoff >= PIPE_BUF) { 94 i->u.pipefs.i_readoff = 0; 95 continue; 96 } 97 } 98 break; 99 } 100 } 101 } 102 if(!i->i_size) { 103 i->u.pipefs.i_readoff = 0; 104 i->u.pipefs.i_writeoff = 0; 105 } 106 return bytes_read; 107 } 108 109 int pipefs_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) 110 { 111 __off_t bytes_written; 112 __size_t n; 113 char *data; 114 int limit; 115 116 bytes_written = 0; 117 data = i->u.pipefs.i_data; 118 119 while(bytes_written < count) { 120 /* if the read end closes then send signal and return */ 121 if(!i->u.pipefs.i_readers) { 122 send_sig(current, SIGPIPE); 123 return -EPIPE; 124 } 125 126 if(i->u.pipefs.i_readoff) { 127 if(i->u.pipefs.i_writeoff <= i->u.pipefs.i_readoff) { 128 limit = i->u.pipefs.i_readoff; 129 } else { 130 limit = PIPE_BUF; 131 } 132 } else { 133 limit = PIPE_BUF; 134 } 135 136 n = MIN((count - bytes_written), (limit - i->u.pipefs.i_writeoff)); 137 138 /* 139 * POSIX requires that any write operation involving fewer than 140 * PIPE_BUF bytes must be automatically executed and finished 141 * without being interleaved with write operations of other 142 * processes to the same pipe. 143 */ 144 if(n && n <= PIPE_BUF) { 145 lock_resource(&pipe_resource); 146 memcpy_b(data + i->u.pipefs.i_writeoff, buffer + bytes_written, n); 147 bytes_written += n; 148 i->u.pipefs.i_writeoff += n; 149 i->i_size += n; 150 if(i->u.pipefs.i_readoff >= PIPE_BUF) { 151 i->u.pipefs.i_readoff = 0; 152 } 153 unlock_resource(&pipe_resource); 154 wakeup(&do_select); 155 wakeup(&pipefs_read); 156 continue; 157 } 158 159 wakeup(&do_select); 160 wakeup(&pipefs_read); 161 if(!(fd_table->flags & O_NONBLOCK)) { 162 if(sleep(&pipefs_write, PROC_INTERRUPTIBLE)) { 163 return -EINTR; 164 } 165 } else { 166 return -EAGAIN; 167 } 168 } 169 return bytes_written; 170 } 171 172 int pipefs_ioctl(struct inode *i, int cmd, unsigned long int arg) 173 { 174 int errno; 175 176 switch(cmd) { 177 case FIONREAD: 178 if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { 179 return errno; 180 } 181 memcpy_b((void *)arg, &i->i_size, sizeof(unsigned int)); 182 break; 183 default: 184 return -EINVAL; 185 } 186 return 0; 187 } 188 189 int pipefs_lseek(struct inode *i, __off_t offset) 190 { 191 return -ESPIPE; 192 } 193 194 int pipefs_select(struct inode *i, int flag) 195 { 196 switch(flag) { 197 case SEL_R: 198 /* 199 * if !i->i_size && !i->u.pipefs.i_writers 200 * should also return 1? 201 */ 202 if(i->i_size || !i->u.pipefs.i_writers) { 203 return 1; 204 } 205 break; 206 case SEL_W: 207 /* 208 * if i->i_size == PIPE_BUF && !i->u.pipefs.i_readers 209 * should also return 1? 210 */ 211 if(i->i_size < PIPE_BUF || !i->u.pipefs.i_readers) { 212 return 1; 213 } 214 break; 215 } 216 return 0; 217 }