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.
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
12 * Jul 2006 (Balazs Gerofi)
18 #include <minix/com.h>
19 #include <minix/u64.h>
26 #include <minix/vfsif.h>
31 /*===========================================================================*
33 *===========================================================================*/
36 return(read_write(READING
));
40 /*===========================================================================*
42 *===========================================================================*/
43 PUBLIC
int read_write(rw_flag
)
44 int rw_flag
; /* READING or WRITING */
46 /* Perform read(fd, buffer, nbytes) or write(fd, buffer, nbytes) call. */
47 register struct filp
*f
;
48 register struct vnode
*vp
;
51 unsigned int off
, cum_io
;
52 int op
, oflags
, r
, chunk
, usr
, seg
, block_spec
, char_spec
;
53 int regular
, partial_pipe
= 0, partial_cnt
= 0;
59 /* Request and response structures */
60 struct readwrite_req req
;
61 struct readwrite_res res
;
63 /* For block spec files */
64 struct breadwrite_req breq
;
66 /* PM loads segments by putting funny things in other bits of the
67 * message, indicated by a high bit in fd. */
68 if (who_e
== PM_PROC_NR
&& (m_in
.fd
& _PM_SEG_FLAG
)) {
69 seg
= (int) m_in
.m1_p2
;
70 usr
= (int) m_in
.m1_p3
;
71 m_in
.fd
&= ~(_PM_SEG_FLAG
); /* get rid of flag bit */
74 usr
= who_e
; /* normal case */
78 /* If the file descriptor is valid, get the vnode, size and mode. */
79 if (m_in
.nbytes
< 0) return(EINVAL
);
80 if ((f
= get_filp(m_in
.fd
)) == NIL_FILP
) return(err_code
);
81 if (((f
->filp_mode
) & (rw_flag
== READING
? R_BIT
: W_BIT
)) == 0) {
82 return(f
->filp_mode
== FILP_CLOSED
? EIO
: EBADF
);
86 return(0); /* so char special files need not check for 0*/
88 /* check if user process has the memory it needs.
89 * if not, copying will fail later.
90 * do this after 0-check above because umap doesn't want to map 0 bytes. */
91 if ((r
= sys_umap(usr
, seg
, (vir_bytes
) m_in
.buffer
, m_in
.nbytes
, &p
)) != OK
)
93 printf("VFS: read_write: umap failed for process %d\n", usr
);
97 position
= f
->filp_pos
;
98 oflags
= f
->filp_flags
;
103 if (vp
->v_pipe
== I_PIPE
) {
104 /* fp->fp_cum_io_partial is only nonzero when doing partial writes */
105 cum_io
= fp
->fp_cum_io_partial
;
111 op
= (rw_flag
== READING
? VFS_DEV_READ
: VFS_DEV_WRITE
);
112 mode_word
= vp
->v_mode
& I_TYPE
;
113 regular
= mode_word
== I_REGULAR
|| mode_word
== I_NAMED_PIPE
;
115 if ((char_spec
= (mode_word
== I_CHAR_SPECIAL
? 1 : 0))) {
116 if (vp
->v_sdev
== NO_DEV
)
117 panic(__FILE__
,"read_write tries to read from "
118 "character device NO_DEV", NO_NUM
);
121 if ((block_spec
= (mode_word
== I_BLOCK_SPECIAL
? 1 : 0))) {
122 if (vp
->v_sdev
== NO_DEV
)
123 panic(__FILE__
,"read_write tries to read from "
124 " block device NO_DEV", NO_NUM
);
127 /* Character special files. */
130 /*dev = (dev_t) f->filp_ino->i_zone[0];*/
131 dev
= (dev_t
) vp
->v_sdev
;
132 r
= dev_io(op
, dev
, usr
, m_in
.buffer
, position
, m_in
.nbytes
, oflags
);
135 position
= add64ul(position
, r
);
139 /* Block special files. */
140 else if (block_spec
) {
141 /* Fill in the fields of the request */
142 breq
.rw_flag
= rw_flag
;
143 breq
.fs_e
= vp
->v_bfs_e
;
144 breq
.blocksize
= vp
->v_blocksize
;
145 breq
.dev
= vp
->v_sdev
;
148 breq
.num_of_bytes
= m_in
.nbytes
;
149 breq
.user_addr
= m_in
.buffer
;
152 r
= req_breadwrite(&breq
, &res
);
154 position
= res
.new_pos
;
155 cum_io
+= res
.cum_io
;
157 /* Regular files (and pipes) */
159 if (rw_flag
== WRITING
&& block_spec
== 0) {
160 /* Check for O_APPEND flag. */
161 if (oflags
& O_APPEND
) position
= cvul64(vp
->v_size
);
163 /* Check in advance to see if file will grow too big. */
164 if (cmp64ul(position
, vp
->v_vmnt
->m_max_file_size
- m_in
.nbytes
) > 0)
169 /* Pipes are a little different. Check. */
170 if (vp
->v_pipe
== I_PIPE
) {
171 r
= pipe_check(vp
, rw_flag
, oflags
,
172 m_in
.nbytes
, position
, &partial_cnt
, 0);
173 if (r
<= 0) return(r
);
176 if (partial_cnt
> 0) {
177 /* So that we don't need to deal with partial count
180 m_in
.nbytes
= MIN(m_in
.nbytes
, partial_cnt
);
184 /* Fill in request structure */
185 req
.fs_e
= vp
->v_fs_e
;
186 req
.rw_flag
= rw_flag
;
187 req
.inode_nr
= vp
->v_inode_nr
;
191 req
.num_of_bytes
= m_in
.nbytes
;
192 req
.user_addr
= m_in
.buffer
;
193 req
.inode_index
= vp
->v_index
;
195 /* Truncate read request at size (mustn't do this for special files). */
196 if((rw_flag
== READING
) &&
197 cmp64ul(add64ul(position
, req
.num_of_bytes
), vp
->v_size
) > 0) {
198 /* Position always should fit in an off_t (LONG_MAX). */
200 assert(cmp64ul(position
, LONG_MAX
) <= 0);
201 pos32
= cv64ul(position
);
203 assert(pos32
<= LONG_MAX
);
204 req
.num_of_bytes
= vp
->v_size
- pos32
;
205 assert(req
.num_of_bytes
>= 0);
209 r
= req_readwrite(&req
, &res
);
213 if (ex64hi(res
.new_pos
))
214 panic(__FILE__
, "read_write: bad new pos", NO_NUM
);
216 position
= res
.new_pos
;
217 cum_io
+= res
.cum_io
;
221 /* On write, update file size and access time. */
222 if (rw_flag
== WRITING
) {
223 if (regular
|| mode_word
== I_DIRECTORY
) {
224 if (cmp64ul(position
, vp
->v_size
) > 0)
226 if (ex64hi(position
) != 0)
229 "read_write: file size too big for v_size",
232 vp
->v_size
= ex64lo(position
);
237 if (vp
->v_pipe
== I_PIPE
) {
238 if (cmp64ul(position
, vp
->v_size
) >= 0) {
239 /* Reset pipe pointers */
242 wf
= find_filp(vp
, W_BIT
);
243 if (wf
!= NIL_FILP
) wf
->filp_pos
= cvu64(0);
248 f
->filp_pos
= position
;
253 /* partial write on pipe with */
254 /* O_NONBLOCK, return write count */
255 if (!(oflags
& O_NONBLOCK
)) {
256 fp
->fp_cum_io_partial
= cum_io
;
257 suspend(XPIPE
); /* partial write on pipe with */
258 return(SUSPEND
); /* nbyte > PIPE_SIZE - non-atomic */
261 fp
->fp_cum_io_partial
= 0;
269 /* Original "uncached" code for block spec files */
271 else if (block_spec
) {
272 char buf
[_MIN_BLOCK_SIZE
];
274 int bleft
= m_in
.nbytes
;
275 dev_t dev
= vp
->v_sdev
;
277 b
= position
/ _MIN_BLOCK_SIZE
;
278 off
= position
% _MIN_BLOCK_SIZE
;
281 /* First read the whole block */
282 r
= dev_bio(VFS_DEV_READ
, dev
, FS_PROC_NR
, buf
,
283 b
* _MIN_BLOCK_SIZE
, _MIN_BLOCK_SIZE
);
285 if (r
!= _MIN_BLOCK_SIZE
)
288 /* How many bytes to copy? */
289 chunk
= MIN(bleft
, _MIN_BLOCK_SIZE
- off
);
291 if (rw_flag
== READING
) {
292 /* Copy a chunk from the buffer to user space. */
293 r
= sys_vircopy(FS_PROC_NR
, D
, (phys_bytes
) (&buf
[off
]),
294 usr
, seg
, (phys_bytes
) m_in
.buffer
,
298 /* Copy a chunk from user space to the buffer. */
299 r
= sys_vircopy(usr
, seg
, (phys_bytes
) m_in
.buffer
,
300 FS_PROC_NR
, D
, (phys_bytes
) (&buf
[off
]),
304 /* Write back if WRITE */
305 if (rw_flag
== WRITING
) {
306 r
= dev_bio(DEV_WRITE
, dev
, FS_PROC_NR
, buf
,
307 b
* _MIN_BLOCK_SIZE
, _MIN_BLOCK_SIZE
);
309 if (r
!= _MIN_BLOCK_SIZE
)
314 m_in
.buffer
+= chunk
;
316 /* 0 offset in the next block */
321 cum_io
= m_in
.nbytes
- bleft
;
328 /*===========================================================================*
330 *===========================================================================*/
331 PUBLIC
int do_getdents()
333 /* Perform the getdents(fd, buf, size) system call. */
337 register struct filp
*rfilp
;
339 /* Is the file descriptor valid? */
340 if ( (rfilp
= get_filp(m_in
.fd
)) == NIL_FILP
) {
344 if (!(rfilp
->filp_mode
& R_BIT
))
347 if ((rfilp
->filp_vno
->v_mode
& I_TYPE
) != I_DIRECTORY
)
350 gid
=cpf_grant_magic(rfilp
->filp_vno
->v_fs_e
, who_e
, (vir_bytes
) m_in
.buffer
,
351 m_in
.nbytes
, CPF_WRITE
);
352 if (gid
< 0) panic(__FILE__
, "cpf_grant_magic failed", gid
);
355 if (ex64hi(rfilp
->filp_pos
) != 0)
356 panic(__FILE__
, "do_getdents: should handle large offsets", NO_NUM
);
358 r
= req_getdents(rfilp
->filp_vno
->v_fs_e
, rfilp
->filp_vno
->v_inode_nr
,
359 ex64lo(rfilp
->filp_pos
), gid
, m_in
.nbytes
, &pos_change
);
364 rfilp
->filp_pos
= add64ul(rfilp
->filp_pos
, pos_change
);