xref: /titanic_41/usr/src/uts/common/fs/cachefs/cachefs_dir.c (revision 69bb4bb45c98da60d21839c4dc3c01ea1be60585)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/systm.h>
31 #include <sys/cred.h>
32 #include <sys/proc.h>
33 #include <sys/user.h>
34 #include <sys/vfs.h>
35 #include <sys/vnode.h>
36 #include <sys/pathname.h>
37 #include <sys/uio.h>
38 #include <sys/tiuser.h>
39 #include <sys/sysmacros.h>
40 #include <sys/kmem.h>
41 #include <sys/ioctl.h>
42 #include <sys/statvfs.h>
43 #include <sys/errno.h>
44 #include <sys/debug.h>
45 #include <sys/cmn_err.h>
46 #include <sys/utsname.h>
47 #include <sys/modctl.h>
48 #include <sys/dirent.h>
49 #include <sys/fbuf.h>
50 #include <rpc/types.h>
51 #include <vm/seg.h>
52 #include <vm/faultcode.h>
53 #include <vm/hat.h>
54 #include <vm/seg_map.h>
55 #include <sys/fs/cachefs_fs.h>
56 #include <sys/fs/cachefs_dir.h>
57 #include <sys/fs/cachefs_log.h>
58 
59 /* forward declarations */
60 static int cachefs_dir_getentrys(struct cnode *, u_offset_t, u_offset_t *,
61     uint_t *, uint_t, caddr_t, int *);
62 static int cachefs_dir_stuff(cnode_t *dcp, uint_t count, caddr_t buf,
63     vnode_t *frontvp, u_offset_t *offsetp, u_offset_t *fsizep);
64 static int cachefs_dir_extend(cnode_t *, u_offset_t *, int incr_frontblks);
65 static int cachefs_dir_fill_common(cnode_t *dcp, cred_t *cr,
66     vnode_t *frontvp, vnode_t *backvp, u_offset_t *frontsize);
67 static int cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp,
68     vnode_t *frontvp, cred_t *cr, int acltoo);
69 
70 
71 
72 /*
73  * cachefs_dir_look() called mainly by lookup (and create), looks up the cached
74  * directory for an entry and returns the information there. If the directory
75  * entry doesn't exist return ENOENT, if it is incomplete, return EINVAL.
76  * Should only call this routine if the dir is populated.
77  * Returns ENOTDIR if dir gets nuked because of front file problems.
78  */
79 int
80 cachefs_dir_look(cnode_t *dcp, char *nm, fid_t *cookiep, uint_t *flagp,
81     u_offset_t *d_offsetp, cfs_cid_t *cidp)
82 {
83 	int error;
84 	struct vattr va;
85 	u_offset_t blockoff = 0LL;
86 	uint_t offset = 0; /* offset inside the block of size MAXBSIZE */
87 	vnode_t *dvp;
88 	struct fscache *fscp = C_TO_FSCACHE(dcp);
89 	cachefscache_t *cachep = fscp->fs_cache;
90 	int nmlen;
91 	struct fbuf *fbp;
92 
93 #ifdef CFSDEBUG
94 	CFS_DEBUG(CFSDEBUG_DIR)
95 		printf("cachefs_dir_look: ENTER dcp %p nm %s\n", (void *)dcp,
96 									nm);
97 #endif
98 	ASSERT(CTOV(dcp)->v_type == VDIR);
99 	ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
100 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
101 
102 	if (dcp->c_frontvp == NULL)
103 		(void) cachefs_getfrontfile(dcp);
104 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
105 		error = ENOTDIR;
106 		goto out;
107 	}
108 
109 	dvp = dcp->c_frontvp;
110 	va.va_mask = AT_SIZE;		/* XXX should save dir size */
111 	error = VOP_GETATTR(dvp, &va, 0, kcred);
112 	if (error) {
113 		cachefs_inval_object(dcp);
114 		error = ENOTDIR;
115 		goto out;
116 	}
117 
118 	ASSERT(va.va_size != 0LL);
119 	nmlen = (int)strlen(nm);
120 	while (blockoff < va.va_size) {
121 		offset = 0;
122 		error =
123 		    fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
124 		if (error)
125 			goto out;
126 
127 		while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
128 			struct c_dirent *dep;
129 
130 			dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
131 								offset);
132 			if ((dep->d_flag & CDE_VALID) &&
133 				(nmlen == dep->d_namelen) &&
134 				strcmp(dep->d_name, nm) == 0) {
135 				if (dep->d_flag & CDE_COMPLETE) {
136 					if (cookiep) {
137 						CACHEFS_FID_COPY(&dep->d_cookie,
138 							cookiep);
139 					}
140 					if (flagp)
141 						*flagp = dep->d_flag;
142 					error = 0;
143 				} else {
144 					error = EINVAL;
145 				}
146 				if (cidp)
147 					*cidp = dep->d_id;
148 				if (d_offsetp)
149 					*d_offsetp = offset + blockoff;
150 				fbrelse(fbp, S_OTHER);
151 				goto out;
152 			}
153 			ASSERT(dep->d_length != 0);
154 			offset += dep->d_length;
155 		}
156 		fbrelse(fbp, S_OTHER);
157 		blockoff += MAXBSIZE;
158 	}
159 	error = ENOENT;
160 
161 out:
162 	if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR))
163 		cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp,
164 		    &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno, 0);
165 #ifdef CFSDEBUG
166 	CFS_DEBUG(CFSDEBUG_DIR)
167 		printf("c_dir_look: EXIT error = %d\n", error);
168 #endif
169 	return (error);
170 }
171 
172 /*
173  * creates a new directory and populates it with "." and ".."
174  */
175 int
176 cachefs_dir_new(cnode_t *dcp, cnode_t *cp)
177 {
178 	int		error = 0;
179 	struct c_dirent	*dep;
180 	u_offset_t	size;
181 	int len;
182 	struct fbuf	*fbp;
183 #ifdef CFSDEBUG
184 	struct vattr	va;
185 
186 	CFS_DEBUG(CFSDEBUG_DIR)
187 		printf("c_dir_new: ENTER dcp %p cp %p\n", (void *)dcp,
188 							(void *)cp);
189 #endif
190 
191 	ASSERT(MUTEX_HELD(&cp->c_statelock));
192 	ASSERT(CTOV(cp)->v_type == VDIR);
193 	ASSERT((cp->c_flags & CN_ASYNC_POPULATE) == 0);
194 	ASSERT(CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(dcp)) == 0);
195 
196 	if (cp->c_frontvp == NULL) {
197 		error = cachefs_getfrontfile(cp);
198 		if (error)
199 			goto out;
200 	}
201 
202 #ifdef CFSDEBUG
203 	va.va_mask = AT_SIZE;
204 	error = VOP_GETATTR(cp->c_frontvp, &va, 0, kcred);
205 	if (error)
206 		goto out;
207 	ASSERT(va.va_size == 0);
208 #endif
209 
210 	/*
211 	 * Extend the directory by one MAXBSIZE chunk
212 	 */
213 	size = 0LL;
214 	error = cachefs_dir_extend(cp, &size, 1);
215 	if (error != 0)
216 		goto out;
217 	error = fbread(cp->c_frontvp, (offset_t)0, MAXBSIZE, S_OTHER, &fbp);
218 	if (error)
219 		goto out;
220 
221 	/*
222 	 * Insert "." and ".."
223 	 */
224 	len = (int)CDE_SIZE(".");
225 	dep = (struct c_dirent *)fbp->fb_addr;
226 	dep->d_length = len;
227 	dep->d_offset = (offset_t)len;
228 	dep->d_flag = CDE_VALID | CDE_COMPLETE;
229 	CACHEFS_FID_COPY(&cp->c_cookie, &dep->d_cookie);
230 	dep->d_id = cp->c_id;
231 	dep->d_namelen = 1;
232 	bcopy(".", dep->d_name, 2);
233 
234 	dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + len);
235 	dep->d_length = MAXBSIZE - len;
236 	dep->d_offset = MAXBSIZE;
237 	dep->d_flag = CDE_VALID | CDE_COMPLETE;
238 	CACHEFS_FID_COPY(&dcp->c_cookie, &dep->d_cookie);
239 	dep->d_id = dcp->c_id;
240 	dep->d_namelen = 2;
241 	bcopy("..", dep->d_name, 3);
242 
243 	(void) fbdwrite(fbp);
244 #ifdef INVALREADDIR
245 	cp->c_metadata.md_flags |= MD_POPULATED | MD_INVALREADDIR;
246 #else
247 	cp->c_metadata.md_flags |= MD_POPULATED;
248 #endif
249 	cp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
250 out:
251 #ifdef CFSDEBUG
252 	CFS_DEBUG(CFSDEBUG_DIR)
253 		printf("cachefs_dir_new: EXIT error = %d\n", error);
254 #endif
255 	return (error);
256 }
257 
258 /*
259  * cachefs_dir_enter adds a new directory entry. Takes as input a fid,
260  * fileno and a sync flag. Most of the time, the caller is content with the
261  * write to the (front) directory being done async. The exception being - for
262  * local files, we should make sure that the directory entry is made
263  * synchronously. That is notified by the caller.
264  * 		issync == 0 || issync == SM_ASYNC !
265  *
266  * The new entry is inserted at the end, so that we can generate local offsets
267  * which are compatible with the backfs offsets (which are used when
268  * disconnected.
269  */
270 int
271 cachefs_dir_enter(cnode_t *dcp, char *nm, fid_t *cookiep, cfs_cid_t *cidp,
272     int issync)
273 {
274 	struct vattr	va;
275 	int offset;
276 	u_offset_t blockoff = 0LL;
277 	u_offset_t	prev_offset;
278 	int		error = 0;
279 	vnode_t		*dvp;
280 	struct c_dirent	*dep;
281 	uint_t		esize;
282 	u_offset_t	dirsize;
283 	struct fbuf	*fbp;
284 
285 #ifdef CFSDEBUG
286 	CFS_DEBUG(CFSDEBUG_DIR)
287 		printf("c_dir_enter: ENTER dcp %p nm %s dirflg %x\n",
288 			(void *)dcp, nm, dcp->c_metadata.md_flags);
289 #endif
290 
291 	ASSERT(MUTEX_HELD(&dcp->c_statelock));
292 	ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
293 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
294 	ASSERT(CTOV(dcp)->v_type == VDIR);
295 	ASSERT(issync == 0 || issync == SM_ASYNC);
296 	ASSERT(strlen(nm) <= MAXNAMELEN);
297 
298 	if (dcp->c_frontvp == NULL)
299 		(void) cachefs_getfrontfile(dcp);
300 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
301 		error = ENOTDIR;
302 		goto out;
303 	}
304 	dvp = dcp->c_frontvp;
305 
306 	/*
307 	 * Get the current EOF for the directory(data file)
308 	 */
309 	va.va_mask = AT_SIZE;
310 	error = VOP_GETATTR(dvp, &va, 0, kcred);
311 	if (error) {
312 		cachefs_inval_object(dcp);
313 		error = ENOTDIR;
314 		goto out;
315 	}
316 
317 	/*
318 	 * Get the last block of the directory
319 	 */
320 	dirsize = va.va_size;
321 	ASSERT(dirsize != 0LL);
322 	ASSERT(!(dirsize & MAXBOFFSET));
323 	ASSERT(dirsize <= MAXOFF_T);
324 	blockoff = dirsize - MAXBSIZE;
325 	error = fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
326 	if (error)
327 		goto out;
328 
329 	/*
330 	 * Find the last entry
331 	 */
332 	offset = 0;
333 	prev_offset = blockoff;
334 	for (;;) {
335 		dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + offset);
336 		if (offset + dep->d_length == MAXBSIZE)
337 			break;
338 		prev_offset = dep->d_offset;
339 		offset += dep->d_length;
340 		ASSERT(offset < MAXBSIZE);
341 	}
342 	esize = C_DIRSIZ(dep);
343 
344 	if (dep->d_length - esize >= CDE_SIZE(nm)) {
345 		/*
346 		 * It has room. If the entry is not valid, we can just use
347 		 * it. Otherwise, we need to adjust its length and offset
348 		 */
349 #ifdef CFSDEBUG
350 		CFS_DEBUG(CFSDEBUG_DIR) {
351 			if (prev_offset >= dep->d_offset) {
352 				printf("cachefs_dir_enter: looks like "
353 				    "we might fail the assert\n");
354 				printf("addr %p, offset %x, "
355 				    "prev_offset %llx, dep->d_offset %llx\n",
356 				    (void *)fbp->fb_addr, offset, prev_offset,
357 				    dep->d_offset);
358 				offset = 0;
359 				prev_offset = blockoff;
360 				for (;;) {
361 					dep = (struct c_dirent *)
362 					    ((uintptr_t)fbp->fb_addr + offset);
363 					printf("offset %x, prev_offset %llx\n",
364 					    offset, prev_offset);
365 					printf("dep->d_offset %llx, "
366 					    "dep->d_length %x\n",
367 					    dep->d_offset, dep->d_length);
368 					if (offset + dep->d_length == MAXBSIZE)
369 						break;
370 					prev_offset = dep->d_offset;
371 					offset += dep->d_length;
372 				}
373 			}
374 		}
375 #endif /* CFSDEBUG */
376 
377 		if (offset)
378 			ASSERT(prev_offset < dep->d_offset);
379 		if (dep->d_flag & CDE_VALID) {
380 			dep->d_length = esize;
381 			dep->d_offset = prev_offset + (u_offset_t)esize;
382 			dep = (struct c_dirent *)((uintptr_t)dep + esize);
383 		}
384 		dep->d_length = (int)((offset_t)MAXBSIZE -
385 				((uintptr_t)dep - (uintptr_t)fbp->fb_addr));
386 	} else {
387 		/*
388 		 * No room - so extend the file by one more
389 		 * MAXBSIZE chunk, and fit the entry there.
390 		 */
391 		fbrelse(fbp, S_OTHER);
392 		error = cachefs_dir_extend(dcp, &dirsize, 1);
393 		if (error != 0)
394 			goto out;
395 		error =
396 		    fbread(dvp, (offset_t)va.va_size, MAXBSIZE, S_OTHER, &fbp);
397 		if (error)
398 			goto out;
399 		dep = (struct c_dirent *)fbp->fb_addr;
400 		dep->d_length = MAXBSIZE;
401 	}
402 
403 	/*
404 	 * Fill in the rest of the new entry
405 	 */
406 	dep->d_offset = dirsize;
407 	dep->d_flag = CDE_VALID;
408 	if (cookiep) {
409 		dep->d_flag |= CDE_COMPLETE;
410 		CACHEFS_FID_COPY(cookiep, &dep->d_cookie);
411 	}
412 	dep->d_id = *cidp;
413 	dep->d_namelen = (ushort_t)strlen(nm);
414 	(void) bcopy(nm, dep->d_name, dep->d_namelen + 1);
415 
416 #ifdef INVALREADDIR
417 	dcp->c_metadata.md_flags |= MD_INVALREADDIR;
418 #endif
419 	dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
420 	if (issync)
421 		(void) fbwrite(fbp);
422 	else
423 		(void) fbdwrite(fbp);
424 out:
425 #ifdef CFSDEBUG
426 	CFS_DEBUG(CFSDEBUG_DIR)
427 		printf("cachefs_dir_enter: EXIT error = %d\n", error);
428 #endif
429 	return (error);
430 }
431 
432 /*
433  * Quite simple, if the deleted entry is the first in the MAXBSIZE block,
434  * we simply mark it invalid. Otherwise, the deleted entries d_length is
435  * just added to the previous entry.
436  */
437 int
438 cachefs_dir_rmentry(cnode_t *dcp, char *nm)
439 {
440 	u_offset_t blockoff = 0LL;
441 	int offset = 0;
442 	struct vattr va;
443 	int error = ENOENT;
444 	vnode_t *dvp;
445 	int nmlen;
446 	struct fbuf *fbp;
447 
448 #ifdef CFSDEBUG
449 	CFS_DEBUG(CFSDEBUG_DIR)
450 		printf("cachefs_dir_rmentry: ENTER dcp %p nm %s\n",
451 		    (void *)dcp, nm);
452 #endif
453 	ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
454 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
455 
456 	if (dcp->c_frontvp == NULL)
457 		(void) cachefs_getfrontfile(dcp);
458 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
459 		error = ENOTDIR;
460 		goto out;
461 	}
462 	dvp = dcp->c_frontvp;
463 
464 	ASSERT(CTOV(dcp)->v_type == VDIR);
465 	ASSERT((dcp->c_flags & CN_NOCACHE) == 0);
466 	ASSERT(dvp != NULL);
467 	va.va_mask = AT_SIZE;
468 	error = VOP_GETATTR(dvp, &va, 0, kcred);
469 	if (error) {
470 		cachefs_inval_object(dcp);
471 		error = ENOTDIR;
472 		goto out;
473 	}
474 	ASSERT(va.va_size != 0LL);
475 
476 	nmlen = (int)strlen(nm);
477 	while (blockoff < va.va_size) {
478 		uint_t *last_len;
479 
480 		offset = 0;
481 		last_len = NULL;
482 		error =
483 		    fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
484 		if (error)
485 			goto out;
486 		while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
487 			struct c_dirent *dep;
488 
489 			dep  = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
490 								offset);
491 			if ((dep->d_flag & CDE_VALID) &&
492 				(nmlen == dep->d_namelen) &&
493 				strcmp(dep->d_name, nm) == 0) {
494 				/*
495 				 * Found the entry. If this was the first entry
496 				 * in the MAXBSIZE block, Mark it invalid. Else
497 				 * add it's length to the previous entry's
498 				 * length.
499 				 */
500 				if (last_len == NULL) {
501 					ASSERT(offset == 0);
502 					dep->d_flag = 0;
503 				} else
504 					*last_len += dep->d_length;
505 				(void) fbdwrite(fbp);
506 				dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
507 				goto out;
508 			}
509 			last_len = &dep->d_length;
510 			offset += dep->d_length;
511 		}
512 		fbrelse(fbp, S_OTHER);
513 		blockoff += MAXBSIZE;
514 	}
515 	error = ENOENT;
516 
517 out:
518 #ifdef CFSDEBUG
519 	CFS_DEBUG(CFSDEBUG_DIR)
520 		printf("cachefs_dir_rmentry: EXIT error = %d\n", error);
521 #endif
522 	return (error);
523 }
524 
525 #if 0
526 /*
527  * This function is used only in cachefs_lookup_back() routine but
528  * is inside #if 0 directive in this routine. So I am keeping this
529  * routine also in #if 0 directive.
530  */
531 
532 /*
533  * This function fills in the cookie and file no of the directory entry
534  * at the offset specified by offset - In other words, makes the entry
535  * "complete".
536  */
537 int
538 cachefs_dir_modentry(cnode_t *dcp, u_offset_t offset, fid_t *cookiep,
539     cfs_cid_t *cidp)
540 {
541 	struct c_dirent *dep;
542 	u_offset_t blockoff = (offset & (offset_t)MAXBMASK);
543 	uint_t off = (uint_t)(offset & (offset_t)MAXBOFFSET);
544 	struct fbuf *fbp;
545 	vnode_t *dvp;
546 	int error = 0;
547 
548 #ifdef CFSDEBUG
549 	CFS_DEBUG(CFSDEBUG_DIR)
550 		printf("cachefs_dir_modentry: ENTER dcp %p offset %lld\n",
551 			(void *)dcp, offset);
552 #endif
553 	ASSERT(CTOV(dcp)->v_type == VDIR);
554 	ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
555 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
556 
557 	if (dcp->c_frontvp == NULL)
558 		(void) cachefs_getfrontfile(dcp);
559 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
560 		return;
561 	}
562 	dvp = dcp->c_frontvp;
563 
564 	error = fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
565 	if (error)
566 		goto out;
567 	dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
568 	if (cookiep) {
569 		dep->d_flag |= CDE_COMPLETE;
570 		CACHEFS_FID_COPY(cookiep, &dep->d_cookie);
571 	}
572 	if (cidp)
573 		dep->d_id = *cidp;
574 	(void) fbdwrite(fbp);
575 	dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
576 
577 out:
578 #ifdef CFSDEBUG
579 	CFS_DEBUG(CFSDEBUG_DIR)
580 		printf("cachefs_dir_modentry: EXIT\n");
581 #endif
582 	return (error);
583 }
584 
585 #endif  /* of #if 0 */
586 
587 /*
588  * Called by cachefs_read_dir(). Gets a bunch if directory entries into buf and
589  * packs them into buf.
590  */
591 static int
592 cachefs_dir_getentrys(struct cnode *dcp, u_offset_t beg_off,
593 		u_offset_t *last_offp, uint_t *cntp, uint_t bufsize,
594 				caddr_t buf, int *eofp)
595 {
596 
597 #define	DIR_ENDOFF	0x7fffffffLL
598 
599 	struct vattr va;
600 	struct c_dirent *dep;
601 	struct fbuf *fbp = NULL;
602 	struct dirent64 *gdp;
603 	u_offset_t blockoff;
604 	uint_t off;
605 	int error;
606 	vnode_t *dvp = dcp->c_frontvp;
607 
608 #ifdef CFSDEBUG
609 	CFS_DEBUG(CFSDEBUG_DIR)
610 	printf("cachefs_dir_getentrys: "
611 	    "ENTER dcp %p beg_off %lld mdflags %x cflags %x\n",
612 	    dcp, beg_off, dcp->c_metadata.md_flags, dcp->c_flags);
613 #endif
614 
615 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
616 
617 	/*
618 	 * blockoff has the offset of the MAXBSIZE block that contains the
619 	 * entry  to start with. off contains the offset relative to the
620 	 * begining of the MAXBSIZE block.
621 	 */
622 	if (eofp)
623 		*eofp = 0;
624 	gdp = (struct dirent64 *)buf;
625 	*cntp = bufsize;
626 	va.va_mask = AT_SIZE;
627 	error = VOP_GETATTR(dvp, &va, 0, kcred);
628 	if (error) {
629 		*cntp = 0;
630 		*last_offp = 0;
631 		if (eofp)
632 			*eofp = 1;
633 		goto out;
634 	}
635 	ASSERT(va.va_size != 0LL);
636 
637 	if (beg_off == DIR_ENDOFF) {
638 		*cntp = 0;
639 		*last_offp = DIR_ENDOFF;
640 		if (eofp)
641 			*eofp = 1;
642 		goto out;
643 	}
644 
645 	/*
646 	 * locate the offset where we start reading.
647 	 */
648 	for (blockoff = 0; blockoff < va.va_size; blockoff += MAXBSIZE) {
649 		error =
650 		    fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
651 		if (error)
652 			goto out;
653 		dep = (struct c_dirent *)fbp->fb_addr;
654 		off = 0;
655 		while (off < MAXBSIZE && dep->d_offset <= beg_off) {
656 			off += dep->d_length;
657 			dep =
658 			    (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
659 		}
660 		if (off < MAXBSIZE)
661 			break;
662 		fbrelse(fbp, S_OTHER);
663 		fbp = NULL;
664 	}
665 
666 	if (blockoff >= va.va_size) {
667 		*cntp = 0;
668 		*last_offp = DIR_ENDOFF;
669 		if (eofp)
670 			*eofp = 1;
671 		goto out;
672 	}
673 
674 	/*
675 	 * Just load up the buffer with directory entries.
676 	 */
677 	for (;;) {
678 		uint_t size;
679 		int this_reclen;
680 
681 		ASSERT((uintptr_t)dep < ((uintptr_t)fbp->fb_addr + MAXBSIZE));
682 		if (dep->d_flag & CDE_VALID) {
683 			this_reclen = DIRENT64_RECLEN(dep->d_namelen);
684 			size = C_DIRSIZ(dep);
685 			ASSERT(size < MAXBSIZE);
686 			if (this_reclen > bufsize)
687 				break;
688 			ASSERT(dep->d_namelen <= MAXNAMELEN);
689 			ASSERT(dep->d_offset > (*last_offp));
690 			gdp->d_ino = dep->d_id.cid_fileno;
691 			gdp->d_off = dep->d_offset;
692 
693 			/* use strncpy(9f) to zero out uninitialized bytes */
694 
695 			ASSERT(strlen(dep->d_name) + 1 <=
696 			    DIRENT64_NAMELEN(this_reclen));
697 			(void) strncpy(gdp->d_name, dep->d_name,
698 			    DIRENT64_NAMELEN(this_reclen));
699 
700 			gdp->d_reclen = (ushort_t)this_reclen;
701 			bufsize -= this_reclen;
702 			gdp = (struct dirent64 *)((uintptr_t)gdp +
703 				gdp->d_reclen);
704 			*last_offp = dep->d_offset;
705 		}
706 
707 		/*
708 		 * Increment the offset. If we've hit EOF, fill in
709 		 * the lastoff and current entries d_off field.
710 		 */
711 		off += dep->d_length;
712 		ASSERT(off <= MAXBSIZE);
713 		if ((blockoff + off) >= va.va_size) {
714 			*last_offp = DIR_ENDOFF;
715 			if (eofp)
716 				*eofp = 1;
717 			break;
718 		}
719 		/*
720 		 * If off == MAXBSIZE, then we need to adjust our
721 		 * window to the next MAXBSIZE block of the directory.
722 		 * Adjust blockoff, off and map it in. Also, increment
723 		 * the directory and buffer pointers.
724 		 */
725 		if (off == MAXBSIZE) {
726 			fbrelse(fbp, S_OTHER);
727 			fbp = NULL;
728 			off = 0;
729 			blockoff += MAXBSIZE;
730 			error = fbread(dvp, (offset_t)blockoff, MAXBSIZE,
731 								S_OTHER, &fbp);
732 			if (error)
733 				goto out;
734 		}
735 		dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
736 	}
737 	*cntp -= bufsize;
738 out:
739 	/*
740 	 * Release any buffer and maping that may exist.
741 	 */
742 	if (fbp)
743 		(void) fbrelse(fbp, S_OTHER);
744 #ifdef CFSDEBUG
745 	CFS_DEBUG(CFSDEBUG_DIR)
746 		printf("ccachefs_dir_getentrys: EXIT error = %d\n", error);
747 #endif
748 	return (error);
749 }
750 
751 /*
752  * Called by cachefs_readdir(). Fills a directory request from the cache
753  */
754 int
755 cachefs_dir_read(struct cnode *dcp, struct uio *uiop, int *eofp)
756 {
757 	int error;
758 	uint_t count;
759 	uint_t size;
760 	caddr_t buf;
761 	u_offset_t next = uiop->uio_loffset;
762 	struct fscache *fscp = C_TO_FSCACHE(dcp);
763 	cachefscache_t *cachep = fscp->fs_cache;
764 	caddr_t chrp, end;
765 	dirent64_t *de;
766 
767 	ASSERT(CTOV(dcp)->v_type == VDIR);
768 	ASSERT(RW_READ_HELD(&dcp->c_rwlock));
769 
770 	ASSERT(next <= MAXOFF_T);
771 #ifdef CFSDEBUG
772 	CFS_DEBUG(CFSDEBUG_DIR)
773 		printf("cachefs_dir_read: ENTER dcp %p\n", (void *)dcp);
774 #endif
775 	ASSERT((dcp->c_metadata.md_flags & (MD_FILE|MD_POPULATED)) ==
776 	    (MD_FILE|MD_POPULATED));
777 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
778 
779 	if (dcp->c_frontvp == NULL)
780 		(void) cachefs_getfrontfile(dcp);
781 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
782 		error = ENOTDIR;
783 		goto out;
784 	}
785 
786 	size = (uint_t)uiop->uio_resid;
787 	buf = cachefs_kmem_alloc(size, KM_SLEEP);
788 	error = cachefs_dir_getentrys(dcp, next, &next, &count, size,
789 	    buf, eofp);
790 	if (error == 0 && (int)count > 0) {
791 		ASSERT(count <= size);
792 		if (fscp->fs_inum_size > 0) {
793 			ino64_t newinum;
794 
795 			mutex_exit(&dcp->c_statelock);
796 			mutex_enter(&fscp->fs_fslock);
797 			end = (caddr_t)((uintptr_t)buf + count);
798 			for (chrp = buf; chrp < end; chrp += de->d_reclen) {
799 				de = (dirent64_t *)chrp;
800 
801 				newinum = cachefs_inum_real2fake(fscp,
802 				    de->d_ino);
803 				if (newinum == 0)
804 					newinum = cachefs_fileno_conflict(fscp,
805 					    de->d_ino);
806 				de->d_ino = newinum;
807 			}
808 			mutex_exit(&fscp->fs_fslock);
809 			mutex_enter(&dcp->c_statelock);
810 		}
811 		error = uiomove(buf, count, UIO_READ, uiop);
812 		if (error == 0)
813 			uiop->uio_loffset = next;
814 	}
815 	(void) cachefs_kmem_free(buf, size);
816 out:
817 	if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR))
818 		cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp,
819 		    &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno, 0);
820 #ifdef CFSDEBUG
821 	CFS_DEBUG(CFSDEBUG_DIR)
822 		printf("cachefs_dir_read: EXIT error = %d\n", error);
823 #endif
824 	return (error);
825 }
826 
827 /*
828  * Fully (including cookie) populates the directory from the back filesystem.
829  */
830 int
831 cachefs_dir_fill(cnode_t *dcp, cred_t *cr)
832 {
833 	int error = 0;
834 	u_offset_t frontsize;
835 	struct fscache *fscp = C_TO_FSCACHE(dcp);
836 
837 #ifdef CFSDEBUG
838 	CFS_DEBUG(CFSDEBUG_DIR)
839 		printf("cachefs_dir_fill: ENTER dcp %p\n", (void *)dcp);
840 #endif
841 	ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
842 	ASSERT(MUTEX_HELD(&dcp->c_statelock));
843 
844 	/* XXX for now return success if async populate is scheduled */
845 	if (dcp->c_flags & CN_ASYNC_POPULATE)
846 		goto out;
847 
848 	/* get the back vp */
849 	if (dcp->c_backvp == NULL) {
850 		error = cachefs_getbackvp(fscp, dcp);
851 		if (error) {
852 			goto out;
853 		}
854 	}
855 
856 	/* get the front file vp */
857 	if (dcp->c_frontvp == NULL)
858 		(void) cachefs_getfrontfile(dcp);
859 	if (dcp->c_flags & CN_NOCACHE) {
860 		error = ENOTDIR;
861 		goto out;
862 	}
863 
864 	/* if dir was modified, toss old contents */
865 	if (dcp->c_metadata.md_flags & MD_INVALREADDIR) {
866 		cachefs_inval_object(dcp);
867 		if (dcp->c_flags & CN_NOCACHE) {
868 			error = ENOTDIR;
869 			goto out;
870 		}
871 	}
872 
873 	error = cachefs_dir_fill_common(dcp, cr,
874 	    dcp->c_frontvp, dcp->c_backvp, &frontsize);
875 	if (error == 0)
876 		error = cachefs_dir_complete(fscp, dcp->c_backvp,
877 		    dcp->c_frontvp, cr, 0);
878 	if (error != 0)
879 		goto out;
880 
881 	/*
882 	 * Mark the directory as not empty. Also bang the flag that says that
883 	 * this directory needs to be sync'ed on inactive.
884 	 */
885 	dcp->c_metadata.md_flags |= MD_POPULATED;
886 	dcp->c_metadata.md_flags &= ~MD_INVALREADDIR;
887 	dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
888 	/*LINTED alignment okay*/
889 	dcp->c_metadata.md_frontblks = frontsize / MAXBSIZE;
890 
891 out:
892 	if (error) {
893 #ifdef CFSDEBUG
894 		CFS_DEBUG(CFSDEBUG_INVALIDATE)
895 			printf("c_dir_fill: invalidating %llu\n",
896 			    (u_longlong_t)dcp->c_id.cid_fileno);
897 #endif
898 		cachefs_inval_object(dcp);
899 	}
900 
901 	return (error);
902 }
903 
904 /*
905  * Does work of populating directory.
906  * Must be called while holding dcp->c_statelock
907  */
908 
909 static int
910 cachefs_dir_fill_common(cnode_t *dcp, cred_t *cr,
911     vnode_t *frontvp, vnode_t *backvp, u_offset_t *frontsize)
912 {
913 	int error = 0;
914 	struct uio uio;
915 	struct iovec iov;
916 	caddr_t buf = NULL;
917 	int count;
918 	int eof = 0;
919 	u_offset_t frontoff;
920 	struct fscache *fscp = C_TO_FSCACHE(dcp);
921 	cachefscache_t *cachep = fscp->fs_cache;
922 #ifdef DEBUG
923 	int loop_count = 0;
924 #endif
925 #ifdef CFSDEBUG
926 	CFS_DEBUG(CFSDEBUG_DIR)
927 		printf("cachefs_dir_fill_common: ENTER dcp %p\n", (void *)dcp);
928 #endif
929 
930 	ASSERT(MUTEX_HELD(&dcp->c_statelock));
931 
932 	frontoff = *frontsize = 0LL;
933 
934 	buf = cachefs_kmem_alloc(MAXBSIZE, KM_SLEEP);
935 	uio.uio_iov = &iov;
936 	uio.uio_iovcnt = 1;
937 	uio.uio_segflg = UIO_SYSSPACE;
938 	uio.uio_fmode = 0;
939 	uio.uio_extflg = UIO_COPY_CACHED;
940 	uio.uio_loffset = 0;
941 	for (;;) {
942 #ifdef DEBUG
943 		loop_count++;
944 #endif
945 		/*
946 		 * Read in a buffer's worth of dirents and enter them in to the
947 		 * directory.
948 		 */
949 		uio.uio_resid = MAXBSIZE;
950 		iov.iov_base = buf;
951 		iov.iov_len = MAXBSIZE;
952 		(void) VOP_RWLOCK(backvp, V_WRITELOCK_FALSE, NULL);
953 		error = VOP_READDIR(backvp, &uio, cr, &eof);
954 		VOP_RWUNLOCK(backvp, V_WRITELOCK_FALSE, NULL);
955 		if (error)
956 			goto out;
957 
958 		/*LINTED alignment okay*/
959 		count = MAXBSIZE - (int)uio.uio_resid;
960 		ASSERT(count >= 0);
961 		if (count > 0) {
962 			if (error = cachefs_dir_stuff(dcp, count, buf,
963 			    frontvp, &frontoff, frontsize))
964 				goto out;
965 			ASSERT((*frontsize) != 0LL);
966 		}
967 		if (eof || count == 0)
968 			break;
969 	}
970 
971 	if (*frontsize == 0LL) {
972 		/* keep us from caching an empty directory */
973 		error = EINVAL;
974 		goto out;
975 	}
976 
977 out:
978 	if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_FILLDIR))
979 		cachefs_log_filldir(cachep, error, fscp->fs_cfsvfsp,
980 		    &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno,
981 		    *frontsize);
982 	if (buf)
983 		cachefs_kmem_free(buf, (uint_t)MAXBSIZE);
984 
985 #ifdef CFSDEBUG
986 	CFS_DEBUG(CFSDEBUG_DIR)
987 		printf("cachefs_dir_fill: EXIT error = %d\n", error);
988 #endif
989 	return (error);
990 }
991 
992 /*
993  * If the directory contains only the elements "." and "..", then this returns
994  * 0, otherwise returns an error.
995  */
996 int
997 cachefs_dir_empty(cnode_t *dcp)
998 {
999 	struct vattr va;
1000 	u_offset_t blockoff = 0;
1001 	int offset;
1002 	struct fbuf *fbp;
1003 	int error;
1004 	vnode_t *dvp = dcp->c_frontvp;
1005 
1006 #ifdef CFSDEBUG
1007 	CFS_DEBUG(CFSDEBUG_DIR)
1008 		printf("cachefs_dir_empty: ENTER dcp %p\n", (void *)dcp);
1009 #endif
1010 	ASSERT(CTOV(dcp)->v_type == VDIR);
1011 	ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
1012 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
1013 
1014 	if (dcp->c_frontvp == NULL)
1015 		(void) cachefs_getfrontfile(dcp);
1016 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0)
1017 		return (ENOTDIR);
1018 
1019 	va.va_mask = AT_SIZE;
1020 	error = VOP_GETATTR(dvp, &va, 0, kcred);
1021 	if (error)
1022 		return (ENOTDIR);
1023 
1024 	ASSERT(va.va_size != 0LL);
1025 	while (blockoff < va.va_size) {
1026 		offset = 0;
1027 		error =
1028 		    fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
1029 		if (error)
1030 			return (error);
1031 		while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
1032 			struct c_dirent *dep;
1033 
1034 			dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
1035 								offset);
1036 			if ((dep->d_flag & CDE_VALID) &&
1037 				((strcmp(dep->d_name, ".") != 0) &&
1038 				(strcmp(dep->d_name, "..") != 0))) {
1039 				(void) fbrelse(fbp, S_OTHER);
1040 				return (0);
1041 			}
1042 			offset += dep->d_length;
1043 		}
1044 		(void) fbrelse(fbp, S_OTHER);
1045 		blockoff += MAXBSIZE;
1046 	}
1047 	return (EEXIST);
1048 }
1049 
1050 /*
1051  * Called by cachefs_dir_fill() to stuff a buffer of dir entries into
1052  * a front file.  This is more efficient than repeated calls to
1053  * cachefs_dir_enter, and it also allows us to maintain entries in backfs
1054  * order (readdir requires that entry offsets be ascending).
1055  */
1056 static int
1057 cachefs_dir_stuff(cnode_t *dcp, uint_t count, caddr_t buf,
1058     vnode_t *frontvp, u_offset_t *offsetp, u_offset_t *fsizep)
1059 {
1060 	int error = 0;
1061 	struct fbuf *fbp;
1062 	struct c_dirent *cdep, *last;
1063 	struct dirent64 *dep;
1064 	int inblk, entsize;
1065 	u_offset_t blockoff = (*offsetp & (offset_t)MAXBMASK);
1066 	/*LINTED alignment okay*/
1067 	uint_t off = (uint_t)(*offsetp & (offset_t)MAXBOFFSET);
1068 
1069 	/*LINTED want count != 0*/
1070 	ASSERT(count > 0);
1071 
1072 	if (*offsetp >= *fsizep) {
1073 		error = cachefs_dir_extend(dcp, fsizep, 0);
1074 		if (error)
1075 			return (error);
1076 	}
1077 
1078 	ASSERT(*fsizep != 0LL);
1079 	last = NULL;
1080 	error = fbread(frontvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
1081 	if (error)
1082 		return (error);
1083 	cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
1084 	inblk = MAXBSIZE-off;
1085 	if (*offsetp != 0) {
1086 		ASSERT(cdep->d_length == inblk);
1087 		inblk -= C_DIRSIZ(cdep);
1088 		last = cdep;
1089 		last->d_length -= inblk;
1090 		off += last->d_length;
1091 		cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
1092 	}
1093 	dep = (struct dirent64 *)buf;
1094 	/*LINTED want count != 0*/
1095 	while (count > 0) {
1096 		if (last) {
1097 			ASSERT(dep->d_off > last->d_offset);
1098 		}
1099 		entsize = (int)CDE_SIZE(dep->d_name);
1100 		if (entsize > inblk) {
1101 			if (last) {
1102 				last->d_length += inblk;
1103 			}
1104 			(void) fbwrite(fbp);
1105 			error = cachefs_dir_extend(dcp, fsizep, 0);
1106 			if (error)
1107 				return (error);
1108 			ASSERT(*fsizep != 0LL);
1109 			blockoff += MAXBSIZE;
1110 			error = fbread(frontvp, (offset_t)blockoff, MAXBSIZE,
1111 							S_OTHER, &fbp);
1112 			if (error)
1113 				return (error);
1114 			off = 0;
1115 			cdep = (struct c_dirent *)fbp->fb_addr;
1116 			inblk = MAXBSIZE;
1117 			last = NULL;
1118 		}
1119 		cdep->d_length = entsize;
1120 		cdep->d_id.cid_fileno = dep->d_ino;
1121 		cdep->d_id.cid_flags = 0;
1122 		cdep->d_namelen = (ushort_t)strlen(dep->d_name);
1123 		cdep->d_flag = CDE_VALID;
1124 		bcopy(dep->d_name, cdep->d_name, cdep->d_namelen+1);
1125 		cdep->d_offset = dep->d_off;
1126 		inblk -= entsize;
1127 		count -= dep->d_reclen;
1128 		dep = (struct dirent64 *)((uintptr_t)dep + dep->d_reclen);
1129 		*offsetp = blockoff + (u_offset_t)off;
1130 		off += entsize;
1131 		last = cdep;
1132 		cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
1133 	}
1134 	if (last) {
1135 		last->d_length += inblk;
1136 	}
1137 	(void) fbwrite(fbp);
1138 	return (error);
1139 }
1140 
1141 static int
1142 cachefs_dir_extend(cnode_t *dcp, u_offset_t *cursize, int incr_frontblks)
1143 {
1144 	struct vattr va;
1145 	cachefscache_t *cachep = C_TO_FSCACHE(dcp)->fs_cache;
1146 	int error = 0;
1147 	struct fscache *fscp = VFS_TO_FSCACHE(CTOV(dcp)->v_vfsp);
1148 
1149 	ASSERT(MUTEX_HELD(&dcp->c_statelock));
1150 	ASSERT(((*cursize) & (MAXBSIZE-1)) == 0);
1151 
1152 	va.va_mask = AT_SIZE;
1153 	va.va_size = (u_offset_t)(*cursize + MAXBSIZE);
1154 	error = cachefs_allocblocks(cachep, 1, dcp->c_metadata.md_rltype);
1155 	if (error)
1156 		return (error);
1157 	error = VOP_SETATTR(dcp->c_frontvp, &va, 0, kcred, NULL);
1158 	if (error) {
1159 		cachefs_freeblocks(cachep, 1, dcp->c_metadata.md_rltype);
1160 		return (error);
1161 	}
1162 	if (incr_frontblks)
1163 		dcp->c_metadata.md_frontblks++;
1164 	if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
1165 		dcp->c_size += MAXBSIZE;
1166 		dcp->c_attr.va_size = dcp->c_size;
1167 	}
1168 	*cursize += MAXBSIZE;
1169 	ASSERT(*cursize != 0LL);
1170 	if (incr_frontblks)
1171 		dcp->c_flags |= CN_UPDATED;
1172 	return (0);
1173 }
1174 
1175 int
1176 cachefs_async_populate_dir(struct cachefs_populate_req *pop, cred_t *cr,
1177     vnode_t *backvp, vnode_t *frontvp)
1178 {
1179 	vnode_t *dvp = pop->cpop_vp;
1180 	struct cnode *dcp = VTOC(dvp);
1181 	u_offset_t frontsize;
1182 	int error = 0;
1183 
1184 #ifdef CFSDEBUG
1185 	CFS_DEBUG(CFSDEBUG_DIR)
1186 		printf("cachefs_async_populate_dir: ENTER dvp %p\n",
1187 								(void *)dvp);
1188 #endif
1189 	ASSERT(MUTEX_HELD(&dcp->c_statelock));
1190 	ASSERT(dvp->v_type == VDIR);
1191 	ASSERT((dcp->c_metadata.md_flags & MD_POPULATED) == 0);
1192 	ASSERT(dcp->c_frontvp == frontvp);
1193 	ASSERT(dcp->c_backvp == backvp);
1194 	ASSERT(CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(dcp)) == 0);
1195 
1196 	/* if dir was modified, toss old contents */
1197 	if (dcp->c_metadata.md_flags & MD_INVALREADDIR) {
1198 		cachefs_inval_object(dcp);
1199 		if (dcp->c_flags & CN_NOCACHE) {
1200 			error = ENOTDIR;
1201 			goto out;
1202 		} else {
1203 			dcp->c_metadata.md_flags &= ~MD_INVALREADDIR;
1204 		}
1205 	}
1206 
1207 
1208 	error = cachefs_dir_fill_common(dcp, cr, frontvp, backvp, &frontsize);
1209 	if (error != 0)
1210 		goto out;
1211 	ASSERT(frontsize != 0LL);
1212 	mutex_exit(&dcp->c_statelock);
1213 	/*
1214 	 * I don't like to break lock here but cachefs_dir_complete()
1215 	 * needs it.
1216 	 */
1217 	error = cachefs_dir_complete(C_TO_FSCACHE(dcp), backvp,
1218 	    frontvp, cr, 1);
1219 	mutex_enter(&dcp->c_statelock);
1220 	if (error != 0)
1221 		goto out;
1222 	/* if went nocache while lock was dropped, get out */
1223 	if ((dcp->c_flags & CN_NOCACHE) || (dcp->c_frontvp == NULL)) {
1224 		error = EINVAL;
1225 	} else {
1226 		/* allocfile and allocblocks have already happened. */
1227 		dcp->c_metadata.md_frontblks = frontsize / MAXBSIZE;
1228 	}
1229 
1230 out:
1231 
1232 #ifdef CFSDEBUG
1233 	CFS_DEBUG(CFSDEBUG_DIR)
1234 		printf("cachefs_async_populate_dir: EXIT error = %d\n", error);
1235 #endif
1236 
1237 	return (error);
1238 }
1239 
1240 static int
1241 cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp, vnode_t *frontvp,
1242     cred_t *cr, int acltoo)
1243 {
1244 	struct c_dirent *dep;
1245 	caddr_t buf = kmem_alloc(MAXBSIZE, KM_SLEEP);
1246 	struct vattr va;
1247 	u_offset_t blockoff;
1248 	int offset;
1249 	u_offset_t dir_size;
1250 	struct fbuf *fbp;
1251 	cnode_t *cp;
1252 	fid_t cookie;
1253 	vnode_t *entry_vp;
1254 	int error = 0;
1255 
1256 	/*
1257 	 * note: caller better not hold a c_statelock if acltoo is set.
1258 	 */
1259 
1260 	va.va_mask = AT_SIZE;
1261 	error = VOP_GETATTR(frontvp, &va, 0, cr);
1262 	if (error)
1263 		goto out;
1264 
1265 	ASSERT(va.va_size != 0LL);
1266 	dir_size = va.va_size;
1267 	ASSERT(dir_size <= MAXOFF_T);
1268 
1269 	for (blockoff = 0; blockoff < dir_size; blockoff += MAXBSIZE) {
1270 		if (error = fbread(frontvp, (offset_t)blockoff,
1271 		    MAXBSIZE, S_OTHER, &fbp))
1272 			goto out;
1273 
1274 		/*
1275 		 * We cannot hold any page locks across the below VOP
1276 		 * operations. We thus copy the directory entries into a
1277 		 * staging buffer, and release the page lock on the directory
1278 		 * by calling fbrelse().  Once any necessary cnodes have
1279 		 * been created, we'll reacquire the page lock with fbread()
1280 		 * and copy the staging buffer back into the frontvp at
1281 		 * blockoff.
1282 		 */
1283 		bcopy(fbp->fb_addr, buf, MAXBSIZE);
1284 		fbrelse(fbp, S_OTHER);
1285 
1286 		for (offset = 0;
1287 		    offset < MAXBSIZE &&
1288 		    (blockoff + (u_offset_t)offset) < dir_size;
1289 		    offset += dep->d_length) {
1290 
1291 			dep = (struct c_dirent *)((uintptr_t)buf + offset);
1292 			ASSERT(dep->d_length != 0);
1293 			if ((dep->d_flag & (CDE_VALID | CDE_COMPLETE)) !=
1294 			    CDE_VALID)
1295 				continue;
1296 
1297 			error = VOP_LOOKUP(backvp, dep->d_name,
1298 			    &entry_vp, (struct pathname *)NULL, 0,
1299 			    (vnode_t *)NULL, cr);
1300 			if (error) {
1301 				/* lookup on .. in / on coc gets ENOENT */
1302 				if (error == ENOENT) {
1303 					error = 0;
1304 					continue;
1305 				}
1306 				goto out;
1307 			}
1308 
1309 			error = cachefs_getcookie(entry_vp, &cookie, NULL, cr,
1310 				TRUE);
1311 			if (error) {
1312 #ifdef CFSDEBUG
1313 				CFS_DEBUG(CFSDEBUG_DIR)
1314 					printf("\t%s: getcookie error\n",
1315 					    dep->d_name);
1316 #endif /* CFSDEBUG */
1317 				VN_RELE(entry_vp);
1318 				goto out;
1319 			}
1320 			CACHEFS_FID_COPY(&cookie, &dep->d_cookie);
1321 			dep->d_flag |= CDE_COMPLETE;
1322 
1323 			if ((! acltoo) ||
1324 			    (! cachefs_vtype_aclok(entry_vp)) ||
1325 			    (fscp->fs_info.fi_mntflags & CFS_NOACL)) {
1326 				VN_RELE(entry_vp);
1327 				continue;
1328 			}
1329 
1330 			error = cachefs_cnode_make(&dep->d_id, fscp, &cookie,
1331 			    NULL, entry_vp, cr, 0, &cp);
1332 			VN_RELE(entry_vp);
1333 			if (error != 0)
1334 				goto out;
1335 
1336 			ASSERT(cp != NULL);
1337 			mutex_enter(&cp->c_statelock);
1338 
1339 			if ((cp->c_flags & CN_NOCACHE) ||
1340 			    (cp->c_metadata.md_flags & MD_ACL)) {
1341 				mutex_exit(&cp->c_statelock);
1342 				VN_RELE(CTOV(cp));
1343 				continue;
1344 			}
1345 
1346 			(void) cachefs_cacheacl(cp, NULL);
1347 			mutex_exit(&cp->c_statelock);
1348 			VN_RELE(CTOV(cp));
1349 		}
1350 
1351 		/*
1352 		 * We must now re-lock the page corresponding to the frontvp,
1353 		 * and copy our staging buffer onto it.
1354 		 */
1355 		if (error = fbread(frontvp, (offset_t)blockoff,
1356 		    MAXBSIZE, S_OTHER, &fbp))
1357 			goto out;
1358 
1359 		bcopy(buf, fbp->fb_addr, MAXBSIZE);
1360 		(void) fbdwrite(fbp);
1361 	}
1362 
1363 out:
1364 	kmem_free(buf, MAXBSIZE);
1365 	return (error);
1366 }
1367