xref: /minix3/minix/fs/mfs/read.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc #include "fs.h"
2*433d6423SLionel Sambuc #include <stddef.h>
3*433d6423SLionel Sambuc #include <string.h>
4*433d6423SLionel Sambuc #include <stdlib.h>
5*433d6423SLionel Sambuc #include <minix/com.h>
6*433d6423SLionel Sambuc #include <minix/u64.h>
7*433d6423SLionel Sambuc #include "buf.h"
8*433d6423SLionel Sambuc #include "inode.h"
9*433d6423SLionel Sambuc #include "super.h"
10*433d6423SLionel Sambuc #include <minix/vfsif.h>
11*433d6423SLionel Sambuc #include <minix/minlib.h>
12*433d6423SLionel Sambuc #include <sys/param.h>
13*433d6423SLionel Sambuc #include <assert.h>
14*433d6423SLionel Sambuc 
15*433d6423SLionel Sambuc 
16*433d6423SLionel Sambuc static struct buf *rahead(struct inode *rip, block_t baseblock, u64_t
17*433d6423SLionel Sambuc 	position, unsigned bytes_ahead);
18*433d6423SLionel Sambuc static int rw_chunk(struct inode *rip, u64_t position, unsigned off,
19*433d6423SLionel Sambuc 	size_t chunk, unsigned left, int rw_flag, cp_grant_id_t gid, unsigned
20*433d6423SLionel Sambuc 	buf_off, unsigned int block_size, int *completed);
21*433d6423SLionel Sambuc 
22*433d6423SLionel Sambuc 
23*433d6423SLionel Sambuc /*===========================================================================*
24*433d6423SLionel Sambuc  *				fs_readwrite				     *
25*433d6423SLionel Sambuc  *===========================================================================*/
26*433d6423SLionel Sambuc int fs_readwrite(void)
27*433d6423SLionel Sambuc {
28*433d6423SLionel Sambuc   int r, rw_flag, block_spec;
29*433d6423SLionel Sambuc   int regular;
30*433d6423SLionel Sambuc   cp_grant_id_t gid;
31*433d6423SLionel Sambuc   off_t position, f_size, bytes_left;
32*433d6423SLionel Sambuc   unsigned int off, cum_io, block_size, chunk;
33*433d6423SLionel Sambuc   mode_t mode_word;
34*433d6423SLionel Sambuc   int completed;
35*433d6423SLionel Sambuc   struct inode *rip;
36*433d6423SLionel Sambuc   size_t nrbytes;
37*433d6423SLionel Sambuc 
38*433d6423SLionel Sambuc   r = OK;
39*433d6423SLionel Sambuc 
40*433d6423SLionel Sambuc   /* Find the inode referred */
41*433d6423SLionel Sambuc   if ((rip = find_inode(fs_dev, fs_m_in.m_vfs_fs_readwrite.inode)) == NULL)
42*433d6423SLionel Sambuc 	return(EINVAL);
43*433d6423SLionel Sambuc 
44*433d6423SLionel Sambuc   mode_word = rip->i_mode & I_TYPE;
45*433d6423SLionel Sambuc   regular = (mode_word == I_REGULAR || mode_word == I_NAMED_PIPE);
46*433d6423SLionel Sambuc   block_spec = (mode_word == I_BLOCK_SPECIAL ? 1 : 0);
47*433d6423SLionel Sambuc 
48*433d6423SLionel Sambuc   /* Determine blocksize */
49*433d6423SLionel Sambuc   if (block_spec) {
50*433d6423SLionel Sambuc 	block_size = get_block_size( (dev_t) rip->i_zone[0]);
51*433d6423SLionel Sambuc 	f_size = MAX_FILE_POS;
52*433d6423SLionel Sambuc   } else {
53*433d6423SLionel Sambuc   	block_size = rip->i_sp->s_block_size;
54*433d6423SLionel Sambuc   	f_size = rip->i_size;
55*433d6423SLionel Sambuc   }
56*433d6423SLionel Sambuc 
57*433d6423SLionel Sambuc   /* Get the values from the request message */
58*433d6423SLionel Sambuc   switch(fs_m_in.m_type) {
59*433d6423SLionel Sambuc   	case REQ_READ: rw_flag = READING; break;
60*433d6423SLionel Sambuc   	case REQ_WRITE: rw_flag = WRITING; break;
61*433d6423SLionel Sambuc   	case REQ_PEEK: rw_flag = PEEKING; break;
62*433d6423SLionel Sambuc 	default: panic("odd request");
63*433d6423SLionel Sambuc   }
64*433d6423SLionel Sambuc   gid = fs_m_in.m_vfs_fs_readwrite.grant;
65*433d6423SLionel Sambuc   position = fs_m_in.m_vfs_fs_readwrite.seek_pos;
66*433d6423SLionel Sambuc   nrbytes = fs_m_in.m_vfs_fs_readwrite.nbytes;
67*433d6423SLionel Sambuc 
68*433d6423SLionel Sambuc   lmfs_reset_rdwt_err();
69*433d6423SLionel Sambuc 
70*433d6423SLionel Sambuc   /* If this is file i/o, check we can write */
71*433d6423SLionel Sambuc   if (rw_flag == WRITING && !block_spec) {
72*433d6423SLionel Sambuc   	  if(rip->i_sp->s_rd_only)
73*433d6423SLionel Sambuc 		  return EROFS;
74*433d6423SLionel Sambuc 
75*433d6423SLionel Sambuc 	  /* Check in advance to see if file will grow too big. */
76*433d6423SLionel Sambuc 	  if (position > (off_t) (rip->i_sp->s_max_size - nrbytes))
77*433d6423SLionel Sambuc 		  return(EFBIG);
78*433d6423SLionel Sambuc 
79*433d6423SLionel Sambuc 	  /* Clear the zone containing present EOF if hole about
80*433d6423SLionel Sambuc 	   * to be created.  This is necessary because all unwritten
81*433d6423SLionel Sambuc 	   * blocks prior to the EOF must read as zeros.
82*433d6423SLionel Sambuc 	   */
83*433d6423SLionel Sambuc 	  if(position > f_size) clear_zone(rip, f_size, 0);
84*433d6423SLionel Sambuc   }
85*433d6423SLionel Sambuc 
86*433d6423SLionel Sambuc   /* If this is block i/o, check we can write */
87*433d6423SLionel Sambuc   if(block_spec && rw_flag == WRITING &&
88*433d6423SLionel Sambuc   	(dev_t) rip->i_zone[0] == superblock.s_dev && superblock.s_rd_only)
89*433d6423SLionel Sambuc 		return EROFS;
90*433d6423SLionel Sambuc 
91*433d6423SLionel Sambuc   cum_io = 0;
92*433d6423SLionel Sambuc   /* Split the transfer into chunks that don't span two blocks. */
93*433d6423SLionel Sambuc   while (nrbytes > 0) {
94*433d6423SLionel Sambuc 	  off = ((unsigned int) position) % block_size; /* offset in blk*/
95*433d6423SLionel Sambuc 	  chunk = min(nrbytes, block_size - off);
96*433d6423SLionel Sambuc 
97*433d6423SLionel Sambuc 	  if (rw_flag == READING) {
98*433d6423SLionel Sambuc 		  bytes_left = f_size - position;
99*433d6423SLionel Sambuc 		  if (position >= f_size) break;	/* we are beyond EOF */
100*433d6423SLionel Sambuc 		  if (chunk > (unsigned int) bytes_left) chunk = bytes_left;
101*433d6423SLionel Sambuc 	  }
102*433d6423SLionel Sambuc 
103*433d6423SLionel Sambuc 	  /* Read or write 'chunk' bytes. */
104*433d6423SLionel Sambuc 	  r = rw_chunk(rip, ((u64_t)((unsigned long)position)), off, chunk,
105*433d6423SLionel Sambuc 	  	       nrbytes, rw_flag, gid, cum_io, block_size, &completed);
106*433d6423SLionel Sambuc 
107*433d6423SLionel Sambuc 	  if (r != OK) break;	/* EOF reached */
108*433d6423SLionel Sambuc 	  if (lmfs_rdwt_err() < 0) break;
109*433d6423SLionel Sambuc 
110*433d6423SLionel Sambuc 	  /* Update counters and pointers. */
111*433d6423SLionel Sambuc 	  nrbytes -= chunk;	/* bytes yet to be read */
112*433d6423SLionel Sambuc 	  cum_io += chunk;	/* bytes read so far */
113*433d6423SLionel Sambuc 	  position += (off_t) chunk;	/* position within the file */
114*433d6423SLionel Sambuc   }
115*433d6423SLionel Sambuc 
116*433d6423SLionel Sambuc   fs_m_out.m_fs_vfs_readwrite.seek_pos = position; /* It might change later and
117*433d6423SLionel Sambuc 						    the VFS has to know this
118*433d6423SLionel Sambuc 						    value */
119*433d6423SLionel Sambuc 
120*433d6423SLionel Sambuc   /* On write, update file size and access time. */
121*433d6423SLionel Sambuc   if (rw_flag == WRITING) {
122*433d6423SLionel Sambuc 	  if (regular || mode_word == I_DIRECTORY) {
123*433d6423SLionel Sambuc 		  if (position > f_size) rip->i_size = position;
124*433d6423SLionel Sambuc 	  }
125*433d6423SLionel Sambuc   }
126*433d6423SLionel Sambuc 
127*433d6423SLionel Sambuc   rip->i_seek = NO_SEEK;
128*433d6423SLionel Sambuc 
129*433d6423SLionel Sambuc   if (lmfs_rdwt_err() != OK) r = lmfs_rdwt_err();	/* check for disk error */
130*433d6423SLionel Sambuc   if (lmfs_rdwt_err() == END_OF_FILE) r = OK;
131*433d6423SLionel Sambuc 
132*433d6423SLionel Sambuc   /* even on a ROFS, writing to a device node on it is fine,
133*433d6423SLionel Sambuc    * just don't update the inode stats for it. And dito for reading.
134*433d6423SLionel Sambuc    */
135*433d6423SLionel Sambuc   if (r == OK && !rip->i_sp->s_rd_only) {
136*433d6423SLionel Sambuc 	  if (rw_flag == READING) rip->i_update |= ATIME;
137*433d6423SLionel Sambuc 	  if (rw_flag == WRITING) rip->i_update |= CTIME | MTIME;
138*433d6423SLionel Sambuc 	  IN_MARKDIRTY(rip);		/* inode is thus now dirty */
139*433d6423SLionel Sambuc   }
140*433d6423SLionel Sambuc 
141*433d6423SLionel Sambuc   fs_m_out.m_fs_vfs_readwrite.nbytes = cum_io;
142*433d6423SLionel Sambuc 
143*433d6423SLionel Sambuc   return(r);
144*433d6423SLionel Sambuc }
145*433d6423SLionel Sambuc 
146*433d6423SLionel Sambuc 
147*433d6423SLionel Sambuc /*===========================================================================*
148*433d6423SLionel Sambuc  *				fs_breadwrite				     *
149*433d6423SLionel Sambuc  *===========================================================================*/
150*433d6423SLionel Sambuc int fs_breadwrite(void)
151*433d6423SLionel Sambuc {
152*433d6423SLionel Sambuc   int r, rw_flag, completed;
153*433d6423SLionel Sambuc   cp_grant_id_t gid;
154*433d6423SLionel Sambuc   u64_t position;
155*433d6423SLionel Sambuc   unsigned int off, cum_io, chunk, block_size;
156*433d6423SLionel Sambuc   size_t nrbytes;
157*433d6423SLionel Sambuc   dev_t target_dev;
158*433d6423SLionel Sambuc 
159*433d6423SLionel Sambuc   /* Pseudo inode for rw_chunk */
160*433d6423SLionel Sambuc   struct inode rip;
161*433d6423SLionel Sambuc 
162*433d6423SLionel Sambuc   r = OK;
163*433d6423SLionel Sambuc 
164*433d6423SLionel Sambuc   target_dev = fs_m_in.m_vfs_fs_breadwrite.device;
165*433d6423SLionel Sambuc 
166*433d6423SLionel Sambuc   /* Get the values from the request message */
167*433d6423SLionel Sambuc   rw_flag = (fs_m_in.m_type == REQ_BREAD ? READING : WRITING);
168*433d6423SLionel Sambuc   gid = fs_m_in.m_vfs_fs_breadwrite.grant;
169*433d6423SLionel Sambuc   position = fs_m_in.m_vfs_fs_breadwrite.seek_pos;
170*433d6423SLionel Sambuc   nrbytes = fs_m_in.m_vfs_fs_breadwrite.nbytes;
171*433d6423SLionel Sambuc 
172*433d6423SLionel Sambuc   block_size = get_block_size(target_dev);
173*433d6423SLionel Sambuc 
174*433d6423SLionel Sambuc   /* Don't block-write to a RO-mounted filesystem. */
175*433d6423SLionel Sambuc   if(superblock.s_dev == target_dev && superblock.s_rd_only)
176*433d6423SLionel Sambuc   	return EROFS;
177*433d6423SLionel Sambuc 
178*433d6423SLionel Sambuc   rip.i_zone[0] = (zone_t) target_dev;
179*433d6423SLionel Sambuc   rip.i_mode = I_BLOCK_SPECIAL;
180*433d6423SLionel Sambuc   rip.i_size = 0;
181*433d6423SLionel Sambuc 
182*433d6423SLionel Sambuc   lmfs_reset_rdwt_err();
183*433d6423SLionel Sambuc 
184*433d6423SLionel Sambuc   cum_io = 0;
185*433d6423SLionel Sambuc   /* Split the transfer into chunks that don't span two blocks. */
186*433d6423SLionel Sambuc   while (nrbytes > 0) {
187*433d6423SLionel Sambuc 	  off = (unsigned int)(position % block_size);	/* offset in blk*/
188*433d6423SLionel Sambuc 	  chunk = min(nrbytes, block_size - off);
189*433d6423SLionel Sambuc 
190*433d6423SLionel Sambuc 	  /* Read or write 'chunk' bytes. */
191*433d6423SLionel Sambuc 	  r = rw_chunk(&rip, position, off, chunk, nrbytes, rw_flag, gid,
192*433d6423SLionel Sambuc 	  	       cum_io, block_size, &completed);
193*433d6423SLionel Sambuc 
194*433d6423SLionel Sambuc 	  if (r != OK) break;	/* EOF reached */
195*433d6423SLionel Sambuc 	  if (lmfs_rdwt_err() < 0) break;
196*433d6423SLionel Sambuc 
197*433d6423SLionel Sambuc 	  /* Update counters and pointers. */
198*433d6423SLionel Sambuc 	  nrbytes -= chunk;	/* bytes yet to be read */
199*433d6423SLionel Sambuc 	  cum_io += chunk;	/* bytes read so far */
200*433d6423SLionel Sambuc 	  position += chunk;	/* position within the file */
201*433d6423SLionel Sambuc   }
202*433d6423SLionel Sambuc 
203*433d6423SLionel Sambuc   fs_m_out.m_fs_vfs_breadwrite.seek_pos = position;
204*433d6423SLionel Sambuc 
205*433d6423SLionel Sambuc   if (lmfs_rdwt_err() != OK) r = lmfs_rdwt_err();	/* check for disk error */
206*433d6423SLionel Sambuc   if (lmfs_rdwt_err() == END_OF_FILE) r = OK;
207*433d6423SLionel Sambuc 
208*433d6423SLionel Sambuc   fs_m_out.m_fs_vfs_breadwrite.nbytes = cum_io;
209*433d6423SLionel Sambuc 
210*433d6423SLionel Sambuc   return(r);
211*433d6423SLionel Sambuc }
212*433d6423SLionel Sambuc 
213*433d6423SLionel Sambuc 
214*433d6423SLionel Sambuc /*===========================================================================*
215*433d6423SLionel Sambuc  *				rw_chunk				     *
216*433d6423SLionel Sambuc  *===========================================================================*/
217*433d6423SLionel Sambuc static int rw_chunk(rip, position, off, chunk, left, rw_flag, gid,
218*433d6423SLionel Sambuc  buf_off, block_size, completed)
219*433d6423SLionel Sambuc register struct inode *rip;	/* pointer to inode for file to be rd/wr */
220*433d6423SLionel Sambuc u64_t position;			/* position within file to read or write */
221*433d6423SLionel Sambuc unsigned off;			/* off within the current block */
222*433d6423SLionel Sambuc unsigned int chunk;		/* number of bytes to read or write */
223*433d6423SLionel Sambuc unsigned left;			/* max number of bytes wanted after position */
224*433d6423SLionel Sambuc int rw_flag;			/* READING, WRITING or PEEKING */
225*433d6423SLionel Sambuc cp_grant_id_t gid;		/* grant */
226*433d6423SLionel Sambuc unsigned buf_off;		/* offset in grant */
227*433d6423SLionel Sambuc unsigned int block_size;	/* block size of FS operating on */
228*433d6423SLionel Sambuc int *completed;			/* number of bytes copied */
229*433d6423SLionel Sambuc {
230*433d6423SLionel Sambuc /* Read or write (part of) a block. */
231*433d6423SLionel Sambuc 
232*433d6423SLionel Sambuc   register struct buf *bp = NULL;
233*433d6423SLionel Sambuc   register int r = OK;
234*433d6423SLionel Sambuc   int n, block_spec;
235*433d6423SLionel Sambuc   block_t b;
236*433d6423SLionel Sambuc   dev_t dev;
237*433d6423SLionel Sambuc   ino_t ino = VMC_NO_INODE;
238*433d6423SLionel Sambuc   u64_t ino_off = rounddown(position, block_size);
239*433d6423SLionel Sambuc 
240*433d6423SLionel Sambuc   /* rw_flag:
241*433d6423SLionel Sambuc    *   READING: read from FS, copy to user
242*433d6423SLionel Sambuc    *   WRITING: copy from user, write to FS
243*433d6423SLionel Sambuc    *   PEEKING: try to get all the blocks into the cache, no copying
244*433d6423SLionel Sambuc    */
245*433d6423SLionel Sambuc 
246*433d6423SLionel Sambuc   *completed = 0;
247*433d6423SLionel Sambuc 
248*433d6423SLionel Sambuc   block_spec = (rip->i_mode & I_TYPE) == I_BLOCK_SPECIAL;
249*433d6423SLionel Sambuc 
250*433d6423SLionel Sambuc   if (block_spec) {
251*433d6423SLionel Sambuc 	b = (unsigned long)(position / block_size);
252*433d6423SLionel Sambuc 	dev = (dev_t) rip->i_zone[0];
253*433d6423SLionel Sambuc   } else {
254*433d6423SLionel Sambuc 	if (ex64hi(position) != 0)
255*433d6423SLionel Sambuc 		panic("rw_chunk: position too high");
256*433d6423SLionel Sambuc 	b = read_map(rip, (off_t) ex64lo(position), 0);
257*433d6423SLionel Sambuc 	dev = rip->i_dev;
258*433d6423SLionel Sambuc 	ino = rip->i_num;
259*433d6423SLionel Sambuc 	assert(ino != VMC_NO_INODE);
260*433d6423SLionel Sambuc   }
261*433d6423SLionel Sambuc 
262*433d6423SLionel Sambuc   if (!block_spec && b == NO_BLOCK) {
263*433d6423SLionel Sambuc 	if (rw_flag == READING) {
264*433d6423SLionel Sambuc 		/* Reading from a nonexistent block.  Must read as all zeros.*/
265*433d6423SLionel Sambuc 		r = sys_safememset(VFS_PROC_NR, gid, (vir_bytes) buf_off,
266*433d6423SLionel Sambuc 			   0, (size_t) chunk);
267*433d6423SLionel Sambuc 		if(r != OK) {
268*433d6423SLionel Sambuc 			printf("MFS: sys_safememset failed\n");
269*433d6423SLionel Sambuc 		}
270*433d6423SLionel Sambuc 		return r;
271*433d6423SLionel Sambuc 	} else {
272*433d6423SLionel Sambuc 		/* Writing to or peeking a nonexistent block.
273*433d6423SLionel Sambuc 		 * Create and enter in inode.
274*433d6423SLionel Sambuc 		 */
275*433d6423SLionel Sambuc 		if ((bp = new_block(rip, (off_t) ex64lo(position))) == NULL)
276*433d6423SLionel Sambuc 			return(err_code);
277*433d6423SLionel Sambuc 	}
278*433d6423SLionel Sambuc   } else if (rw_flag == READING || rw_flag == PEEKING) {
279*433d6423SLionel Sambuc 	/* Read and read ahead if convenient. */
280*433d6423SLionel Sambuc 	bp = rahead(rip, b, position, left);
281*433d6423SLionel Sambuc   } else {
282*433d6423SLionel Sambuc 	/* Normally an existing block to be partially overwritten is first read
283*433d6423SLionel Sambuc 	 * in.  However, a full block need not be read in.  If it is already in
284*433d6423SLionel Sambuc 	 * the cache, acquire it, otherwise just acquire a free buffer.
285*433d6423SLionel Sambuc 	 */
286*433d6423SLionel Sambuc 	n = (chunk == block_size ? NO_READ : NORMAL);
287*433d6423SLionel Sambuc 	if (!block_spec && off == 0 && (off_t) ex64lo(position) >= rip->i_size)
288*433d6423SLionel Sambuc 		n = NO_READ;
289*433d6423SLionel Sambuc 	if(block_spec) {
290*433d6423SLionel Sambuc 		assert(ino == VMC_NO_INODE);
291*433d6423SLionel Sambuc 		bp = get_block(dev, b, n);
292*433d6423SLionel Sambuc 	} else {
293*433d6423SLionel Sambuc 		assert(ino != VMC_NO_INODE);
294*433d6423SLionel Sambuc 		assert(!(ino_off % block_size));
295*433d6423SLionel Sambuc 		bp = lmfs_get_block_ino(dev, b, n, ino, ino_off);
296*433d6423SLionel Sambuc 	}
297*433d6423SLionel Sambuc   }
298*433d6423SLionel Sambuc 
299*433d6423SLionel Sambuc   /* In all cases, bp now points to a valid buffer. */
300*433d6423SLionel Sambuc   assert(bp != NULL);
301*433d6423SLionel Sambuc 
302*433d6423SLionel Sambuc   if (rw_flag == WRITING && chunk != block_size && !block_spec &&
303*433d6423SLionel Sambuc       (off_t) ex64lo(position) >= rip->i_size && off == 0) {
304*433d6423SLionel Sambuc 	zero_block(bp);
305*433d6423SLionel Sambuc   }
306*433d6423SLionel Sambuc 
307*433d6423SLionel Sambuc   if (rw_flag == READING) {
308*433d6423SLionel Sambuc 	/* Copy a chunk from the block buffer to user space. */
309*433d6423SLionel Sambuc 	r = sys_safecopyto(VFS_PROC_NR, gid, (vir_bytes) buf_off,
310*433d6423SLionel Sambuc 			   (vir_bytes) (b_data(bp)+off), (size_t) chunk);
311*433d6423SLionel Sambuc   } else if(rw_flag == WRITING) {
312*433d6423SLionel Sambuc 	/* Copy a chunk from user space to the block buffer. */
313*433d6423SLionel Sambuc 	r = sys_safecopyfrom(VFS_PROC_NR, gid, (vir_bytes) buf_off,
314*433d6423SLionel Sambuc 			     (vir_bytes) (b_data(bp)+off), (size_t) chunk);
315*433d6423SLionel Sambuc 	MARKDIRTY(bp);
316*433d6423SLionel Sambuc   }
317*433d6423SLionel Sambuc 
318*433d6423SLionel Sambuc   n = (off + chunk == block_size ? FULL_DATA_BLOCK : PARTIAL_DATA_BLOCK);
319*433d6423SLionel Sambuc   put_block(bp, n);
320*433d6423SLionel Sambuc 
321*433d6423SLionel Sambuc   return(r);
322*433d6423SLionel Sambuc }
323*433d6423SLionel Sambuc 
324*433d6423SLionel Sambuc 
325*433d6423SLionel Sambuc /*===========================================================================*
326*433d6423SLionel Sambuc  *				read_map				     *
327*433d6423SLionel Sambuc  *===========================================================================*/
328*433d6423SLionel Sambuc block_t read_map(rip, position, opportunistic)
329*433d6423SLionel Sambuc register struct inode *rip;	/* ptr to inode to map from */
330*433d6423SLionel Sambuc off_t position;			/* position in file whose blk wanted */
331*433d6423SLionel Sambuc int opportunistic;		/* if nonzero, only use cache for metadata */
332*433d6423SLionel Sambuc {
333*433d6423SLionel Sambuc /* Given an inode and a position within the corresponding file, locate the
334*433d6423SLionel Sambuc  * block (not zone) number in which that position is to be found and return it.
335*433d6423SLionel Sambuc  */
336*433d6423SLionel Sambuc 
337*433d6423SLionel Sambuc   struct buf *bp;
338*433d6423SLionel Sambuc   zone_t z;
339*433d6423SLionel Sambuc   int scale, boff, index, zind;
340*433d6423SLionel Sambuc   unsigned int dzones, nr_indirects;
341*433d6423SLionel Sambuc   block_t b;
342*433d6423SLionel Sambuc   unsigned long excess, zone, block_pos;
343*433d6423SLionel Sambuc   int iomode = NORMAL;
344*433d6423SLionel Sambuc 
345*433d6423SLionel Sambuc   if(opportunistic) iomode = PREFETCH;
346*433d6423SLionel Sambuc 
347*433d6423SLionel Sambuc   scale = rip->i_sp->s_log_zone_size;	/* for block-zone conversion */
348*433d6423SLionel Sambuc   block_pos = position/rip->i_sp->s_block_size;	/* relative blk # in file */
349*433d6423SLionel Sambuc   zone = block_pos >> scale;	/* position's zone */
350*433d6423SLionel Sambuc   boff = (int) (block_pos - (zone << scale) ); /* relative blk # within zone */
351*433d6423SLionel Sambuc   dzones = rip->i_ndzones;
352*433d6423SLionel Sambuc   nr_indirects = rip->i_nindirs;
353*433d6423SLionel Sambuc 
354*433d6423SLionel Sambuc   /* Is 'position' to be found in the inode itself? */
355*433d6423SLionel Sambuc   if (zone < dzones) {
356*433d6423SLionel Sambuc 	zind = (int) zone;	/* index should be an int */
357*433d6423SLionel Sambuc 	z = rip->i_zone[zind];
358*433d6423SLionel Sambuc 	if (z == NO_ZONE) return(NO_BLOCK);
359*433d6423SLionel Sambuc 	b = (block_t) ((z << scale) + boff);
360*433d6423SLionel Sambuc 	return(b);
361*433d6423SLionel Sambuc   }
362*433d6423SLionel Sambuc 
363*433d6423SLionel Sambuc   /* It is not in the inode, so it must be single or double indirect. */
364*433d6423SLionel Sambuc   excess = zone - dzones;	/* first Vx_NR_DZONES don't count */
365*433d6423SLionel Sambuc 
366*433d6423SLionel Sambuc   if (excess < nr_indirects) {
367*433d6423SLionel Sambuc 	/* 'position' can be located via the single indirect block. */
368*433d6423SLionel Sambuc 	z = rip->i_zone[dzones];
369*433d6423SLionel Sambuc   } else {
370*433d6423SLionel Sambuc 	/* 'position' can be located via the double indirect block. */
371*433d6423SLionel Sambuc 	if ( (z = rip->i_zone[dzones+1]) == NO_ZONE) return(NO_BLOCK);
372*433d6423SLionel Sambuc 	excess -= nr_indirects;			/* single indir doesn't count*/
373*433d6423SLionel Sambuc 	b = (block_t) z << scale;
374*433d6423SLionel Sambuc 	ASSERT(rip->i_dev != NO_DEV);
375*433d6423SLionel Sambuc 	index = (int) (excess/nr_indirects);
376*433d6423SLionel Sambuc 	if ((unsigned int) index > rip->i_nindirs)
377*433d6423SLionel Sambuc 		return(NO_BLOCK);	/* Can't go beyond double indirects */
378*433d6423SLionel Sambuc 	bp = get_block(rip->i_dev, b, iomode); /* get double indirect block */
379*433d6423SLionel Sambuc 	if(opportunistic && lmfs_dev(bp) == NO_DEV) {
380*433d6423SLionel Sambuc 		put_block(bp, INDIRECT_BLOCK);
381*433d6423SLionel Sambuc 		return NO_BLOCK;
382*433d6423SLionel Sambuc 	}
383*433d6423SLionel Sambuc 	ASSERT(lmfs_dev(bp) != NO_DEV);
384*433d6423SLionel Sambuc 	ASSERT(lmfs_dev(bp) == rip->i_dev);
385*433d6423SLionel Sambuc 	z = rd_indir(bp, index);		/* z= zone for single*/
386*433d6423SLionel Sambuc 	put_block(bp, INDIRECT_BLOCK);		/* release double ind block */
387*433d6423SLionel Sambuc 	excess = excess % nr_indirects;		/* index into single ind blk */
388*433d6423SLionel Sambuc   }
389*433d6423SLionel Sambuc 
390*433d6423SLionel Sambuc   /* 'z' is zone num for single indirect block; 'excess' is index into it. */
391*433d6423SLionel Sambuc   if (z == NO_ZONE) return(NO_BLOCK);
392*433d6423SLionel Sambuc   b = (block_t) z << scale;			/* b is blk # for single ind */
393*433d6423SLionel Sambuc   bp = get_block(rip->i_dev, b, iomode);	/* get single indirect block */
394*433d6423SLionel Sambuc   if(opportunistic && lmfs_dev(bp) == NO_DEV) {
395*433d6423SLionel Sambuc 	put_block(bp, INDIRECT_BLOCK);
396*433d6423SLionel Sambuc 	return NO_BLOCK;
397*433d6423SLionel Sambuc   }
398*433d6423SLionel Sambuc   z = rd_indir(bp, (int) excess);		/* get block pointed to */
399*433d6423SLionel Sambuc   put_block(bp, INDIRECT_BLOCK);		/* release single indir blk */
400*433d6423SLionel Sambuc   if (z == NO_ZONE) return(NO_BLOCK);
401*433d6423SLionel Sambuc   b = (block_t) ((z << scale) + boff);
402*433d6423SLionel Sambuc   return(b);
403*433d6423SLionel Sambuc }
404*433d6423SLionel Sambuc 
405*433d6423SLionel Sambuc struct buf *get_block_map(register struct inode *rip, u64_t position)
406*433d6423SLionel Sambuc {
407*433d6423SLionel Sambuc 	block_t b = read_map(rip, position, 0);	/* get block number */
408*433d6423SLionel Sambuc 	int block_size = get_block_size(rip->i_dev);
409*433d6423SLionel Sambuc 	if(b == NO_BLOCK)
410*433d6423SLionel Sambuc 		return NULL;
411*433d6423SLionel Sambuc 	position = rounddown(position, block_size);
412*433d6423SLionel Sambuc 	assert(rip->i_num != VMC_NO_INODE);
413*433d6423SLionel Sambuc 	return lmfs_get_block_ino(rip->i_dev, b, NORMAL, rip->i_num, position);
414*433d6423SLionel Sambuc }
415*433d6423SLionel Sambuc 
416*433d6423SLionel Sambuc /*===========================================================================*
417*433d6423SLionel Sambuc  *				rd_indir				     *
418*433d6423SLionel Sambuc  *===========================================================================*/
419*433d6423SLionel Sambuc zone_t rd_indir(bp, index)
420*433d6423SLionel Sambuc struct buf *bp;			/* pointer to indirect block */
421*433d6423SLionel Sambuc int index;			/* index into *bp */
422*433d6423SLionel Sambuc {
423*433d6423SLionel Sambuc   struct super_block *sp;
424*433d6423SLionel Sambuc   zone_t zone;
425*433d6423SLionel Sambuc 
426*433d6423SLionel Sambuc   if(bp == NULL)
427*433d6423SLionel Sambuc 	panic("rd_indir() on NULL");
428*433d6423SLionel Sambuc 
429*433d6423SLionel Sambuc   sp = get_super(lmfs_dev(bp));	/* need super block to find file sys type */
430*433d6423SLionel Sambuc 
431*433d6423SLionel Sambuc   /* read a zone from an indirect block */
432*433d6423SLionel Sambuc   assert(sp->s_version == V3);
433*433d6423SLionel Sambuc   zone = (zone_t) conv4(sp->s_native, (long) b_v2_ind(bp)[index]);
434*433d6423SLionel Sambuc 
435*433d6423SLionel Sambuc   if (zone != NO_ZONE &&
436*433d6423SLionel Sambuc 		(zone < (zone_t) sp->s_firstdatazone || zone >= sp->s_zones)) {
437*433d6423SLionel Sambuc 	printf("Illegal zone number %ld in indirect block, index %d\n",
438*433d6423SLionel Sambuc 	       (long) zone, index);
439*433d6423SLionel Sambuc 	panic("check file system");
440*433d6423SLionel Sambuc   }
441*433d6423SLionel Sambuc 
442*433d6423SLionel Sambuc   return(zone);
443*433d6423SLionel Sambuc }
444*433d6423SLionel Sambuc 
445*433d6423SLionel Sambuc /*===========================================================================*
446*433d6423SLionel Sambuc  *				rahead					     *
447*433d6423SLionel Sambuc  *===========================================================================*/
448*433d6423SLionel Sambuc static struct buf *rahead(rip, baseblock, position, bytes_ahead)
449*433d6423SLionel Sambuc register struct inode *rip;	/* pointer to inode for file to be read */
450*433d6423SLionel Sambuc block_t baseblock;		/* block at current position */
451*433d6423SLionel Sambuc u64_t position;			/* position within file */
452*433d6423SLionel Sambuc unsigned bytes_ahead;		/* bytes beyond position for immediate use */
453*433d6423SLionel Sambuc {
454*433d6423SLionel Sambuc /* Fetch a block from the cache or the device.  If a physical read is
455*433d6423SLionel Sambuc  * required, prefetch as many more blocks as convenient into the cache.
456*433d6423SLionel Sambuc  * This usually covers bytes_ahead and is at least BLOCKS_MINIMUM.
457*433d6423SLionel Sambuc  * The device driver may decide it knows better and stop reading at a
458*433d6423SLionel Sambuc  * cylinder boundary (or after an error).  Rw_scattered() puts an optional
459*433d6423SLionel Sambuc  * flag on all reads to allow this.
460*433d6423SLionel Sambuc  */
461*433d6423SLionel Sambuc /* Minimum number of blocks to prefetch. */
462*433d6423SLionel Sambuc   int nr_bufs = lmfs_nr_bufs();
463*433d6423SLionel Sambuc # define BLOCKS_MINIMUM		(nr_bufs < 50 ? 18 : 32)
464*433d6423SLionel Sambuc   int block_spec, scale, read_q_size;
465*433d6423SLionel Sambuc   unsigned int blocks_ahead, fragment, block_size;
466*433d6423SLionel Sambuc   block_t block, blocks_left;
467*433d6423SLionel Sambuc   off_t ind1_pos;
468*433d6423SLionel Sambuc   dev_t dev;
469*433d6423SLionel Sambuc   struct buf *bp;
470*433d6423SLionel Sambuc   static unsigned int readqsize = 0;
471*433d6423SLionel Sambuc   static struct buf **read_q;
472*433d6423SLionel Sambuc   u64_t position_running;
473*433d6423SLionel Sambuc   int inuse_before = lmfs_bufs_in_use();
474*433d6423SLionel Sambuc 
475*433d6423SLionel Sambuc   if(readqsize != nr_bufs) {
476*433d6423SLionel Sambuc 	if(readqsize > 0) {
477*433d6423SLionel Sambuc 		assert(read_q != NULL);
478*433d6423SLionel Sambuc 		free(read_q);
479*433d6423SLionel Sambuc 	}
480*433d6423SLionel Sambuc 	if(!(read_q = malloc(sizeof(read_q[0])*nr_bufs)))
481*433d6423SLionel Sambuc 		panic("couldn't allocate read_q");
482*433d6423SLionel Sambuc 	readqsize = nr_bufs;
483*433d6423SLionel Sambuc   }
484*433d6423SLionel Sambuc 
485*433d6423SLionel Sambuc   block_spec = (rip->i_mode & I_TYPE) == I_BLOCK_SPECIAL;
486*433d6423SLionel Sambuc   if (block_spec)
487*433d6423SLionel Sambuc 	dev = (dev_t) rip->i_zone[0];
488*433d6423SLionel Sambuc   else
489*433d6423SLionel Sambuc 	dev = rip->i_dev;
490*433d6423SLionel Sambuc 
491*433d6423SLionel Sambuc   assert(dev != NO_DEV);
492*433d6423SLionel Sambuc 
493*433d6423SLionel Sambuc   block_size = get_block_size(dev);
494*433d6423SLionel Sambuc 
495*433d6423SLionel Sambuc   block = baseblock;
496*433d6423SLionel Sambuc 
497*433d6423SLionel Sambuc   fragment = position % block_size;
498*433d6423SLionel Sambuc   position -= fragment;
499*433d6423SLionel Sambuc   position_running = position;
500*433d6423SLionel Sambuc   bytes_ahead += fragment;
501*433d6423SLionel Sambuc   blocks_ahead = (bytes_ahead + block_size - 1) / block_size;
502*433d6423SLionel Sambuc 
503*433d6423SLionel Sambuc   if(block_spec)
504*433d6423SLionel Sambuc 	  bp = get_block(dev, block, PREFETCH);
505*433d6423SLionel Sambuc   else
506*433d6423SLionel Sambuc 	  bp = lmfs_get_block_ino(dev, block, PREFETCH, rip->i_num, position);
507*433d6423SLionel Sambuc 
508*433d6423SLionel Sambuc   assert(bp != NULL);
509*433d6423SLionel Sambuc   assert(bp->lmfs_count > 0);
510*433d6423SLionel Sambuc   if (lmfs_dev(bp) != NO_DEV) return(bp);
511*433d6423SLionel Sambuc 
512*433d6423SLionel Sambuc   /* The best guess for the number of blocks to prefetch:  A lot.
513*433d6423SLionel Sambuc    * It is impossible to tell what the device looks like, so we don't even
514*433d6423SLionel Sambuc    * try to guess the geometry, but leave it to the driver.
515*433d6423SLionel Sambuc    *
516*433d6423SLionel Sambuc    * The floppy driver can read a full track with no rotational delay, and it
517*433d6423SLionel Sambuc    * avoids reading partial tracks if it can, so handing it enough buffers to
518*433d6423SLionel Sambuc    * read two tracks is perfect.  (Two, because some diskette types have
519*433d6423SLionel Sambuc    * an odd number of sectors per track, so a block may span tracks.)
520*433d6423SLionel Sambuc    *
521*433d6423SLionel Sambuc    * The disk drivers don't try to be smart.  With todays disks it is
522*433d6423SLionel Sambuc    * impossible to tell what the real geometry looks like, so it is best to
523*433d6423SLionel Sambuc    * read as much as you can.  With luck the caching on the drive allows
524*433d6423SLionel Sambuc    * for a little time to start the next read.
525*433d6423SLionel Sambuc    *
526*433d6423SLionel Sambuc    * The current solution below is a bit of a hack, it just reads blocks from
527*433d6423SLionel Sambuc    * the current file position hoping that more of the file can be found.  A
528*433d6423SLionel Sambuc    * better solution must look at the already available zone pointers and
529*433d6423SLionel Sambuc    * indirect blocks (but don't call read_map!).
530*433d6423SLionel Sambuc    */
531*433d6423SLionel Sambuc 
532*433d6423SLionel Sambuc   if (block_spec && rip->i_size == 0) {
533*433d6423SLionel Sambuc 	blocks_left = (block_t) NR_IOREQS;
534*433d6423SLionel Sambuc   } else {
535*433d6423SLionel Sambuc 	blocks_left = (block_t) (rip->i_size-ex64lo(position)+(block_size-1)) /
536*433d6423SLionel Sambuc 								block_size;
537*433d6423SLionel Sambuc 
538*433d6423SLionel Sambuc 	/* Go for the first indirect block if we are in its neighborhood. */
539*433d6423SLionel Sambuc 	if (!block_spec) {
540*433d6423SLionel Sambuc 		scale = rip->i_sp->s_log_zone_size;
541*433d6423SLionel Sambuc 		ind1_pos = (off_t) rip->i_ndzones * (block_size << scale);
542*433d6423SLionel Sambuc 		if ((off_t) ex64lo(position) <= ind1_pos &&
543*433d6423SLionel Sambuc 		     rip->i_size > ind1_pos) {
544*433d6423SLionel Sambuc 			blocks_ahead++;
545*433d6423SLionel Sambuc 			blocks_left++;
546*433d6423SLionel Sambuc 		}
547*433d6423SLionel Sambuc 	}
548*433d6423SLionel Sambuc   }
549*433d6423SLionel Sambuc 
550*433d6423SLionel Sambuc   /* No more than the maximum request. */
551*433d6423SLionel Sambuc   if (blocks_ahead > NR_IOREQS) blocks_ahead = NR_IOREQS;
552*433d6423SLionel Sambuc 
553*433d6423SLionel Sambuc   /* Read at least the minimum number of blocks, but not after a seek. */
554*433d6423SLionel Sambuc   if (blocks_ahead < BLOCKS_MINIMUM && rip->i_seek == NO_SEEK)
555*433d6423SLionel Sambuc 	blocks_ahead = BLOCKS_MINIMUM;
556*433d6423SLionel Sambuc 
557*433d6423SLionel Sambuc   /* Can't go past end of file. */
558*433d6423SLionel Sambuc   if (blocks_ahead > blocks_left) blocks_ahead = blocks_left;
559*433d6423SLionel Sambuc 
560*433d6423SLionel Sambuc   read_q_size = 0;
561*433d6423SLionel Sambuc 
562*433d6423SLionel Sambuc   /* Acquire block buffers. */
563*433d6423SLionel Sambuc   for (;;) {
564*433d6423SLionel Sambuc   	block_t thisblock;
565*433d6423SLionel Sambuc 	assert(bp->lmfs_count > 0);
566*433d6423SLionel Sambuc 	read_q[read_q_size++] = bp;
567*433d6423SLionel Sambuc 
568*433d6423SLionel Sambuc 	if (--blocks_ahead == 0) break;
569*433d6423SLionel Sambuc 
570*433d6423SLionel Sambuc 	/* Don't trash the cache, leave 4 free. */
571*433d6423SLionel Sambuc 	if (lmfs_bufs_in_use() >= nr_bufs - 4) break;
572*433d6423SLionel Sambuc 
573*433d6423SLionel Sambuc 	block++;
574*433d6423SLionel Sambuc 	position_running += block_size;
575*433d6423SLionel Sambuc 
576*433d6423SLionel Sambuc 	if(!block_spec &&
577*433d6423SLionel Sambuc 	  (thisblock = read_map(rip, (off_t) ex64lo(position_running), 1)) != NO_BLOCK) {
578*433d6423SLionel Sambuc 	  	bp = lmfs_get_block_ino(dev, thisblock, PREFETCH, rip->i_num, position_running);
579*433d6423SLionel Sambuc 	} else {
580*433d6423SLionel Sambuc 		bp = get_block(dev, block, PREFETCH);
581*433d6423SLionel Sambuc 	}
582*433d6423SLionel Sambuc 	assert(bp);
583*433d6423SLionel Sambuc 	assert(bp->lmfs_count > 0);
584*433d6423SLionel Sambuc 	if (lmfs_dev(bp) != NO_DEV) {
585*433d6423SLionel Sambuc 		/* Oops, block already in the cache, get out. */
586*433d6423SLionel Sambuc 		put_block(bp, FULL_DATA_BLOCK);
587*433d6423SLionel Sambuc 		break;
588*433d6423SLionel Sambuc 	}
589*433d6423SLionel Sambuc   }
590*433d6423SLionel Sambuc   lmfs_rw_scattered(dev, read_q, read_q_size, READING);
591*433d6423SLionel Sambuc 
592*433d6423SLionel Sambuc   assert(inuse_before == lmfs_bufs_in_use());
593*433d6423SLionel Sambuc 
594*433d6423SLionel Sambuc   if(block_spec)
595*433d6423SLionel Sambuc 	  return get_block(dev, baseblock, NORMAL);
596*433d6423SLionel Sambuc   return(lmfs_get_block_ino(dev, baseblock, NORMAL, rip->i_num, position));
597*433d6423SLionel Sambuc }
598*433d6423SLionel Sambuc 
599*433d6423SLionel Sambuc 
600*433d6423SLionel Sambuc /*===========================================================================*
601*433d6423SLionel Sambuc  *				fs_getdents				     *
602*433d6423SLionel Sambuc  *===========================================================================*/
603*433d6423SLionel Sambuc int fs_getdents(void)
604*433d6423SLionel Sambuc {
605*433d6423SLionel Sambuc #define GETDENTS_BUFSIZE	(sizeof(struct dirent) + MFS_NAME_MAX + 1)
606*433d6423SLionel Sambuc #define GETDENTS_ENTRIES	8
607*433d6423SLionel Sambuc   static char getdents_buf[GETDENTS_BUFSIZE * GETDENTS_ENTRIES];
608*433d6423SLionel Sambuc   register struct inode *rip;
609*433d6423SLionel Sambuc   int o, r, done;
610*433d6423SLionel Sambuc   unsigned int block_size, len, reclen;
611*433d6423SLionel Sambuc   ino_t ino;
612*433d6423SLionel Sambuc   cp_grant_id_t gid;
613*433d6423SLionel Sambuc   size_t size, tmpbuf_off, userbuf_off;
614*433d6423SLionel Sambuc   off_t pos, off, block_pos, new_pos, ent_pos;
615*433d6423SLionel Sambuc   struct buf *bp;
616*433d6423SLionel Sambuc   struct direct *dp;
617*433d6423SLionel Sambuc   struct dirent *dep;
618*433d6423SLionel Sambuc   char *cp;
619*433d6423SLionel Sambuc 
620*433d6423SLionel Sambuc   ino = fs_m_in.m_vfs_fs_getdents.inode;
621*433d6423SLionel Sambuc   gid = fs_m_in.m_vfs_fs_getdents.grant;
622*433d6423SLionel Sambuc   size = fs_m_in.m_vfs_fs_getdents.mem_size;
623*433d6423SLionel Sambuc   pos = fs_m_in.m_vfs_fs_getdents.seek_pos;
624*433d6423SLionel Sambuc 
625*433d6423SLionel Sambuc   /* Check whether the position is properly aligned */
626*433d6423SLionel Sambuc   if( (unsigned int) pos % DIR_ENTRY_SIZE)
627*433d6423SLionel Sambuc 	  return(ENOENT);
628*433d6423SLionel Sambuc 
629*433d6423SLionel Sambuc   if( (rip = get_inode(fs_dev, ino)) == NULL)
630*433d6423SLionel Sambuc 	  return(EINVAL);
631*433d6423SLionel Sambuc 
632*433d6423SLionel Sambuc   block_size = rip->i_sp->s_block_size;
633*433d6423SLionel Sambuc   off = (pos % block_size);		/* Offset in block */
634*433d6423SLionel Sambuc   block_pos = pos - off;
635*433d6423SLionel Sambuc   done = FALSE;		/* Stop processing directory blocks when done is set */
636*433d6423SLionel Sambuc 
637*433d6423SLionel Sambuc   tmpbuf_off = 0;	/* Offset in getdents_buf */
638*433d6423SLionel Sambuc   memset(getdents_buf, '\0', sizeof(getdents_buf));	/* Avoid leaking any data */
639*433d6423SLionel Sambuc   userbuf_off = 0;	/* Offset in the user's buffer */
640*433d6423SLionel Sambuc 
641*433d6423SLionel Sambuc   /* The default position for the next request is EOF. If the user's buffer
642*433d6423SLionel Sambuc    * fills up before EOF, new_pos will be modified. */
643*433d6423SLionel Sambuc   new_pos = rip->i_size;
644*433d6423SLionel Sambuc 
645*433d6423SLionel Sambuc   for(; block_pos < rip->i_size; block_pos += block_size) {
646*433d6423SLionel Sambuc 	/* Since directories don't have holes, 'bp' cannot be NULL. */
647*433d6423SLionel Sambuc 	bp = get_block_map(rip, block_pos);	/* get a dir block */
648*433d6423SLionel Sambuc 	assert(bp != NULL);
649*433d6423SLionel Sambuc 
650*433d6423SLionel Sambuc 	  /* Search a directory block. */
651*433d6423SLionel Sambuc 	  if (block_pos < pos)
652*433d6423SLionel Sambuc 		  dp = &b_dir(bp)[off / DIR_ENTRY_SIZE];
653*433d6423SLionel Sambuc 	  else
654*433d6423SLionel Sambuc 		  dp = &b_dir(bp)[0];
655*433d6423SLionel Sambuc 	  for (; dp < &b_dir(bp)[NR_DIR_ENTRIES(block_size)]; dp++) {
656*433d6423SLionel Sambuc 		  if (dp->mfs_d_ino == 0)
657*433d6423SLionel Sambuc 			  continue;	/* Entry is not in use */
658*433d6423SLionel Sambuc 
659*433d6423SLionel Sambuc 		  /* Compute the length of the name */
660*433d6423SLionel Sambuc 		  cp = memchr(dp->mfs_d_name, '\0', sizeof(dp->mfs_d_name));
661*433d6423SLionel Sambuc 		  if (cp == NULL)
662*433d6423SLionel Sambuc 			  len = sizeof(dp->mfs_d_name);
663*433d6423SLionel Sambuc 		  else
664*433d6423SLionel Sambuc 			  len = cp - (dp->mfs_d_name);
665*433d6423SLionel Sambuc 
666*433d6423SLionel Sambuc 		  /* Compute record length; also does alignment. */
667*433d6423SLionel Sambuc 		  reclen = _DIRENT_RECLEN(dep, len);
668*433d6423SLionel Sambuc 
669*433d6423SLionel Sambuc 		  /* Need the position of this entry in the directory */
670*433d6423SLionel Sambuc 		  ent_pos = block_pos + ((char *) dp - (char *) bp->data);
671*433d6423SLionel Sambuc 
672*433d6423SLionel Sambuc 		if (userbuf_off + tmpbuf_off + reclen >= size) {
673*433d6423SLionel Sambuc 			  /* The user has no space for one more record */
674*433d6423SLionel Sambuc 			  done = TRUE;
675*433d6423SLionel Sambuc 
676*433d6423SLionel Sambuc 			  /* Record the position of this entry, it is the
677*433d6423SLionel Sambuc 			   * starting point of the next request (unless the
678*433d6423SLionel Sambuc 			   * postion is modified with lseek).
679*433d6423SLionel Sambuc 			   */
680*433d6423SLionel Sambuc 			  new_pos = ent_pos;
681*433d6423SLionel Sambuc 			  break;
682*433d6423SLionel Sambuc 		}
683*433d6423SLionel Sambuc 
684*433d6423SLionel Sambuc 		if (tmpbuf_off + reclen >= GETDENTS_BUFSIZE*GETDENTS_ENTRIES) {
685*433d6423SLionel Sambuc 			  r = sys_safecopyto(VFS_PROC_NR, gid,
686*433d6423SLionel Sambuc 			  		     (vir_bytes) userbuf_off,
687*433d6423SLionel Sambuc 					     (vir_bytes) getdents_buf,
688*433d6423SLionel Sambuc 					     (size_t) tmpbuf_off);
689*433d6423SLionel Sambuc 			  if (r != OK) {
690*433d6423SLionel Sambuc 			  	put_inode(rip);
691*433d6423SLionel Sambuc 			  	return(r);
692*433d6423SLionel Sambuc 			  }
693*433d6423SLionel Sambuc 
694*433d6423SLionel Sambuc 			  userbuf_off += tmpbuf_off;
695*433d6423SLionel Sambuc 			  tmpbuf_off = 0;
696*433d6423SLionel Sambuc 		}
697*433d6423SLionel Sambuc 
698*433d6423SLionel Sambuc 		dep = (struct dirent *) &getdents_buf[tmpbuf_off];
699*433d6423SLionel Sambuc 		dep->d_fileno = (ino_t) dp->mfs_d_ino;
700*433d6423SLionel Sambuc 		dep->d_reclen = (unsigned short) reclen;
701*433d6423SLionel Sambuc 		dep->d_namlen = len;
702*433d6423SLionel Sambuc 		memcpy(dep->d_name, dp->mfs_d_name, len);
703*433d6423SLionel Sambuc 		{
704*433d6423SLionel Sambuc 			struct inode *entrip;
705*433d6423SLionel Sambuc 			if(!(entrip = get_inode(fs_dev, dep->d_fileno)))
706*433d6423SLionel Sambuc 				panic("unexpected get_inode failure");
707*433d6423SLionel Sambuc 			dep->d_type = fs_mode_to_type(entrip->i_mode);
708*433d6423SLionel Sambuc 			put_inode(entrip);
709*433d6423SLionel Sambuc 		}
710*433d6423SLionel Sambuc 		dep->d_name[len] = '\0';
711*433d6423SLionel Sambuc 		tmpbuf_off += reclen;
712*433d6423SLionel Sambuc 	}
713*433d6423SLionel Sambuc 
714*433d6423SLionel Sambuc 	put_block(bp, DIRECTORY_BLOCK);
715*433d6423SLionel Sambuc 	if (done)
716*433d6423SLionel Sambuc 		break;
717*433d6423SLionel Sambuc   }
718*433d6423SLionel Sambuc 
719*433d6423SLionel Sambuc   if (tmpbuf_off != 0) {
720*433d6423SLionel Sambuc 	r = sys_safecopyto(VFS_PROC_NR, gid, (vir_bytes) userbuf_off,
721*433d6423SLionel Sambuc 	  		     (vir_bytes) getdents_buf, (size_t) tmpbuf_off);
722*433d6423SLionel Sambuc 	if (r != OK) {
723*433d6423SLionel Sambuc 		put_inode(rip);
724*433d6423SLionel Sambuc 		return(r);
725*433d6423SLionel Sambuc 	}
726*433d6423SLionel Sambuc 
727*433d6423SLionel Sambuc 	userbuf_off += tmpbuf_off;
728*433d6423SLionel Sambuc   }
729*433d6423SLionel Sambuc 
730*433d6423SLionel Sambuc   if (done && userbuf_off == 0)
731*433d6423SLionel Sambuc 	  r = EINVAL;		/* The user's buffer is too small */
732*433d6423SLionel Sambuc   else {
733*433d6423SLionel Sambuc 	  fs_m_out.m_fs_vfs_getdents.nbytes = userbuf_off;
734*433d6423SLionel Sambuc 	  fs_m_out.m_fs_vfs_getdents.seek_pos = new_pos;
735*433d6423SLionel Sambuc 	  if(!rip->i_sp->s_rd_only) {
736*433d6423SLionel Sambuc 		  rip->i_update |= ATIME;
737*433d6423SLionel Sambuc 		  IN_MARKDIRTY(rip);
738*433d6423SLionel Sambuc 	  }
739*433d6423SLionel Sambuc 	  r = OK;
740*433d6423SLionel Sambuc   }
741*433d6423SLionel Sambuc 
742*433d6423SLionel Sambuc   put_inode(rip);		/* release the inode */
743*433d6423SLionel Sambuc   return(r);
744*433d6423SLionel Sambuc }
745*433d6423SLionel Sambuc 
746