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