1 /* This file contains the heart of the mechanism used to read (and write) 2 * files. Read and write requests are split up into chunks that do not cross 3 * block boundaries. Each chunk is then processed in turn. Reads on special 4 * files are also detected and handled. 5 * 6 * The entry points into this file are 7 * do_read: perform the READ system call by calling read_write 8 * do_getdents: read entries from a directory (GETDENTS) 9 * read_write: actually do the work of READ and WRITE 10 * 11 */ 12 13 #include "fs.h" 14 #include <minix/callnr.h> 15 #include <minix/com.h> 16 #include <minix/u64.h> 17 #include <minix/vfsif.h> 18 #include <assert.h> 19 #include <sys/dirent.h> 20 #include <fcntl.h> 21 #include <unistd.h> 22 #include "file.h" 23 #include "scratchpad.h" 24 #include "vnode.h" 25 #include "vmnt.h" 26 27 28 /*===========================================================================* 29 * do_read * 30 *===========================================================================*/ 31 int do_read(void) 32 { 33 return(do_read_write_peek(READING, job_m_in.m_lc_vfs_readwrite.fd, 34 job_m_in.m_lc_vfs_readwrite.buf, job_m_in.m_lc_vfs_readwrite.len)); 35 } 36 37 38 /*===========================================================================* 39 * lock_bsf * 40 *===========================================================================*/ 41 void lock_bsf(void) 42 { 43 struct worker_thread *org_self; 44 45 if (mutex_trylock(&bsf_lock) == 0) 46 return; 47 48 org_self = worker_suspend(); 49 50 if (mutex_lock(&bsf_lock) != 0) 51 panic("unable to lock block special file lock"); 52 53 worker_resume(org_self); 54 } 55 56 /*===========================================================================* 57 * unlock_bsf * 58 *===========================================================================*/ 59 void unlock_bsf(void) 60 { 61 if (mutex_unlock(&bsf_lock) != 0) 62 panic("failed to unlock block special file lock"); 63 } 64 65 /*===========================================================================* 66 * check_bsf * 67 *===========================================================================*/ 68 void check_bsf_lock(void) 69 { 70 int r = mutex_trylock(&bsf_lock); 71 72 if (r == -EBUSY) 73 panic("bsf_lock locked"); 74 else if (r != 0) 75 panic("bsf_lock weird state"); 76 77 /* r == 0 */ 78 unlock_bsf(); 79 } 80 81 /*===========================================================================* 82 * actual_read_write_peek * 83 *===========================================================================*/ 84 int actual_read_write_peek(struct fproc *rfp, int rw_flag, int io_fd, 85 vir_bytes io_buf, size_t io_nbytes) 86 { 87 /* Perform read(fd, buffer, nbytes) or write(fd, buffer, nbytes) call. */ 88 struct filp *f; 89 tll_access_t locktype; 90 int r; 91 int ro = 1; 92 93 if(rw_flag == WRITING) ro = 0; 94 95 scratch(rfp).file.fd_nr = io_fd; 96 scratch(rfp).io.io_buffer = io_buf; 97 scratch(rfp).io.io_nbytes = io_nbytes; 98 99 locktype = rw_flag == WRITING ? VNODE_WRITE : VNODE_READ; 100 if ((f = get_filp2(rfp, scratch(rfp).file.fd_nr, locktype)) == NULL) 101 return(err_code); 102 103 assert(f->filp_count > 0); 104 105 if (((f->filp_mode) & (ro ? R_BIT : W_BIT)) == 0) { 106 unlock_filp(f); 107 return(EBADF); 108 } 109 if (scratch(rfp).io.io_nbytes == 0) { 110 unlock_filp(f); 111 return(0); /* so char special files need not check for 0*/ 112 } 113 114 r = read_write(rfp, rw_flag, f, scratch(rfp).io.io_buffer, 115 scratch(rfp).io.io_nbytes, who_e); 116 117 unlock_filp(f); 118 return(r); 119 } 120 121 /*===========================================================================* 122 * do_read_write_peek * 123 *===========================================================================*/ 124 int do_read_write_peek(int rw_flag, int io_fd, vir_bytes io_buf, size_t io_nbytes) 125 { 126 return actual_read_write_peek(fp, rw_flag, io_fd, io_buf, io_nbytes); 127 } 128 129 /*===========================================================================* 130 * read_write * 131 *===========================================================================*/ 132 int read_write(struct fproc *rfp, int rw_flag, struct filp *f, 133 vir_bytes buf, size_t size, endpoint_t for_e) 134 { 135 register struct vnode *vp; 136 off_t position, res_pos; 137 size_t cum_io, res_cum_io; 138 size_t cum_io_incr; 139 int op, r; 140 dev_t dev; 141 142 position = f->filp_pos; 143 vp = f->filp_vno; 144 r = OK; 145 cum_io = 0; 146 147 assert(rw_flag == READING || rw_flag == WRITING || rw_flag == PEEKING); 148 149 if (size > SSIZE_MAX) return(EINVAL); 150 151 op = (rw_flag == READING ? CDEV_READ : CDEV_WRITE); 152 153 if (S_ISFIFO(vp->v_mode)) { /* Pipes */ 154 if (rfp->fp_cum_io_partial != 0) { 155 panic("VFS: read_write: fp_cum_io_partial not clear"); 156 } 157 if(rw_flag == PEEKING) { 158 printf("read_write: peek on pipe makes no sense\n"); 159 return EINVAL; 160 } 161 r = rw_pipe(rw_flag, for_e, f, buf, size); 162 } else if (S_ISCHR(vp->v_mode)) { /* Character special files. */ 163 if(rw_flag == PEEKING) { 164 printf("read_write: peek on char device makes no sense\n"); 165 return EINVAL; 166 } 167 168 if (vp->v_sdev == NO_DEV) 169 panic("VFS: read_write tries to access char dev NO_DEV"); 170 171 dev = vp->v_sdev; 172 173 r = cdev_io(op, dev, for_e, buf, position, size, f->filp_flags); 174 if (r >= 0) { 175 /* This should no longer happen: all calls are asynchronous. */ 176 printf("VFS: I/O to device %llx succeeded immediately!?\n", dev); 177 cum_io = r; 178 position += r; 179 r = OK; 180 } else if (r == SUSPEND) { 181 /* FIXME: multiple read/write operations on a single filp 182 * should be serialized. They currently aren't; in order to 183 * achieve a similar effect, we optimistically advance the file 184 * position here. This works under the following assumptions: 185 * - character drivers that use the seek position at all, 186 * expose a view of a statically-sized range of bytes, i.e., 187 * they are basically byte-granular block devices; 188 * - if short I/O or an error is returned, all subsequent calls 189 * will return (respectively) EOF and an error; 190 * - the application never checks its own file seek position, 191 * or does not care that it may end up having seeked beyond 192 * the number of bytes it has actually read; 193 * - communication to the character driver is FIFO (this one 194 * is actually true! whew). 195 * Many improvements are possible here, but in the end, 196 * anything short of queuing concurrent operations will be 197 * suboptimal - so we settle for this hack for now. 198 */ 199 position += size; 200 } 201 } else if (S_ISBLK(vp->v_mode)) { /* Block special files. */ 202 if (vp->v_sdev == NO_DEV) 203 panic("VFS: read_write tries to access block dev NO_DEV"); 204 205 lock_bsf(); 206 207 if(rw_flag == PEEKING) { 208 r = req_bpeek(vp->v_bfs_e, vp->v_sdev, position, size); 209 } else { 210 r = req_breadwrite(vp->v_bfs_e, for_e, vp->v_sdev, position, 211 size, buf, rw_flag, &res_pos, &res_cum_io); 212 if (r == OK) { 213 position = res_pos; 214 cum_io += res_cum_io; 215 } 216 } 217 218 unlock_bsf(); 219 } else { /* Regular files */ 220 if (rw_flag == WRITING) { 221 /* Check for O_APPEND flag. */ 222 if (f->filp_flags & O_APPEND) position = vp->v_size; 223 } 224 225 /* Issue request */ 226 if(rw_flag == PEEKING) { 227 r = req_peek(vp->v_fs_e, vp->v_inode_nr, position, size); 228 } else { 229 off_t new_pos; 230 r = req_readwrite(vp->v_fs_e, vp->v_inode_nr, position, 231 rw_flag, for_e, buf, size, &new_pos, 232 &cum_io_incr); 233 234 if (r >= 0) { 235 position = new_pos; 236 cum_io += cum_io_incr; 237 } 238 } 239 } 240 241 /* On write, update file size and access time. */ 242 if (rw_flag == WRITING) { 243 if (S_ISREG(vp->v_mode) || S_ISDIR(vp->v_mode)) { 244 if (position > vp->v_size) { 245 vp->v_size = position; 246 } 247 } 248 } 249 250 f->filp_pos = position; 251 252 if (r == EPIPE && rw_flag == WRITING) { 253 /* Process is writing, but there is no reader. Tell the kernel to 254 * generate s SIGPIPE signal. 255 */ 256 if (!(f->filp_flags & O_NOSIGPIPE)) { 257 sys_kill(rfp->fp_endpoint, SIGPIPE); 258 } 259 } 260 261 if (r == OK) { 262 return(cum_io); 263 } 264 return(r); 265 } 266 267 /*===========================================================================* 268 * do_getdents * 269 *===========================================================================*/ 270 int do_getdents(void) 271 { 272 /* Perform the getdents(fd, buf, size) system call. */ 273 int r = OK; 274 off_t new_pos; 275 register struct filp *rfilp; 276 277 scratch(fp).file.fd_nr = job_m_in.m_lc_vfs_readwrite.fd; 278 scratch(fp).io.io_buffer = job_m_in.m_lc_vfs_readwrite.buf; 279 scratch(fp).io.io_nbytes = job_m_in.m_lc_vfs_readwrite.len; 280 281 /* Is the file descriptor valid? */ 282 if ( (rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_READ)) == NULL) 283 return(err_code); 284 285 if (!(rfilp->filp_mode & R_BIT)) 286 r = EBADF; 287 else if (!S_ISDIR(rfilp->filp_vno->v_mode)) 288 r = EBADF; 289 290 if (r == OK) { 291 r = req_getdents(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr, 292 rfilp->filp_pos, scratch(fp).io.io_buffer, 293 scratch(fp).io.io_nbytes, &new_pos, 0); 294 295 if (r > 0) rfilp->filp_pos = new_pos; 296 } 297 298 unlock_filp(rfilp); 299 return(r); 300 } 301 302 303 /*===========================================================================* 304 * rw_pipe * 305 *===========================================================================*/ 306 int rw_pipe(rw_flag, usr_e, f, buf, req_size) 307 int rw_flag; /* READING or WRITING */ 308 endpoint_t usr_e; 309 struct filp *f; 310 vir_bytes buf; 311 size_t req_size; 312 { 313 int r, oflags, partial_pipe = 0; 314 size_t size, cum_io; 315 size_t cum_io_incr; 316 struct vnode *vp; 317 off_t position, new_pos; 318 319 /* Must make sure we're operating on locked filp and vnode */ 320 assert(tll_locked_by_me(&f->filp_vno->v_lock)); 321 assert(mutex_trylock(&f->filp_lock) == -EDEADLK); 322 323 oflags = f->filp_flags; 324 vp = f->filp_vno; 325 position = 0; /* Not actually used */ 326 327 assert(rw_flag == READING || rw_flag == WRITING); 328 329 /* fp->fp_cum_io_partial is only nonzero when doing partial writes */ 330 cum_io = fp->fp_cum_io_partial; 331 332 r = pipe_check(f, rw_flag, oflags, req_size, 0); 333 if (r <= 0) { 334 if (r == SUSPEND) pipe_suspend(f, buf, req_size); 335 return(r); 336 } 337 338 size = r; 339 if (size < req_size) partial_pipe = 1; 340 341 /* Truncate read request at size. */ 342 if (rw_flag == READING && size > vp->v_size) { 343 size = vp->v_size; 344 } 345 346 if (vp->v_mapfs_e == 0) 347 panic("unmapped pipe"); 348 349 r = req_readwrite(vp->v_mapfs_e, vp->v_mapinode_nr, position, rw_flag, usr_e, 350 buf, size, &new_pos, &cum_io_incr); 351 352 if (r != OK) { 353 return(r); 354 } 355 356 cum_io += cum_io_incr; 357 buf += cum_io_incr; 358 req_size -= cum_io_incr; 359 360 if (rw_flag == READING) 361 vp->v_size -= cum_io_incr; 362 else 363 vp->v_size += cum_io_incr; 364 365 if (partial_pipe) { 366 /* partial write on pipe with */ 367 /* O_NONBLOCK, return write count */ 368 if (!(oflags & O_NONBLOCK)) { 369 /* partial write on pipe with req_size > PIPE_SIZE, 370 * non-atomic 371 */ 372 fp->fp_cum_io_partial = cum_io; 373 pipe_suspend(f, buf, req_size); 374 return(SUSPEND); 375 } 376 } 377 378 fp->fp_cum_io_partial = 0; 379 380 return(cum_io); 381 } 382