1433d6423SLionel Sambuc /* This file contains the heart of the mechanism used to read (and write) 2433d6423SLionel Sambuc * files. Read and write requests are split up into chunks that do not cross 3433d6423SLionel Sambuc * block boundaries. Each chunk is then processed in turn. Reads on special 4433d6423SLionel Sambuc * files are also detected and handled. 5433d6423SLionel Sambuc * 6433d6423SLionel Sambuc * The entry points into this file are 7433d6423SLionel Sambuc * do_read: perform the READ system call by calling read_write 8433d6423SLionel Sambuc * do_getdents: read entries from a directory (GETDENTS) 9433d6423SLionel Sambuc * read_write: actually do the work of READ and WRITE 10433d6423SLionel Sambuc * 11433d6423SLionel Sambuc */ 12433d6423SLionel Sambuc 13433d6423SLionel Sambuc #include "fs.h" 14433d6423SLionel Sambuc #include <minix/callnr.h> 15433d6423SLionel Sambuc #include <minix/com.h> 16433d6423SLionel Sambuc #include <minix/u64.h> 17433d6423SLionel Sambuc #include <minix/vfsif.h> 18433d6423SLionel Sambuc #include <assert.h> 19433d6423SLionel Sambuc #include <sys/dirent.h> 20433d6423SLionel Sambuc #include <fcntl.h> 21433d6423SLionel Sambuc #include <unistd.h> 22433d6423SLionel Sambuc #include "file.h" 23433d6423SLionel Sambuc #include "scratchpad.h" 24433d6423SLionel Sambuc #include "vnode.h" 25433d6423SLionel Sambuc #include "vmnt.h" 26433d6423SLionel Sambuc 27433d6423SLionel Sambuc 28433d6423SLionel Sambuc /*===========================================================================* 29433d6423SLionel Sambuc * do_read * 30433d6423SLionel Sambuc *===========================================================================*/ 31433d6423SLionel Sambuc int do_read(void) 32433d6423SLionel Sambuc { 33433d6423SLionel Sambuc return(do_read_write_peek(READING, job_m_in.m_lc_vfs_readwrite.fd, 34433d6423SLionel Sambuc job_m_in.m_lc_vfs_readwrite.buf, job_m_in.m_lc_vfs_readwrite.len)); 35433d6423SLionel Sambuc } 36433d6423SLionel Sambuc 37433d6423SLionel Sambuc 38433d6423SLionel Sambuc /*===========================================================================* 39433d6423SLionel Sambuc * lock_bsf * 40433d6423SLionel Sambuc *===========================================================================*/ 41433d6423SLionel Sambuc void lock_bsf(void) 42433d6423SLionel Sambuc { 43433d6423SLionel Sambuc struct worker_thread *org_self; 44433d6423SLionel Sambuc 45433d6423SLionel Sambuc if (mutex_trylock(&bsf_lock) == 0) 46433d6423SLionel Sambuc return; 47433d6423SLionel Sambuc 48433d6423SLionel Sambuc org_self = worker_suspend(); 49433d6423SLionel Sambuc 50433d6423SLionel Sambuc if (mutex_lock(&bsf_lock) != 0) 51433d6423SLionel Sambuc panic("unable to lock block special file lock"); 52433d6423SLionel Sambuc 53433d6423SLionel Sambuc worker_resume(org_self); 54433d6423SLionel Sambuc } 55433d6423SLionel Sambuc 56433d6423SLionel Sambuc /*===========================================================================* 57433d6423SLionel Sambuc * unlock_bsf * 58433d6423SLionel Sambuc *===========================================================================*/ 59433d6423SLionel Sambuc void unlock_bsf(void) 60433d6423SLionel Sambuc { 61433d6423SLionel Sambuc if (mutex_unlock(&bsf_lock) != 0) 62433d6423SLionel Sambuc panic("failed to unlock block special file lock"); 63433d6423SLionel Sambuc } 64433d6423SLionel Sambuc 65433d6423SLionel Sambuc /*===========================================================================* 66433d6423SLionel Sambuc * check_bsf * 67433d6423SLionel Sambuc *===========================================================================*/ 68433d6423SLionel Sambuc void check_bsf_lock(void) 69433d6423SLionel Sambuc { 70433d6423SLionel Sambuc int r = mutex_trylock(&bsf_lock); 71433d6423SLionel Sambuc 72433d6423SLionel Sambuc if (r == -EBUSY) 73433d6423SLionel Sambuc panic("bsf_lock locked"); 74433d6423SLionel Sambuc else if (r != 0) 75433d6423SLionel Sambuc panic("bsf_lock weird state"); 76433d6423SLionel Sambuc 77433d6423SLionel Sambuc /* r == 0 */ 78433d6423SLionel Sambuc unlock_bsf(); 79433d6423SLionel Sambuc } 80433d6423SLionel Sambuc 81433d6423SLionel Sambuc /*===========================================================================* 82433d6423SLionel Sambuc * actual_read_write_peek * 83433d6423SLionel Sambuc *===========================================================================*/ 84433d6423SLionel Sambuc int actual_read_write_peek(struct fproc *rfp, int rw_flag, int io_fd, 85433d6423SLionel Sambuc vir_bytes io_buf, size_t io_nbytes) 86433d6423SLionel Sambuc { 87433d6423SLionel Sambuc /* Perform read(fd, buffer, nbytes) or write(fd, buffer, nbytes) call. */ 88433d6423SLionel Sambuc struct filp *f; 89433d6423SLionel Sambuc tll_access_t locktype; 90433d6423SLionel Sambuc int r; 91433d6423SLionel Sambuc int ro = 1; 92433d6423SLionel Sambuc 93433d6423SLionel Sambuc if(rw_flag == WRITING) ro = 0; 94433d6423SLionel Sambuc 95433d6423SLionel Sambuc scratch(rfp).file.fd_nr = io_fd; 96433d6423SLionel Sambuc scratch(rfp).io.io_buffer = io_buf; 97433d6423SLionel Sambuc scratch(rfp).io.io_nbytes = io_nbytes; 98433d6423SLionel Sambuc 99433d6423SLionel Sambuc locktype = rw_flag == WRITING ? VNODE_WRITE : VNODE_READ; 100433d6423SLionel Sambuc if ((f = get_filp2(rfp, scratch(rfp).file.fd_nr, locktype)) == NULL) 101433d6423SLionel Sambuc return(err_code); 102433d6423SLionel Sambuc 103433d6423SLionel Sambuc assert(f->filp_count > 0); 104433d6423SLionel Sambuc 105433d6423SLionel Sambuc if (((f->filp_mode) & (ro ? R_BIT : W_BIT)) == 0) { 106433d6423SLionel Sambuc unlock_filp(f); 107433d6423SLionel Sambuc return(EBADF); 108433d6423SLionel Sambuc } 109433d6423SLionel Sambuc if (scratch(rfp).io.io_nbytes == 0) { 110433d6423SLionel Sambuc unlock_filp(f); 111433d6423SLionel Sambuc return(0); /* so char special files need not check for 0*/ 112433d6423SLionel Sambuc } 113433d6423SLionel Sambuc 114433d6423SLionel Sambuc r = read_write(rfp, rw_flag, f, scratch(rfp).io.io_buffer, 115433d6423SLionel Sambuc scratch(rfp).io.io_nbytes, who_e); 116433d6423SLionel Sambuc 117433d6423SLionel Sambuc unlock_filp(f); 118433d6423SLionel Sambuc return(r); 119433d6423SLionel Sambuc } 120433d6423SLionel Sambuc 121433d6423SLionel Sambuc /*===========================================================================* 122433d6423SLionel Sambuc * do_read_write_peek * 123433d6423SLionel Sambuc *===========================================================================*/ 124433d6423SLionel Sambuc int do_read_write_peek(int rw_flag, int io_fd, vir_bytes io_buf, size_t io_nbytes) 125433d6423SLionel Sambuc { 126433d6423SLionel Sambuc return actual_read_write_peek(fp, rw_flag, io_fd, io_buf, io_nbytes); 127433d6423SLionel Sambuc } 128433d6423SLionel Sambuc 129433d6423SLionel Sambuc /*===========================================================================* 130433d6423SLionel Sambuc * read_write * 131433d6423SLionel Sambuc *===========================================================================*/ 132433d6423SLionel Sambuc int read_write(struct fproc *rfp, int rw_flag, struct filp *f, 133433d6423SLionel Sambuc vir_bytes buf, size_t size, endpoint_t for_e) 134433d6423SLionel Sambuc { 135433d6423SLionel Sambuc register struct vnode *vp; 136433d6423SLionel Sambuc off_t position, res_pos; 137*3c8950ccSBen Gras size_t cum_io, res_cum_io; 138*3c8950ccSBen Gras size_t cum_io_incr; 139433d6423SLionel Sambuc int op, r; 140433d6423SLionel Sambuc dev_t dev; 141433d6423SLionel Sambuc 142433d6423SLionel Sambuc position = f->filp_pos; 143433d6423SLionel Sambuc vp = f->filp_vno; 144433d6423SLionel Sambuc r = OK; 145433d6423SLionel Sambuc cum_io = 0; 146433d6423SLionel Sambuc 147433d6423SLionel Sambuc assert(rw_flag == READING || rw_flag == WRITING || rw_flag == PEEKING); 148433d6423SLionel Sambuc 149433d6423SLionel Sambuc if (size > SSIZE_MAX) return(EINVAL); 150433d6423SLionel Sambuc 151433d6423SLionel Sambuc op = (rw_flag == READING ? CDEV_READ : CDEV_WRITE); 152433d6423SLionel Sambuc 153433d6423SLionel Sambuc if (S_ISFIFO(vp->v_mode)) { /* Pipes */ 154433d6423SLionel Sambuc if (rfp->fp_cum_io_partial != 0) { 155433d6423SLionel Sambuc panic("VFS: read_write: fp_cum_io_partial not clear"); 156433d6423SLionel Sambuc } 157433d6423SLionel Sambuc if(rw_flag == PEEKING) { 158433d6423SLionel Sambuc printf("read_write: peek on pipe makes no sense\n"); 159433d6423SLionel Sambuc return EINVAL; 160433d6423SLionel Sambuc } 161433d6423SLionel Sambuc r = rw_pipe(rw_flag, for_e, f, buf, size); 162433d6423SLionel Sambuc } else if (S_ISCHR(vp->v_mode)) { /* Character special files. */ 163433d6423SLionel Sambuc if(rw_flag == PEEKING) { 164433d6423SLionel Sambuc printf("read_write: peek on char device makes no sense\n"); 165433d6423SLionel Sambuc return EINVAL; 166433d6423SLionel Sambuc } 167433d6423SLionel Sambuc 168433d6423SLionel Sambuc if (vp->v_sdev == NO_DEV) 169433d6423SLionel Sambuc panic("VFS: read_write tries to access char dev NO_DEV"); 170433d6423SLionel Sambuc 171433d6423SLionel Sambuc dev = vp->v_sdev; 172433d6423SLionel Sambuc 173433d6423SLionel Sambuc r = cdev_io(op, dev, for_e, buf, position, size, f->filp_flags); 174433d6423SLionel Sambuc if (r >= 0) { 175433d6423SLionel Sambuc /* This should no longer happen: all calls are asynchronous. */ 176433d6423SLionel Sambuc printf("VFS: I/O to device %llx succeeded immediately!?\n", dev); 177433d6423SLionel Sambuc cum_io = r; 178433d6423SLionel Sambuc position += r; 179433d6423SLionel Sambuc r = OK; 180433d6423SLionel Sambuc } else if (r == SUSPEND) { 181433d6423SLionel Sambuc /* FIXME: multiple read/write operations on a single filp 182433d6423SLionel Sambuc * should be serialized. They currently aren't; in order to 183433d6423SLionel Sambuc * achieve a similar effect, we optimistically advance the file 184433d6423SLionel Sambuc * position here. This works under the following assumptions: 185433d6423SLionel Sambuc * - character drivers that use the seek position at all, 186433d6423SLionel Sambuc * expose a view of a statically-sized range of bytes, i.e., 187433d6423SLionel Sambuc * they are basically byte-granular block devices; 188433d6423SLionel Sambuc * - if short I/O or an error is returned, all subsequent calls 189433d6423SLionel Sambuc * will return (respectively) EOF and an error; 190433d6423SLionel Sambuc * - the application never checks its own file seek position, 191433d6423SLionel Sambuc * or does not care that it may end up having seeked beyond 192433d6423SLionel Sambuc * the number of bytes it has actually read; 193433d6423SLionel Sambuc * - communication to the character driver is FIFO (this one 194433d6423SLionel Sambuc * is actually true! whew). 195433d6423SLionel Sambuc * Many improvements are possible here, but in the end, 196433d6423SLionel Sambuc * anything short of queuing concurrent operations will be 197433d6423SLionel Sambuc * suboptimal - so we settle for this hack for now. 198433d6423SLionel Sambuc */ 199433d6423SLionel Sambuc position += size; 200433d6423SLionel Sambuc } 201433d6423SLionel Sambuc } else if (S_ISBLK(vp->v_mode)) { /* Block special files. */ 202433d6423SLionel Sambuc if (vp->v_sdev == NO_DEV) 203433d6423SLionel Sambuc panic("VFS: read_write tries to access block dev NO_DEV"); 204433d6423SLionel Sambuc 205433d6423SLionel Sambuc lock_bsf(); 206433d6423SLionel Sambuc 207433d6423SLionel Sambuc if(rw_flag == PEEKING) { 208433d6423SLionel Sambuc r = req_bpeek(vp->v_bfs_e, vp->v_sdev, position, size); 209433d6423SLionel Sambuc } else { 210433d6423SLionel Sambuc r = req_breadwrite(vp->v_bfs_e, for_e, vp->v_sdev, position, 211433d6423SLionel Sambuc size, buf, rw_flag, &res_pos, &res_cum_io); 212433d6423SLionel Sambuc if (r == OK) { 213433d6423SLionel Sambuc position = res_pos; 214433d6423SLionel Sambuc cum_io += res_cum_io; 215433d6423SLionel Sambuc } 216433d6423SLionel Sambuc } 217433d6423SLionel Sambuc 218433d6423SLionel Sambuc unlock_bsf(); 219433d6423SLionel Sambuc } else { /* Regular files */ 220433d6423SLionel Sambuc if (rw_flag == WRITING) { 221433d6423SLionel Sambuc /* Check for O_APPEND flag. */ 222433d6423SLionel Sambuc if (f->filp_flags & O_APPEND) position = vp->v_size; 223433d6423SLionel Sambuc } 224433d6423SLionel Sambuc 225433d6423SLionel Sambuc /* Issue request */ 226433d6423SLionel Sambuc if(rw_flag == PEEKING) { 227433d6423SLionel Sambuc r = req_peek(vp->v_fs_e, vp->v_inode_nr, position, size); 228433d6423SLionel Sambuc } else { 229433d6423SLionel Sambuc off_t new_pos; 230433d6423SLionel Sambuc r = req_readwrite(vp->v_fs_e, vp->v_inode_nr, position, 231433d6423SLionel Sambuc rw_flag, for_e, buf, size, &new_pos, 232433d6423SLionel Sambuc &cum_io_incr); 233433d6423SLionel Sambuc 234433d6423SLionel Sambuc if (r >= 0) { 235433d6423SLionel Sambuc position = new_pos; 236433d6423SLionel Sambuc cum_io += cum_io_incr; 237433d6423SLionel Sambuc } 238433d6423SLionel Sambuc } 239433d6423SLionel Sambuc } 240433d6423SLionel Sambuc 241433d6423SLionel Sambuc /* On write, update file size and access time. */ 242433d6423SLionel Sambuc if (rw_flag == WRITING) { 243433d6423SLionel Sambuc if (S_ISREG(vp->v_mode) || S_ISDIR(vp->v_mode)) { 244433d6423SLionel Sambuc if (position > vp->v_size) { 245433d6423SLionel Sambuc vp->v_size = position; 246433d6423SLionel Sambuc } 247433d6423SLionel Sambuc } 248433d6423SLionel Sambuc } 249433d6423SLionel Sambuc 250433d6423SLionel Sambuc f->filp_pos = position; 251433d6423SLionel Sambuc 252433d6423SLionel Sambuc if (r == EPIPE && rw_flag == WRITING) { 253433d6423SLionel Sambuc /* Process is writing, but there is no reader. Tell the kernel to 254433d6423SLionel Sambuc * generate s SIGPIPE signal. 255433d6423SLionel Sambuc */ 256433d6423SLionel Sambuc if (!(f->filp_flags & O_NOSIGPIPE)) { 257433d6423SLionel Sambuc sys_kill(rfp->fp_endpoint, SIGPIPE); 258433d6423SLionel Sambuc } 259433d6423SLionel Sambuc } 260433d6423SLionel Sambuc 261433d6423SLionel Sambuc if (r == OK) { 262433d6423SLionel Sambuc return(cum_io); 263433d6423SLionel Sambuc } 264433d6423SLionel Sambuc return(r); 265433d6423SLionel Sambuc } 266433d6423SLionel Sambuc 267433d6423SLionel Sambuc /*===========================================================================* 268433d6423SLionel Sambuc * do_getdents * 269433d6423SLionel Sambuc *===========================================================================*/ 270433d6423SLionel Sambuc int do_getdents(void) 271433d6423SLionel Sambuc { 272433d6423SLionel Sambuc /* Perform the getdents(fd, buf, size) system call. */ 273433d6423SLionel Sambuc int r = OK; 274433d6423SLionel Sambuc off_t new_pos; 275433d6423SLionel Sambuc register struct filp *rfilp; 276433d6423SLionel Sambuc 277433d6423SLionel Sambuc scratch(fp).file.fd_nr = job_m_in.m_lc_vfs_readwrite.fd; 278433d6423SLionel Sambuc scratch(fp).io.io_buffer = job_m_in.m_lc_vfs_readwrite.buf; 279433d6423SLionel Sambuc scratch(fp).io.io_nbytes = job_m_in.m_lc_vfs_readwrite.len; 280433d6423SLionel Sambuc 281433d6423SLionel Sambuc /* Is the file descriptor valid? */ 282433d6423SLionel Sambuc if ( (rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_READ)) == NULL) 283433d6423SLionel Sambuc return(err_code); 284433d6423SLionel Sambuc 285433d6423SLionel Sambuc if (!(rfilp->filp_mode & R_BIT)) 286433d6423SLionel Sambuc r = EBADF; 287433d6423SLionel Sambuc else if (!S_ISDIR(rfilp->filp_vno->v_mode)) 288433d6423SLionel Sambuc r = EBADF; 289433d6423SLionel Sambuc 290433d6423SLionel Sambuc if (r == OK) { 291433d6423SLionel Sambuc r = req_getdents(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr, 292433d6423SLionel Sambuc rfilp->filp_pos, scratch(fp).io.io_buffer, 293433d6423SLionel Sambuc scratch(fp).io.io_nbytes, &new_pos, 0); 294433d6423SLionel Sambuc 295433d6423SLionel Sambuc if (r > 0) rfilp->filp_pos = new_pos; 296433d6423SLionel Sambuc } 297433d6423SLionel Sambuc 298433d6423SLionel Sambuc unlock_filp(rfilp); 299433d6423SLionel Sambuc return(r); 300433d6423SLionel Sambuc } 301433d6423SLionel Sambuc 302433d6423SLionel Sambuc 303433d6423SLionel Sambuc /*===========================================================================* 304433d6423SLionel Sambuc * rw_pipe * 305433d6423SLionel Sambuc *===========================================================================*/ 306433d6423SLionel Sambuc int rw_pipe(rw_flag, usr_e, f, buf, req_size) 307433d6423SLionel Sambuc int rw_flag; /* READING or WRITING */ 308433d6423SLionel Sambuc endpoint_t usr_e; 309433d6423SLionel Sambuc struct filp *f; 310433d6423SLionel Sambuc vir_bytes buf; 311433d6423SLionel Sambuc size_t req_size; 312433d6423SLionel Sambuc { 313433d6423SLionel Sambuc int r, oflags, partial_pipe = 0; 314*3c8950ccSBen Gras size_t size, cum_io; 315*3c8950ccSBen Gras size_t cum_io_incr; 316433d6423SLionel Sambuc struct vnode *vp; 317433d6423SLionel Sambuc off_t position, new_pos; 318433d6423SLionel Sambuc 319433d6423SLionel Sambuc /* Must make sure we're operating on locked filp and vnode */ 320433d6423SLionel Sambuc assert(tll_locked_by_me(&f->filp_vno->v_lock)); 321433d6423SLionel Sambuc assert(mutex_trylock(&f->filp_lock) == -EDEADLK); 322433d6423SLionel Sambuc 323433d6423SLionel Sambuc oflags = f->filp_flags; 324433d6423SLionel Sambuc vp = f->filp_vno; 325433d6423SLionel Sambuc position = 0; /* Not actually used */ 326433d6423SLionel Sambuc 327433d6423SLionel Sambuc assert(rw_flag == READING || rw_flag == WRITING); 328433d6423SLionel Sambuc 329433d6423SLionel Sambuc /* fp->fp_cum_io_partial is only nonzero when doing partial writes */ 330433d6423SLionel Sambuc cum_io = fp->fp_cum_io_partial; 331433d6423SLionel Sambuc 332433d6423SLionel Sambuc r = pipe_check(f, rw_flag, oflags, req_size, 0); 333433d6423SLionel Sambuc if (r <= 0) { 334433d6423SLionel Sambuc if (r == SUSPEND) pipe_suspend(f, buf, req_size); 335433d6423SLionel Sambuc return(r); 336433d6423SLionel Sambuc } 337433d6423SLionel Sambuc 338433d6423SLionel Sambuc size = r; 339433d6423SLionel Sambuc if (size < req_size) partial_pipe = 1; 340433d6423SLionel Sambuc 341433d6423SLionel Sambuc /* Truncate read request at size. */ 342433d6423SLionel Sambuc if (rw_flag == READING && size > vp->v_size) { 343433d6423SLionel Sambuc size = vp->v_size; 344433d6423SLionel Sambuc } 345433d6423SLionel Sambuc 346433d6423SLionel Sambuc if (vp->v_mapfs_e == 0) 347433d6423SLionel Sambuc panic("unmapped pipe"); 348433d6423SLionel Sambuc 349433d6423SLionel Sambuc r = req_readwrite(vp->v_mapfs_e, vp->v_mapinode_nr, position, rw_flag, usr_e, 350433d6423SLionel Sambuc buf, size, &new_pos, &cum_io_incr); 351433d6423SLionel Sambuc 352433d6423SLionel Sambuc if (r != OK) { 353433d6423SLionel Sambuc return(r); 354433d6423SLionel Sambuc } 355433d6423SLionel Sambuc 356433d6423SLionel Sambuc cum_io += cum_io_incr; 357433d6423SLionel Sambuc buf += cum_io_incr; 358433d6423SLionel Sambuc req_size -= cum_io_incr; 359433d6423SLionel Sambuc 360f859061eSDavid van Moolenbroek if (rw_flag == READING) 361f859061eSDavid van Moolenbroek vp->v_size -= cum_io_incr; 362f859061eSDavid van Moolenbroek else 363f859061eSDavid van Moolenbroek vp->v_size += cum_io_incr; 364433d6423SLionel Sambuc 365433d6423SLionel Sambuc if (partial_pipe) { 366433d6423SLionel Sambuc /* partial write on pipe with */ 367433d6423SLionel Sambuc /* O_NONBLOCK, return write count */ 368433d6423SLionel Sambuc if (!(oflags & O_NONBLOCK)) { 369433d6423SLionel Sambuc /* partial write on pipe with req_size > PIPE_SIZE, 370433d6423SLionel Sambuc * non-atomic 371433d6423SLionel Sambuc */ 372433d6423SLionel Sambuc fp->fp_cum_io_partial = cum_io; 373433d6423SLionel Sambuc pipe_suspend(f, buf, req_size); 374433d6423SLionel Sambuc return(SUSPEND); 375433d6423SLionel Sambuc } 376433d6423SLionel Sambuc } 377433d6423SLionel Sambuc 378433d6423SLionel Sambuc fp->fp_cum_io_partial = 0; 379433d6423SLionel Sambuc 380433d6423SLionel Sambuc return(cum_io); 381433d6423SLionel Sambuc } 382