xref: /minix/minix/servers/vfs/read.c (revision e3b78ef1)
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