14 #include <minix/vfsif.h>
18 FORWARD
_PROTOTYPE( int rw_chunk
, (struct inode
*rip
, u64_t position
,
19 unsigned off
, int chunk
, unsigned left
, int rw_flag
,
20 char *buff
, int seg
, int usr
, int block_size
, int *completed
));
23 /*===========================================================================*
25 *===========================================================================*/
26 PUBLIC
int fs_readwrite(void)
28 int r
, usr
, seg
, rw_flag
, chunk
, block_size
, block_spec
;
29 int partial_cnt
, regular
, partial_pipe
, nrbytes
;
30 off_t position
, f_size
, bytes_left
;
31 unsigned int off
, cum_io
;
33 int completed
, r2
= OK
;
40 /* Try to get inode according to its index */
41 if (fs_m_in
.REQ_FD_INODE_INDEX
>= 0 &&
42 fs_m_in
.REQ_FD_INODE_INDEX
< NR_INODES
&&
43 inode
[fs_m_in
.REQ_FD_INODE_INDEX
].i_num
== fs_m_in
.REQ_FD_INODE_NR
) {
44 rip
= &inode
[fs_m_in
.REQ_FD_INODE_INDEX
];
47 /* Find the inode referred */
48 rip
= find_inode(fs_dev
, fs_m_in
.REQ_FD_INODE_NR
);
50 printf("FS: unavaliable inode by fs_readwrite(), nr: %d\n",
51 fs_m_in
.REQ_FD_INODE_NR
);
56 mode_word
= rip
->i_mode
& I_TYPE
;
57 regular
= (mode_word
== I_REGULAR
|| mode_word
== I_NAMED_PIPE
);
58 block_spec
= (mode_word
== I_BLOCK_SPECIAL
? 1 : 0);
60 /* Determine blocksize */
61 block_size
= (block_spec
? get_block_size(rip
->i_zone
[0])
62 : rip
->i_sp
->s_block_size
);
64 f_size
= (block_spec
? ULONG_MAX
: rip
->i_size
);
66 /* Get the values from the request message */
67 rw_flag
= (fs_m_in
.m_type
== REQ_READ
? READING
: WRITING
);
68 usr
= fs_m_in
.REQ_FD_WHO_E
;
69 seg
= fs_m_in
.REQ_FD_SEG
;
70 position
= fs_m_in
.REQ_FD_POS
;
71 nrbytes
= (unsigned) fs_m_in
.REQ_FD_NBYTES
;
72 /*partial_cnt = fs_m_in.REQ_FD_PARTIAL;*/
73 user_addr
= fs_m_in
.REQ_FD_USER_ADDR
;
75 /*if (partial_cnt > 0) partial_pipe = 1;*/
77 rdwt_err
= OK
; /* set to EIO if disk error occurs */
79 if (rw_flag
== WRITING
&& block_spec
== 0) {
80 /* Clear the zone containing present EOF if hole about
81 * to be created. This is necessary because all unwritten
82 * blocks prior to the EOF must read as zeros.
84 if (position
> f_size
) clear_zone(rip
, f_size
, 0);
88 /* Split the transfer into chunks that don't span two blocks. */
89 while (nrbytes
!= 0) {
90 off
= (unsigned int) (position
% block_size
);/* offset in blk*/
92 chunk
= MIN(nrbytes
, block_size
- off
);
93 if (chunk
< 0) chunk
= block_size
- off
;
95 if (rw_flag
== READING
) {
96 bytes_left
= f_size
- position
;
97 if (position
>= f_size
) break; /* we are beyond EOF */
98 if (chunk
> bytes_left
) chunk
= (int) bytes_left
;
101 /* Read or write 'chunk' bytes. */
102 r
= rw_chunk(rip
, cvul64(position
), off
, chunk
, (unsigned) nrbytes
,
103 rw_flag
, user_addr
, seg
, usr
, block_size
, &completed
);
105 if (r
!= OK
) break; /* EOF reached */
106 if (rdwt_err
< 0) break;
108 /* Update counters and pointers. */
109 user_addr
+= chunk
; /* user buffer address */
110 nrbytes
-= chunk
; /* bytes yet to be read */
111 cum_io
+= chunk
; /* bytes read so far */
112 position
+= chunk
; /* position within the file */
115 fs_m_out
.RES_FD_POS
= position
; /* It might change later and the VFS has
116 to know this value */
118 /* On write, update file size and access time. */
119 if (rw_flag
== WRITING
) {
120 if (regular
|| mode_word
== I_DIRECTORY
) {
121 if (position
> f_size
) rip
->i_size
= position
;
125 if (rip
->i_pipe
== I_PIPE
) {
126 if ( position
>= rip
->i_size
) {
127 /* Reset pipe pointers. */
128 rip
->i_size
= 0; /* no data left */
129 position
= 0; /* reset reader(s) */
134 /* Check to see if read-ahead is called for, and if so, set it up. */
135 if (rw_flag
== READING
&& rip
->i_seek
== NO_SEEK
&& position
% block_size
== 0
136 && (regular
|| mode_word
== I_DIRECTORY
)) {
138 rdahedpos
= position
;
140 rip
->i_seek
= NO_SEEK
;
142 if (rdwt_err
!= OK
) r
= rdwt_err
; /* check for disk error */
143 if (rdwt_err
== END_OF_FILE
) r
= OK
;
145 /* if user-space copying failed, read/write failed. */
146 if (r
== OK
&& r2
!= OK
) {
151 if (rw_flag
== READING
) rip
->i_update
|= ATIME
;
152 if (rw_flag
== WRITING
) rip
->i_update
|= CTIME
| MTIME
;
153 rip
->i_dirt
= DIRTY
; /* inode is thus now dirty */
156 fs_m_out
.RES_FD_CUM_IO
= cum_io
;
157 fs_m_out
.RES_FD_SIZE
= rip
->i_size
;
163 /*===========================================================================*
165 *===========================================================================*/
166 PUBLIC
int fs_breadwrite(void)
168 int r
, usr
, rw_flag
, chunk
, block_size
;
171 unsigned int off
, cum_io
;
173 int completed
, r2
= OK
;
176 /* Pseudo inode for rw_chunk */
181 /* Get the values from the request message */
182 rw_flag
= (fs_m_in
.m_type
== REQ_BREAD
? READING
: WRITING
);
183 usr
= fs_m_in
.REQ_XFD_WHO_E
;
184 position
= make64(fs_m_in
.REQ_XFD_POS_LO
, fs_m_in
.REQ_XFD_POS_HI
);
185 nrbytes
= (unsigned) fs_m_in
.REQ_XFD_NBYTES
;
186 user_addr
= fs_m_in
.REQ_XFD_USER_ADDR
;
188 block_size
= get_block_size(fs_m_in
.REQ_XFD_BDEV
);
190 rip
.i_zone
[0] = fs_m_in
.REQ_XFD_BDEV
;
191 rip
.i_mode
= I_BLOCK_SPECIAL
;
194 rdwt_err
= OK
; /* set to EIO if disk error occurs */
197 /* Split the transfer into chunks that don't span two blocks. */
198 while (nrbytes
!= 0) {
199 off
= rem64u(position
, block_size
); /* offset in blk*/
201 chunk
= MIN(nrbytes
, block_size
- off
);
202 if (chunk
< 0) chunk
= block_size
- off
;
204 /* Read or write 'chunk' bytes. */
205 r
= rw_chunk(&rip
, position
, off
, chunk
, (unsigned) nrbytes
,
206 rw_flag
, user_addr
, D
, usr
, block_size
, &completed
);
208 if (r
!= OK
) break; /* EOF reached */
209 if (rdwt_err
< 0) break;
211 /* Update counters and pointers. */
212 user_addr
+= chunk
; /* user buffer address */
213 nrbytes
-= chunk
; /* bytes yet to be read */
214 cum_io
+= chunk
; /* bytes read so far */
215 position
= add64ul(position
, chunk
); /* position within the file */
218 fs_m_out
.RES_XFD_POS_LO
= ex64lo(position
);
219 fs_m_out
.RES_XFD_POS_HI
= ex64hi(position
);
221 if (rdwt_err
!= OK
) r
= rdwt_err
; /* check for disk error */
222 if (rdwt_err
== END_OF_FILE
) r
= OK
;
224 fs_m_out
.RES_XFD_CUM_IO
= cum_io
;
230 /*===========================================================================*
232 *===========================================================================*/
233 PRIVATE
int rw_chunk(rip
, position
, off
, chunk
, left
, rw_flag
, buff
,
234 seg
, usr
, block_size
, completed
)
235 register struct inode
*rip
; /* pointer to inode for file to be rd/wr */
236 u64_t position
; /* position within file to read or write */
237 unsigned off
; /* off within the current block */
238 int chunk
; /* number of bytes to read or write */
239 unsigned left
; /* max number of bytes wanted after position */
240 int rw_flag
; /* READING or WRITING */
241 char *buff
; /* virtual address of the user buffer */
242 int seg
; /* T or D segment in user space */
243 int usr
; /* which user process */
244 int block_size
; /* block size of FS operating on */
245 int *completed
; /* number of bytes copied */
247 /* Read or write (part of) a block. */
249 register struct buf
*bp
;
257 block_spec
= (rip
->i_mode
& I_TYPE
) == I_BLOCK_SPECIAL
;
260 b
= div64u(position
, block_size
);
261 dev
= (dev_t
) rip
->i_zone
[0];
264 if (ex64hi(position
) != 0)
265 panic(__FILE__
, "rw_chunk: position too high", NO_NUM
);
266 b
= read_map(rip
, ex64lo(position
));
270 if (!block_spec
&& b
== NO_BLOCK
) {
271 if (rw_flag
== READING
) {
272 /* Reading from a nonexistent block. Must read as all zeros.*/
273 bp
= get_block(NO_DEV
, NO_BLOCK
, NORMAL
); /* get a buffer */
277 /* Writing to a nonexistent block. Create and enter in inode.*/
278 if ((bp
= new_block(rip
, ex64lo(position
))) == NIL_BUF
)
282 else if (rw_flag
== READING
) {
283 /* Read and read ahead if convenient. */
284 bp
= rahead(rip
, b
, position
, left
);
287 /* Normally an existing block to be partially overwritten is first read
288 * in. However, a full block need not be read in. If it is already in
289 * the cache, acquire it, otherwise just acquire a free buffer.
291 n
= (chunk
== block_size
? NO_READ
: NORMAL
);
292 if (!block_spec
&& off
== 0 && ex64lo(position
) >= rip
->i_size
)
294 bp
= get_block(dev
, b
, n
);
297 /* In all cases, bp now points to a valid buffer. */
299 panic(__FILE__
,"bp not valid in rw_chunk, this can't happen", NO_NUM
);
302 if (rw_flag
== WRITING
&& chunk
!= block_size
&& !block_spec
&&
303 ex64lo(position
) >= rip
->i_size
&& off
== 0) {
307 if (rw_flag
== READING
) {
308 /* Copy a chunk from the block buffer to user space. */
309 r
= sys_vircopy(SELF_E
, D
, (phys_bytes
) (bp
->b_data
+off
),
310 usr
, seg
, (phys_bytes
) buff
,
314 /* Copy a chunk from user space to the block buffer. */
315 r
= sys_vircopy(usr
, seg
, (phys_bytes
) buff
,
316 SELF_E
, D
, (phys_bytes
) (bp
->b_data
+off
),
320 n
= (off
+ chunk
== block_size
? FULL_DATA_BLOCK
: PARTIAL_DATA_BLOCK
);
327 /*===========================================================================*
329 *===========================================================================*/
330 PUBLIC block_t
read_map(rip
, position
)
331 register struct inode
*rip
; /* ptr to inode to map from */
332 off_t position
; /* position in file whose blk wanted */
334 /* Given an inode and a position within the corresponding file, locate the
335 * block (not zone) number in which that position is to be found and return it.
338 register struct buf
*bp
;
340 int scale
, boff
, dzones
, nr_indirects
, index
, zind
, ex
;
342 long excess
, zone
, block_pos
;
344 scale
= rip
->i_sp
->s_log_zone_size
; /* for block-zone conversion */
345 block_pos
= position
/rip
->i_sp
->s_block_size
; /* relative blk # in file */
346 zone
= block_pos
>> scale
; /* position's zone */
347 boff
= (int) (block_pos
- (zone
<< scale
) ); /* relative blk # within zone */
348 dzones
= rip
->i_ndzones
;
349 nr_indirects
= rip
->i_nindirs
;
351 /* Is 'position' to be found in the inode itself? */
353 zind
= (int) zone
; /* index should be an int */
354 z
= rip
->i_zone
[zind
];
355 if (z
== NO_ZONE
) return(NO_BLOCK
);
356 b
= ((block_t
) z
<< scale
) + boff
;
360 /* It is not in the inode, so it must be single or double indirect. */
361 excess
= zone
- dzones
; /* first Vx_NR_DZONES don't count */
363 if (excess
< nr_indirects
) {
364 /* 'position' can be located via the single indirect block. */
365 z
= rip
->i_zone
[dzones
];
367 /* 'position' can be located via the double indirect block. */
368 if ( (z
= rip
->i_zone
[dzones
+1]) == NO_ZONE
) return(NO_BLOCK
);
369 excess
-= nr_indirects
; /* single indir doesn't count*/
370 b
= (block_t
) z
<< scale
;
371 bp
= get_block(rip
->i_dev
, b
, NORMAL
); /* get double indirect block */
372 index
= (int) (excess
/nr_indirects
);
373 z
= rd_indir(bp
, index
); /* z= zone for single*/
374 put_block(bp
, INDIRECT_BLOCK
); /* release double ind block */
375 excess
= excess
% nr_indirects
; /* index into single ind blk */
378 /* 'z' is zone num for single indirect block; 'excess' is index into it. */
379 if (z
== NO_ZONE
) return(NO_BLOCK
);
380 b
= (block_t
) z
<< scale
; /* b is blk # for single ind */
381 bp
= get_block(rip
->i_dev
, b
, NORMAL
); /* get single indirect block */
382 ex
= (int) excess
; /* need an integer */
383 z
= rd_indir(bp
, ex
); /* get block pointed to */
384 put_block(bp
, INDIRECT_BLOCK
); /* release single indir blk */
385 if (z
== NO_ZONE
) return(NO_BLOCK
);
386 b
= ((block_t
) z
<< scale
) + boff
;
390 /*===========================================================================*
392 *===========================================================================*/
393 PUBLIC zone_t
rd_indir(bp
, index
)
394 struct buf
*bp
; /* pointer to indirect block */
395 int index
; /* index into *bp */
397 /* Given a pointer to an indirect block, read one entry. The reason for
398 * making a separate routine out of this is that there are four cases:
399 * V1 (IBM and 68000), and V2 (IBM and 68000).
402 struct super_block
*sp
;
403 zone_t zone
; /* V2 zones are longs (shorts in V1) */
406 panic(__FILE__
, "rd_indir() on NIL_BUF", NO_NUM
);
408 sp
= get_super(bp
->b_dev
); /* need super block to find file sys type */
410 /* read a zone from an indirect block */
411 if (sp
->s_version
== V1
)
412 zone
= (zone_t
) conv2(sp
->s_native
, (int) bp
->b_v1_ind
[index
]);
414 zone
= (zone_t
) conv4(sp
->s_native
, (long) bp
->b_v2_ind
[index
]);
416 if (zone
!= NO_ZONE
&&
417 (zone
< (zone_t
) sp
->s_firstdatazone
|| zone
>= sp
->s_zones
)) {
418 printf("Illegal zone number %ld in indirect block, index %d\n",
420 panic(__FILE__
,"check file system", NO_NUM
);
425 /*===========================================================================*
427 *===========================================================================*/
428 PUBLIC
void read_ahead()
430 /* Read a block into the cache before it is needed. */
432 register struct inode
*rip
;
436 rip
= rdahed_inode
; /* pointer to inode to read ahead from */
437 block_size
= get_block_size(rip
->i_dev
);
438 rdahed_inode
= NIL_INODE
; /* turn off read ahead */
439 if ( (b
= read_map(rip
, rdahedpos
)) == NO_BLOCK
) return; /* at EOF */
440 bp
= rahead(rip
, b
, cvul64(rdahedpos
), block_size
);
441 put_block(bp
, PARTIAL_DATA_BLOCK
);
444 /*===========================================================================*
446 *===========================================================================*/
447 PUBLIC
struct buf
*rahead(rip
, baseblock
, position
, bytes_ahead
)
448 register struct inode
*rip
; /* pointer to inode for file to be read */
449 block_t baseblock
; /* block at current position */
450 u64_t position
; /* position within file */
451 unsigned bytes_ahead
; /* bytes beyond position for immediate use */
453 /* Fetch a block from the cache or the device. If a physical read is
454 * required, prefetch as many more blocks as convenient into the cache.
455 * This usually covers bytes_ahead and is at least BLOCKS_MINIMUM.
456 * The device driver may decide it knows better and stop reading at a
457 * cylinder boundary (or after an error). Rw_scattered() puts an optional
458 * flag on all reads to allow this.
461 /* Minimum number of blocks to prefetch. */
462 # define BLOCKS_MINIMUM (NR_BUFS < 50 ? 18 : 32)
463 int block_spec
, scale
, read_q_size
;
464 unsigned int blocks_ahead
, fragment
;
465 block_t block
, blocks_left
;
469 static struct buf
*read_q
[NR_BUFS
];
471 block_spec
= (rip
->i_mode
& I_TYPE
) == I_BLOCK_SPECIAL
;
473 dev
= (dev_t
) rip
->i_zone
[0];
477 block_size
= get_block_size(dev
);
480 bp
= get_block(dev
, block
, PREFETCH
);
481 if (bp
->b_dev
!= NO_DEV
) return(bp
);
483 /* The best guess for the number of blocks to prefetch: A lot.
484 * It is impossible to tell what the device looks like, so we don't even
485 * try to guess the geometry, but leave it to the driver.
487 * The floppy driver can read a full track with no rotational delay, and it
488 * avoids reading partial tracks if it can, so handing it enough buffers to
489 * read two tracks is perfect. (Two, because some diskette types have
490 * an odd number of sectors per track, so a block may span tracks.)
492 * The disk drivers don't try to be smart. With todays disks it is
493 * impossible to tell what the real geometry looks like, so it is best to
494 * read as much as you can. With luck the caching on the drive allows
495 * for a little time to start the next read.
497 * The current solution below is a bit of a hack, it just reads blocks from
498 * the current file position hoping that more of the file can be found. A
499 * better solution must look at the already available zone pointers and
500 * indirect blocks (but don't call read_map!).
503 fragment
= rem64u(position
, block_size
);
504 position
= sub64u(position
, fragment
);
505 bytes_ahead
+= fragment
;
507 blocks_ahead
= (bytes_ahead
+ block_size
- 1) / block_size
;
509 if (block_spec
&& rip
->i_size
== 0) {
510 blocks_left
= NR_IOREQS
;
512 blocks_left
= (rip
->i_size
- ex64lo(position
) + block_size
- 1) /
515 /* Go for the first indirect block if we are in its neighborhood. */
517 scale
= rip
->i_sp
->s_log_zone_size
;
518 ind1_pos
= (off_t
) rip
->i_ndzones
* (block_size
<< scale
);
519 if (ex64lo(position
) <= ind1_pos
&& rip
->i_size
> ind1_pos
) {
526 /* No more than the maximum request. */
527 if (blocks_ahead
> NR_IOREQS
) blocks_ahead
= NR_IOREQS
;
529 /* Read at least the minimum number of blocks, but not after a seek. */
530 if (blocks_ahead
< BLOCKS_MINIMUM
&& rip
->i_seek
== NO_SEEK
)
531 blocks_ahead
= BLOCKS_MINIMUM
;
533 /* Can't go past end of file. */
534 if (blocks_ahead
> blocks_left
) blocks_ahead
= blocks_left
;
538 /* Acquire block buffers. */
540 read_q
[read_q_size
++] = bp
;
542 if (--blocks_ahead
== 0) break;
544 /* Don't trash the cache, leave 4 free. */
545 if (bufs_in_use
>= NR_BUFS
- 4) break;
549 bp
= get_block(dev
, block
, PREFETCH
);
550 if (bp
->b_dev
!= NO_DEV
) {
551 /* Oops, block already in the cache, get out. */
552 put_block(bp
, FULL_DATA_BLOCK
);
556 rw_scattered(dev
, read_q
, read_q_size
, READING
);
557 return(get_block(dev
, baseblock
, NORMAL
));
561 #define GETDENTS_BUFSIZ 257
563 PRIVATE
char getdents_buf
[GETDENTS_BUFSIZ
];
565 /*===========================================================================*
567 *===========================================================================*/
568 PUBLIC
int fs_getdents(void)
570 register struct inode
*rip
;
571 int o
, r
, block_size
, len
, reclen
, done
;
575 size_t size
, tmpbuf_off
, userbuf_off
;
576 off_t pos
, off
, block_pos
, new_pos
, ent_pos
;
582 ino
= fs_m_in
.REQ_GDE_INODE
;
583 gid
= fs_m_in
.REQ_GDE_GRANT
;
584 size
= fs_m_in
.REQ_GDE_SIZE
;
585 pos
= fs_m_in
.REQ_GDE_POS
;
587 /* Check whether the position is properly aligned */
588 if (pos
% DIR_ENTRY_SIZE
)
591 if ( (rip
= get_inode(fs_dev
, ino
)) == NIL_INODE
) {
592 printf("MFS(%d) get_inode by fs_getdents() failed\n", SELF_E
);
596 block_size
= rip
->i_sp
->s_block_size
;
597 off
= (pos
% block_size
); /* Offset in block */
599 done
= FALSE
; /* Stop processing directory blocks
603 tmpbuf_off
= 0; /* Offset in getdents_buf */
604 memset(getdents_buf
, '\0', GETDENTS_BUFSIZ
); /* Avoid leaking any data */
605 userbuf_off
= 0; /* Offset in the user's buffer */
607 /* The default position for the next request is EOF. If the user's buffer
608 * fills up before EOF, new_pos will be modified.
610 new_pos
= rip
->i_size
;
612 for (; block_pos
< rip
->i_size
; block_pos
+= block_size
) {
613 b
= read_map(rip
, block_pos
); /* get block number */
615 /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */
616 bp
= get_block(rip
->i_dev
, b
, NORMAL
); /* get a dir block */
619 panic(__FILE__
,"get_block returned NO_BLOCK", NO_NUM
);
621 /* Search a directory block. */
623 dp
= &bp
->b_dir
[off
/ DIR_ENTRY_SIZE
];
626 for (; dp
< &bp
->b_dir
[NR_DIR_ENTRIES(block_size
)]; dp
++) {
628 continue; /* Entry is not in use */
630 /* Compute the length of the name */
631 cp
= memchr(dp
->d_name
, '\0', NAME_MAX
);
638 /* Compute record length */
639 reclen
= offsetof(struct dirent
, d_name
) + len
+ 1;
640 o
= (reclen
% sizeof(long));
642 reclen
+= sizeof(long)-o
;
644 /* Need the postition of this entry in the directory */
645 ent_pos
= block_pos
+ ((char *)dp
- bp
->b_data
);
647 if (tmpbuf_off
+ reclen
> GETDENTS_BUFSIZ
)
649 r
= sys_safecopyto(FS_PROC_NR
, gid
, userbuf_off
,
650 (vir_bytes
)getdents_buf
, tmpbuf_off
, D
);
654 "fs_getdents: sys_safecopyto failed\n",
658 userbuf_off
+= tmpbuf_off
;
662 if (userbuf_off
+ tmpbuf_off
+ reclen
> size
)
664 /* The user has no space for one more record */
667 /* Record the postion of this entry, it is the
668 * starting point of the next request (unless the
669 * postion is modified with lseek).
675 dep
= (struct dirent
*)&getdents_buf
[tmpbuf_off
];
676 dep
->d_ino
= dp
->d_ino
;
678 dep
->d_reclen
= reclen
;
679 memcpy(dep
->d_name
, dp
->d_name
, len
);
680 dep
->d_name
[len
]= '\0';
681 tmpbuf_off
+= reclen
;
683 put_block(bp
, DIRECTORY_BLOCK
);
690 r
= sys_safecopyto(FS_PROC_NR
, gid
, userbuf_off
,
691 (vir_bytes
)getdents_buf
, tmpbuf_off
, D
);
693 panic(__FILE__
, "fs_getdents: sys_safecopyto failed\n", r
);
695 userbuf_off
+= tmpbuf_off
;
699 fs_m_out
.RES_GDE_POS_CHANGE
= 0; /* No change in case of an error */
700 if (done
&& userbuf_off
== 0)
701 r
= EINVAL
; /* The user's buffer is too small */
706 fs_m_out
.RES_GDE_POS_CHANGE
= new_pos
-pos
;
709 put_inode(rip
); /* release the inode */