xref: /onnv-gate/usr/src/uts/common/fs/cachefs/cachefs_dir.c (revision 5331:3047ad28a67b)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*5331Samw  * Common Development and Distribution License (the "License").
6*5331Samw  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*5331Samw  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include <sys/param.h>
280Sstevel@tonic-gate #include <sys/types.h>
290Sstevel@tonic-gate #include <sys/systm.h>
300Sstevel@tonic-gate #include <sys/cred.h>
310Sstevel@tonic-gate #include <sys/proc.h>
320Sstevel@tonic-gate #include <sys/user.h>
330Sstevel@tonic-gate #include <sys/vfs.h>
340Sstevel@tonic-gate #include <sys/vnode.h>
350Sstevel@tonic-gate #include <sys/pathname.h>
360Sstevel@tonic-gate #include <sys/uio.h>
370Sstevel@tonic-gate #include <sys/tiuser.h>
380Sstevel@tonic-gate #include <sys/sysmacros.h>
390Sstevel@tonic-gate #include <sys/kmem.h>
400Sstevel@tonic-gate #include <sys/ioctl.h>
410Sstevel@tonic-gate #include <sys/statvfs.h>
420Sstevel@tonic-gate #include <sys/errno.h>
430Sstevel@tonic-gate #include <sys/debug.h>
440Sstevel@tonic-gate #include <sys/cmn_err.h>
450Sstevel@tonic-gate #include <sys/utsname.h>
460Sstevel@tonic-gate #include <sys/modctl.h>
470Sstevel@tonic-gate #include <sys/dirent.h>
480Sstevel@tonic-gate #include <sys/fbuf.h>
490Sstevel@tonic-gate #include <rpc/types.h>
500Sstevel@tonic-gate #include <vm/seg.h>
510Sstevel@tonic-gate #include <vm/faultcode.h>
520Sstevel@tonic-gate #include <vm/hat.h>
530Sstevel@tonic-gate #include <vm/seg_map.h>
540Sstevel@tonic-gate #include <sys/fs/cachefs_fs.h>
550Sstevel@tonic-gate #include <sys/fs/cachefs_dir.h>
560Sstevel@tonic-gate #include <sys/fs/cachefs_log.h>
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /* forward declarations */
590Sstevel@tonic-gate static int cachefs_dir_getentrys(struct cnode *, u_offset_t, u_offset_t *,
600Sstevel@tonic-gate     uint_t *, uint_t, caddr_t, int *);
610Sstevel@tonic-gate static int cachefs_dir_stuff(cnode_t *dcp, uint_t count, caddr_t buf,
620Sstevel@tonic-gate     vnode_t *frontvp, u_offset_t *offsetp, u_offset_t *fsizep);
630Sstevel@tonic-gate static int cachefs_dir_extend(cnode_t *, u_offset_t *, int incr_frontblks);
640Sstevel@tonic-gate static int cachefs_dir_fill_common(cnode_t *dcp, cred_t *cr,
650Sstevel@tonic-gate     vnode_t *frontvp, vnode_t *backvp, u_offset_t *frontsize);
660Sstevel@tonic-gate static int cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp,
670Sstevel@tonic-gate     vnode_t *frontvp, cred_t *cr, int acltoo);
680Sstevel@tonic-gate 
690Sstevel@tonic-gate 
700Sstevel@tonic-gate 
710Sstevel@tonic-gate /*
720Sstevel@tonic-gate  * cachefs_dir_look() called mainly by lookup (and create), looks up the cached
730Sstevel@tonic-gate  * directory for an entry and returns the information there. If the directory
740Sstevel@tonic-gate  * entry doesn't exist return ENOENT, if it is incomplete, return EINVAL.
750Sstevel@tonic-gate  * Should only call this routine if the dir is populated.
760Sstevel@tonic-gate  * Returns ENOTDIR if dir gets nuked because of front file problems.
770Sstevel@tonic-gate  */
780Sstevel@tonic-gate int
cachefs_dir_look(cnode_t * dcp,char * nm,fid_t * cookiep,uint_t * flagp,u_offset_t * d_offsetp,cfs_cid_t * cidp)790Sstevel@tonic-gate cachefs_dir_look(cnode_t *dcp, char *nm, fid_t *cookiep, uint_t *flagp,
800Sstevel@tonic-gate     u_offset_t *d_offsetp, cfs_cid_t *cidp)
810Sstevel@tonic-gate {
820Sstevel@tonic-gate 	int error;
830Sstevel@tonic-gate 	struct vattr va;
840Sstevel@tonic-gate 	u_offset_t blockoff = 0LL;
850Sstevel@tonic-gate 	uint_t offset = 0; /* offset inside the block of size MAXBSIZE */
860Sstevel@tonic-gate 	vnode_t *dvp;
870Sstevel@tonic-gate 	struct fscache *fscp = C_TO_FSCACHE(dcp);
880Sstevel@tonic-gate 	cachefscache_t *cachep = fscp->fs_cache;
890Sstevel@tonic-gate 	int nmlen;
900Sstevel@tonic-gate 	struct fbuf *fbp;
910Sstevel@tonic-gate 
920Sstevel@tonic-gate #ifdef CFSDEBUG
930Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
940Sstevel@tonic-gate 		printf("cachefs_dir_look: ENTER dcp %p nm %s\n", (void *)dcp,
950Sstevel@tonic-gate 									nm);
960Sstevel@tonic-gate #endif
970Sstevel@tonic-gate 	ASSERT(CTOV(dcp)->v_type == VDIR);
980Sstevel@tonic-gate 	ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
990Sstevel@tonic-gate 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	if (dcp->c_frontvp == NULL)
1020Sstevel@tonic-gate 		(void) cachefs_getfrontfile(dcp);
1030Sstevel@tonic-gate 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
1040Sstevel@tonic-gate 		error = ENOTDIR;
1050Sstevel@tonic-gate 		goto out;
1060Sstevel@tonic-gate 	}
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate 	dvp = dcp->c_frontvp;
1090Sstevel@tonic-gate 	va.va_mask = AT_SIZE;		/* XXX should save dir size */
110*5331Samw 	error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
1110Sstevel@tonic-gate 	if (error) {
1120Sstevel@tonic-gate 		cachefs_inval_object(dcp);
1130Sstevel@tonic-gate 		error = ENOTDIR;
1140Sstevel@tonic-gate 		goto out;
1150Sstevel@tonic-gate 	}
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	ASSERT(va.va_size != 0LL);
1180Sstevel@tonic-gate 	nmlen = (int)strlen(nm);
1190Sstevel@tonic-gate 	while (blockoff < va.va_size) {
1200Sstevel@tonic-gate 		offset = 0;
1210Sstevel@tonic-gate 		error =
1220Sstevel@tonic-gate 		    fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
1230Sstevel@tonic-gate 		if (error)
1240Sstevel@tonic-gate 			goto out;
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 		while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
1270Sstevel@tonic-gate 			struct c_dirent *dep;
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 			dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
1300Sstevel@tonic-gate 								offset);
1310Sstevel@tonic-gate 			if ((dep->d_flag & CDE_VALID) &&
1320Sstevel@tonic-gate 				(nmlen == dep->d_namelen) &&
1330Sstevel@tonic-gate 				strcmp(dep->d_name, nm) == 0) {
1340Sstevel@tonic-gate 				if (dep->d_flag & CDE_COMPLETE) {
1350Sstevel@tonic-gate 					if (cookiep) {
1360Sstevel@tonic-gate 						CACHEFS_FID_COPY(&dep->d_cookie,
1370Sstevel@tonic-gate 							cookiep);
1380Sstevel@tonic-gate 					}
1390Sstevel@tonic-gate 					if (flagp)
1400Sstevel@tonic-gate 						*flagp = dep->d_flag;
1410Sstevel@tonic-gate 					error = 0;
1420Sstevel@tonic-gate 				} else {
1430Sstevel@tonic-gate 					error = EINVAL;
1440Sstevel@tonic-gate 				}
1450Sstevel@tonic-gate 				if (cidp)
1460Sstevel@tonic-gate 					*cidp = dep->d_id;
1470Sstevel@tonic-gate 				if (d_offsetp)
1480Sstevel@tonic-gate 					*d_offsetp = offset + blockoff;
1490Sstevel@tonic-gate 				fbrelse(fbp, S_OTHER);
1500Sstevel@tonic-gate 				goto out;
1510Sstevel@tonic-gate 			}
1520Sstevel@tonic-gate 			ASSERT(dep->d_length != 0);
1530Sstevel@tonic-gate 			offset += dep->d_length;
1540Sstevel@tonic-gate 		}
1550Sstevel@tonic-gate 		fbrelse(fbp, S_OTHER);
1560Sstevel@tonic-gate 		blockoff += MAXBSIZE;
1570Sstevel@tonic-gate 	}
1580Sstevel@tonic-gate 	error = ENOENT;
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate out:
1610Sstevel@tonic-gate 	if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR))
1620Sstevel@tonic-gate 		cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp,
1630Sstevel@tonic-gate 		    &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno, 0);
1640Sstevel@tonic-gate #ifdef CFSDEBUG
1650Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
1660Sstevel@tonic-gate 		printf("c_dir_look: EXIT error = %d\n", error);
1670Sstevel@tonic-gate #endif
1680Sstevel@tonic-gate 	return (error);
1690Sstevel@tonic-gate }
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate /*
1720Sstevel@tonic-gate  * creates a new directory and populates it with "." and ".."
1730Sstevel@tonic-gate  */
1740Sstevel@tonic-gate int
cachefs_dir_new(cnode_t * dcp,cnode_t * cp)1750Sstevel@tonic-gate cachefs_dir_new(cnode_t *dcp, cnode_t *cp)
1760Sstevel@tonic-gate {
1770Sstevel@tonic-gate 	int		error = 0;
1780Sstevel@tonic-gate 	struct c_dirent	*dep;
1790Sstevel@tonic-gate 	u_offset_t	size;
1800Sstevel@tonic-gate 	int len;
1810Sstevel@tonic-gate 	struct fbuf	*fbp;
1820Sstevel@tonic-gate #ifdef CFSDEBUG
1830Sstevel@tonic-gate 	struct vattr	va;
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
1860Sstevel@tonic-gate 		printf("c_dir_new: ENTER dcp %p cp %p\n", (void *)dcp,
1870Sstevel@tonic-gate 							(void *)cp);
1880Sstevel@tonic-gate #endif
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cp->c_statelock));
1910Sstevel@tonic-gate 	ASSERT(CTOV(cp)->v_type == VDIR);
1920Sstevel@tonic-gate 	ASSERT((cp->c_flags & CN_ASYNC_POPULATE) == 0);
1930Sstevel@tonic-gate 	ASSERT(CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(dcp)) == 0);
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	if (cp->c_frontvp == NULL) {
1960Sstevel@tonic-gate 		error = cachefs_getfrontfile(cp);
1970Sstevel@tonic-gate 		if (error)
1980Sstevel@tonic-gate 			goto out;
1990Sstevel@tonic-gate 	}
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate #ifdef CFSDEBUG
2020Sstevel@tonic-gate 	va.va_mask = AT_SIZE;
203*5331Samw 	error = VOP_GETATTR(cp->c_frontvp, &va, 0, kcred, NULL);
2040Sstevel@tonic-gate 	if (error)
2050Sstevel@tonic-gate 		goto out;
2060Sstevel@tonic-gate 	ASSERT(va.va_size == 0);
2070Sstevel@tonic-gate #endif
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 	/*
2100Sstevel@tonic-gate 	 * Extend the directory by one MAXBSIZE chunk
2110Sstevel@tonic-gate 	 */
2120Sstevel@tonic-gate 	size = 0LL;
2130Sstevel@tonic-gate 	error = cachefs_dir_extend(cp, &size, 1);
2140Sstevel@tonic-gate 	if (error != 0)
2150Sstevel@tonic-gate 		goto out;
2160Sstevel@tonic-gate 	error = fbread(cp->c_frontvp, (offset_t)0, MAXBSIZE, S_OTHER, &fbp);
2170Sstevel@tonic-gate 	if (error)
2180Sstevel@tonic-gate 		goto out;
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	/*
2210Sstevel@tonic-gate 	 * Insert "." and ".."
2220Sstevel@tonic-gate 	 */
2230Sstevel@tonic-gate 	len = (int)CDE_SIZE(".");
2240Sstevel@tonic-gate 	dep = (struct c_dirent *)fbp->fb_addr;
2250Sstevel@tonic-gate 	dep->d_length = len;
2260Sstevel@tonic-gate 	dep->d_offset = (offset_t)len;
2270Sstevel@tonic-gate 	dep->d_flag = CDE_VALID | CDE_COMPLETE;
2280Sstevel@tonic-gate 	CACHEFS_FID_COPY(&cp->c_cookie, &dep->d_cookie);
2290Sstevel@tonic-gate 	dep->d_id = cp->c_id;
2300Sstevel@tonic-gate 	dep->d_namelen = 1;
2310Sstevel@tonic-gate 	bcopy(".", dep->d_name, 2);
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate 	dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + len);
2340Sstevel@tonic-gate 	dep->d_length = MAXBSIZE - len;
2350Sstevel@tonic-gate 	dep->d_offset = MAXBSIZE;
2360Sstevel@tonic-gate 	dep->d_flag = CDE_VALID | CDE_COMPLETE;
2370Sstevel@tonic-gate 	CACHEFS_FID_COPY(&dcp->c_cookie, &dep->d_cookie);
2380Sstevel@tonic-gate 	dep->d_id = dcp->c_id;
2390Sstevel@tonic-gate 	dep->d_namelen = 2;
2400Sstevel@tonic-gate 	bcopy("..", dep->d_name, 3);
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	(void) fbdwrite(fbp);
2430Sstevel@tonic-gate #ifdef INVALREADDIR
2440Sstevel@tonic-gate 	cp->c_metadata.md_flags |= MD_POPULATED | MD_INVALREADDIR;
2450Sstevel@tonic-gate #else
2460Sstevel@tonic-gate 	cp->c_metadata.md_flags |= MD_POPULATED;
2470Sstevel@tonic-gate #endif
2480Sstevel@tonic-gate 	cp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
2490Sstevel@tonic-gate out:
2500Sstevel@tonic-gate #ifdef CFSDEBUG
2510Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
2520Sstevel@tonic-gate 		printf("cachefs_dir_new: EXIT error = %d\n", error);
2530Sstevel@tonic-gate #endif
2540Sstevel@tonic-gate 	return (error);
2550Sstevel@tonic-gate }
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate /*
2580Sstevel@tonic-gate  * cachefs_dir_enter adds a new directory entry. Takes as input a fid,
2590Sstevel@tonic-gate  * fileno and a sync flag. Most of the time, the caller is content with the
2600Sstevel@tonic-gate  * write to the (front) directory being done async. The exception being - for
2610Sstevel@tonic-gate  * local files, we should make sure that the directory entry is made
2620Sstevel@tonic-gate  * synchronously. That is notified by the caller.
2630Sstevel@tonic-gate  * 		issync == 0 || issync == SM_ASYNC !
2640Sstevel@tonic-gate  *
2650Sstevel@tonic-gate  * The new entry is inserted at the end, so that we can generate local offsets
2660Sstevel@tonic-gate  * which are compatible with the backfs offsets (which are used when
2670Sstevel@tonic-gate  * disconnected.
2680Sstevel@tonic-gate  */
2690Sstevel@tonic-gate int
cachefs_dir_enter(cnode_t * dcp,char * nm,fid_t * cookiep,cfs_cid_t * cidp,int issync)2700Sstevel@tonic-gate cachefs_dir_enter(cnode_t *dcp, char *nm, fid_t *cookiep, cfs_cid_t *cidp,
2710Sstevel@tonic-gate     int issync)
2720Sstevel@tonic-gate {
2730Sstevel@tonic-gate 	struct vattr	va;
2740Sstevel@tonic-gate 	int offset;
2750Sstevel@tonic-gate 	u_offset_t blockoff = 0LL;
2760Sstevel@tonic-gate 	u_offset_t	prev_offset;
2770Sstevel@tonic-gate 	int		error = 0;
2780Sstevel@tonic-gate 	vnode_t		*dvp;
2790Sstevel@tonic-gate 	struct c_dirent	*dep;
2800Sstevel@tonic-gate 	uint_t		esize;
2810Sstevel@tonic-gate 	u_offset_t	dirsize;
2820Sstevel@tonic-gate 	struct fbuf	*fbp;
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate #ifdef CFSDEBUG
2850Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
2860Sstevel@tonic-gate 		printf("c_dir_enter: ENTER dcp %p nm %s dirflg %x\n",
2870Sstevel@tonic-gate 			(void *)dcp, nm, dcp->c_metadata.md_flags);
2880Sstevel@tonic-gate #endif
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&dcp->c_statelock));
2910Sstevel@tonic-gate 	ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
2920Sstevel@tonic-gate 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
2930Sstevel@tonic-gate 	ASSERT(CTOV(dcp)->v_type == VDIR);
2940Sstevel@tonic-gate 	ASSERT(issync == 0 || issync == SM_ASYNC);
2950Sstevel@tonic-gate 	ASSERT(strlen(nm) <= MAXNAMELEN);
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	if (dcp->c_frontvp == NULL)
2980Sstevel@tonic-gate 		(void) cachefs_getfrontfile(dcp);
2990Sstevel@tonic-gate 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
3000Sstevel@tonic-gate 		error = ENOTDIR;
3010Sstevel@tonic-gate 		goto out;
3020Sstevel@tonic-gate 	}
3030Sstevel@tonic-gate 	dvp = dcp->c_frontvp;
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 	/*
3060Sstevel@tonic-gate 	 * Get the current EOF for the directory(data file)
3070Sstevel@tonic-gate 	 */
3080Sstevel@tonic-gate 	va.va_mask = AT_SIZE;
309*5331Samw 	error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
3100Sstevel@tonic-gate 	if (error) {
3110Sstevel@tonic-gate 		cachefs_inval_object(dcp);
3120Sstevel@tonic-gate 		error = ENOTDIR;
3130Sstevel@tonic-gate 		goto out;
3140Sstevel@tonic-gate 	}
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 	/*
3170Sstevel@tonic-gate 	 * Get the last block of the directory
3180Sstevel@tonic-gate 	 */
3190Sstevel@tonic-gate 	dirsize = va.va_size;
3200Sstevel@tonic-gate 	ASSERT(dirsize != 0LL);
3210Sstevel@tonic-gate 	ASSERT(!(dirsize & MAXBOFFSET));
3220Sstevel@tonic-gate 	ASSERT(dirsize <= MAXOFF_T);
3230Sstevel@tonic-gate 	blockoff = dirsize - MAXBSIZE;
3240Sstevel@tonic-gate 	error = fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
3250Sstevel@tonic-gate 	if (error)
3260Sstevel@tonic-gate 		goto out;
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	/*
3290Sstevel@tonic-gate 	 * Find the last entry
3300Sstevel@tonic-gate 	 */
3310Sstevel@tonic-gate 	offset = 0;
3320Sstevel@tonic-gate 	prev_offset = blockoff;
3330Sstevel@tonic-gate 	for (;;) {
3340Sstevel@tonic-gate 		dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + offset);
3350Sstevel@tonic-gate 		if (offset + dep->d_length == MAXBSIZE)
3360Sstevel@tonic-gate 			break;
3370Sstevel@tonic-gate 		prev_offset = dep->d_offset;
3380Sstevel@tonic-gate 		offset += dep->d_length;
3390Sstevel@tonic-gate 		ASSERT(offset < MAXBSIZE);
3400Sstevel@tonic-gate 	}
3410Sstevel@tonic-gate 	esize = C_DIRSIZ(dep);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	if (dep->d_length - esize >= CDE_SIZE(nm)) {
3440Sstevel@tonic-gate 		/*
3450Sstevel@tonic-gate 		 * It has room. If the entry is not valid, we can just use
3460Sstevel@tonic-gate 		 * it. Otherwise, we need to adjust its length and offset
3470Sstevel@tonic-gate 		 */
3480Sstevel@tonic-gate #ifdef CFSDEBUG
3490Sstevel@tonic-gate 		CFS_DEBUG(CFSDEBUG_DIR) {
3500Sstevel@tonic-gate 			if (prev_offset >= dep->d_offset) {
3510Sstevel@tonic-gate 				printf("cachefs_dir_enter: looks like "
3520Sstevel@tonic-gate 				    "we might fail the assert\n");
3530Sstevel@tonic-gate 				printf("addr %p, offset %x, "
3540Sstevel@tonic-gate 				    "prev_offset %llx, dep->d_offset %llx\n",
3550Sstevel@tonic-gate 				    (void *)fbp->fb_addr, offset, prev_offset,
3560Sstevel@tonic-gate 				    dep->d_offset);
3570Sstevel@tonic-gate 				offset = 0;
3580Sstevel@tonic-gate 				prev_offset = blockoff;
3590Sstevel@tonic-gate 				for (;;) {
3600Sstevel@tonic-gate 					dep = (struct c_dirent *)
3610Sstevel@tonic-gate 					    ((uintptr_t)fbp->fb_addr + offset);
3620Sstevel@tonic-gate 					printf("offset %x, prev_offset %llx\n",
3630Sstevel@tonic-gate 					    offset, prev_offset);
3640Sstevel@tonic-gate 					printf("dep->d_offset %llx, "
3650Sstevel@tonic-gate 					    "dep->d_length %x\n",
3660Sstevel@tonic-gate 					    dep->d_offset, dep->d_length);
3670Sstevel@tonic-gate 					if (offset + dep->d_length == MAXBSIZE)
3680Sstevel@tonic-gate 						break;
3690Sstevel@tonic-gate 					prev_offset = dep->d_offset;
3700Sstevel@tonic-gate 					offset += dep->d_length;
3710Sstevel@tonic-gate 				}
3720Sstevel@tonic-gate 			}
3730Sstevel@tonic-gate 		}
3740Sstevel@tonic-gate #endif /* CFSDEBUG */
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 		if (offset)
3770Sstevel@tonic-gate 			ASSERT(prev_offset < dep->d_offset);
3780Sstevel@tonic-gate 		if (dep->d_flag & CDE_VALID) {
3790Sstevel@tonic-gate 			dep->d_length = esize;
3800Sstevel@tonic-gate 			dep->d_offset = prev_offset + (u_offset_t)esize;
3810Sstevel@tonic-gate 			dep = (struct c_dirent *)((uintptr_t)dep + esize);
3820Sstevel@tonic-gate 		}
3830Sstevel@tonic-gate 		dep->d_length = (int)((offset_t)MAXBSIZE -
3840Sstevel@tonic-gate 				((uintptr_t)dep - (uintptr_t)fbp->fb_addr));
3850Sstevel@tonic-gate 	} else {
3860Sstevel@tonic-gate 		/*
3870Sstevel@tonic-gate 		 * No room - so extend the file by one more
3880Sstevel@tonic-gate 		 * MAXBSIZE chunk, and fit the entry there.
3890Sstevel@tonic-gate 		 */
3900Sstevel@tonic-gate 		fbrelse(fbp, S_OTHER);
3910Sstevel@tonic-gate 		error = cachefs_dir_extend(dcp, &dirsize, 1);
3920Sstevel@tonic-gate 		if (error != 0)
3930Sstevel@tonic-gate 			goto out;
3940Sstevel@tonic-gate 		error =
3950Sstevel@tonic-gate 		    fbread(dvp, (offset_t)va.va_size, MAXBSIZE, S_OTHER, &fbp);
3960Sstevel@tonic-gate 		if (error)
3970Sstevel@tonic-gate 			goto out;
3980Sstevel@tonic-gate 		dep = (struct c_dirent *)fbp->fb_addr;
3990Sstevel@tonic-gate 		dep->d_length = MAXBSIZE;
4000Sstevel@tonic-gate 	}
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 	/*
4030Sstevel@tonic-gate 	 * Fill in the rest of the new entry
4040Sstevel@tonic-gate 	 */
4050Sstevel@tonic-gate 	dep->d_offset = dirsize;
4060Sstevel@tonic-gate 	dep->d_flag = CDE_VALID;
4070Sstevel@tonic-gate 	if (cookiep) {
4080Sstevel@tonic-gate 		dep->d_flag |= CDE_COMPLETE;
4090Sstevel@tonic-gate 		CACHEFS_FID_COPY(cookiep, &dep->d_cookie);
4100Sstevel@tonic-gate 	}
4110Sstevel@tonic-gate 	dep->d_id = *cidp;
4120Sstevel@tonic-gate 	dep->d_namelen = (ushort_t)strlen(nm);
4130Sstevel@tonic-gate 	(void) bcopy(nm, dep->d_name, dep->d_namelen + 1);
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate #ifdef INVALREADDIR
4160Sstevel@tonic-gate 	dcp->c_metadata.md_flags |= MD_INVALREADDIR;
4170Sstevel@tonic-gate #endif
4180Sstevel@tonic-gate 	dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
4190Sstevel@tonic-gate 	if (issync)
4200Sstevel@tonic-gate 		(void) fbwrite(fbp);
4210Sstevel@tonic-gate 	else
4220Sstevel@tonic-gate 		(void) fbdwrite(fbp);
4230Sstevel@tonic-gate out:
4240Sstevel@tonic-gate #ifdef CFSDEBUG
4250Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
4260Sstevel@tonic-gate 		printf("cachefs_dir_enter: EXIT error = %d\n", error);
4270Sstevel@tonic-gate #endif
4280Sstevel@tonic-gate 	return (error);
4290Sstevel@tonic-gate }
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate /*
4320Sstevel@tonic-gate  * Quite simple, if the deleted entry is the first in the MAXBSIZE block,
4330Sstevel@tonic-gate  * we simply mark it invalid. Otherwise, the deleted entries d_length is
4340Sstevel@tonic-gate  * just added to the previous entry.
4350Sstevel@tonic-gate  */
4360Sstevel@tonic-gate int
cachefs_dir_rmentry(cnode_t * dcp,char * nm)4370Sstevel@tonic-gate cachefs_dir_rmentry(cnode_t *dcp, char *nm)
4380Sstevel@tonic-gate {
4390Sstevel@tonic-gate 	u_offset_t blockoff = 0LL;
4400Sstevel@tonic-gate 	int offset = 0;
4410Sstevel@tonic-gate 	struct vattr va;
4420Sstevel@tonic-gate 	int error = ENOENT;
4430Sstevel@tonic-gate 	vnode_t *dvp;
4440Sstevel@tonic-gate 	int nmlen;
4450Sstevel@tonic-gate 	struct fbuf *fbp;
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate #ifdef CFSDEBUG
4480Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
4490Sstevel@tonic-gate 		printf("cachefs_dir_rmentry: ENTER dcp %p nm %s\n",
4500Sstevel@tonic-gate 		    (void *)dcp, nm);
4510Sstevel@tonic-gate #endif
4520Sstevel@tonic-gate 	ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
4530Sstevel@tonic-gate 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	if (dcp->c_frontvp == NULL)
4560Sstevel@tonic-gate 		(void) cachefs_getfrontfile(dcp);
4570Sstevel@tonic-gate 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
4580Sstevel@tonic-gate 		error = ENOTDIR;
4590Sstevel@tonic-gate 		goto out;
4600Sstevel@tonic-gate 	}
4610Sstevel@tonic-gate 	dvp = dcp->c_frontvp;
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 	ASSERT(CTOV(dcp)->v_type == VDIR);
4640Sstevel@tonic-gate 	ASSERT((dcp->c_flags & CN_NOCACHE) == 0);
4650Sstevel@tonic-gate 	ASSERT(dvp != NULL);
4660Sstevel@tonic-gate 	va.va_mask = AT_SIZE;
467*5331Samw 	error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
4680Sstevel@tonic-gate 	if (error) {
4690Sstevel@tonic-gate 		cachefs_inval_object(dcp);
4700Sstevel@tonic-gate 		error = ENOTDIR;
4710Sstevel@tonic-gate 		goto out;
4720Sstevel@tonic-gate 	}
4730Sstevel@tonic-gate 	ASSERT(va.va_size != 0LL);
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	nmlen = (int)strlen(nm);
4760Sstevel@tonic-gate 	while (blockoff < va.va_size) {
4770Sstevel@tonic-gate 		uint_t *last_len;
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 		offset = 0;
4800Sstevel@tonic-gate 		last_len = NULL;
4810Sstevel@tonic-gate 		error =
4820Sstevel@tonic-gate 		    fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
4830Sstevel@tonic-gate 		if (error)
4840Sstevel@tonic-gate 			goto out;
4850Sstevel@tonic-gate 		while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
4860Sstevel@tonic-gate 			struct c_dirent *dep;
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate 			dep  = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
4890Sstevel@tonic-gate 								offset);
4900Sstevel@tonic-gate 			if ((dep->d_flag & CDE_VALID) &&
4910Sstevel@tonic-gate 				(nmlen == dep->d_namelen) &&
4920Sstevel@tonic-gate 				strcmp(dep->d_name, nm) == 0) {
4930Sstevel@tonic-gate 				/*
4940Sstevel@tonic-gate 				 * Found the entry. If this was the first entry
4950Sstevel@tonic-gate 				 * in the MAXBSIZE block, Mark it invalid. Else
4960Sstevel@tonic-gate 				 * add it's length to the previous entry's
4970Sstevel@tonic-gate 				 * length.
4980Sstevel@tonic-gate 				 */
4990Sstevel@tonic-gate 				if (last_len == NULL) {
5000Sstevel@tonic-gate 					ASSERT(offset == 0);
5010Sstevel@tonic-gate 					dep->d_flag = 0;
5020Sstevel@tonic-gate 				} else
5030Sstevel@tonic-gate 					*last_len += dep->d_length;
5040Sstevel@tonic-gate 				(void) fbdwrite(fbp);
5050Sstevel@tonic-gate 				dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
5060Sstevel@tonic-gate 				goto out;
5070Sstevel@tonic-gate 			}
5080Sstevel@tonic-gate 			last_len = &dep->d_length;
5090Sstevel@tonic-gate 			offset += dep->d_length;
5100Sstevel@tonic-gate 		}
5110Sstevel@tonic-gate 		fbrelse(fbp, S_OTHER);
5120Sstevel@tonic-gate 		blockoff += MAXBSIZE;
5130Sstevel@tonic-gate 	}
5140Sstevel@tonic-gate 	error = ENOENT;
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate out:
5170Sstevel@tonic-gate #ifdef CFSDEBUG
5180Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
5190Sstevel@tonic-gate 		printf("cachefs_dir_rmentry: EXIT error = %d\n", error);
5200Sstevel@tonic-gate #endif
5210Sstevel@tonic-gate 	return (error);
5220Sstevel@tonic-gate }
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate #if 0
5250Sstevel@tonic-gate /*
5260Sstevel@tonic-gate  * This function is used only in cachefs_lookup_back() routine but
5270Sstevel@tonic-gate  * is inside #if 0 directive in this routine. So I am keeping this
5280Sstevel@tonic-gate  * routine also in #if 0 directive.
5290Sstevel@tonic-gate  */
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate /*
5320Sstevel@tonic-gate  * This function fills in the cookie and file no of the directory entry
5330Sstevel@tonic-gate  * at the offset specified by offset - In other words, makes the entry
5340Sstevel@tonic-gate  * "complete".
5350Sstevel@tonic-gate  */
5360Sstevel@tonic-gate int
5370Sstevel@tonic-gate cachefs_dir_modentry(cnode_t *dcp, u_offset_t offset, fid_t *cookiep,
5380Sstevel@tonic-gate     cfs_cid_t *cidp)
5390Sstevel@tonic-gate {
5400Sstevel@tonic-gate 	struct c_dirent *dep;
5410Sstevel@tonic-gate 	u_offset_t blockoff = (offset & (offset_t)MAXBMASK);
5420Sstevel@tonic-gate 	uint_t off = (uint_t)(offset & (offset_t)MAXBOFFSET);
5430Sstevel@tonic-gate 	struct fbuf *fbp;
5440Sstevel@tonic-gate 	vnode_t *dvp;
5450Sstevel@tonic-gate 	int error = 0;
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate #ifdef CFSDEBUG
5480Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
5490Sstevel@tonic-gate 		printf("cachefs_dir_modentry: ENTER dcp %p offset %lld\n",
5500Sstevel@tonic-gate 			(void *)dcp, offset);
5510Sstevel@tonic-gate #endif
5520Sstevel@tonic-gate 	ASSERT(CTOV(dcp)->v_type == VDIR);
5530Sstevel@tonic-gate 	ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
5540Sstevel@tonic-gate 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 	if (dcp->c_frontvp == NULL)
5570Sstevel@tonic-gate 		(void) cachefs_getfrontfile(dcp);
5580Sstevel@tonic-gate 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
5590Sstevel@tonic-gate 		return;
5600Sstevel@tonic-gate 	}
5610Sstevel@tonic-gate 	dvp = dcp->c_frontvp;
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	error = fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
5640Sstevel@tonic-gate 	if (error)
5650Sstevel@tonic-gate 		goto out;
5660Sstevel@tonic-gate 	dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
5670Sstevel@tonic-gate 	if (cookiep) {
5680Sstevel@tonic-gate 		dep->d_flag |= CDE_COMPLETE;
5690Sstevel@tonic-gate 		CACHEFS_FID_COPY(cookiep, &dep->d_cookie);
5700Sstevel@tonic-gate 	}
5710Sstevel@tonic-gate 	if (cidp)
5720Sstevel@tonic-gate 		dep->d_id = *cidp;
5730Sstevel@tonic-gate 	(void) fbdwrite(fbp);
5740Sstevel@tonic-gate 	dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate out:
5770Sstevel@tonic-gate #ifdef CFSDEBUG
5780Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
5790Sstevel@tonic-gate 		printf("cachefs_dir_modentry: EXIT\n");
5800Sstevel@tonic-gate #endif
5810Sstevel@tonic-gate 	return (error);
5820Sstevel@tonic-gate }
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate #endif  /* of #if 0 */
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate /*
5870Sstevel@tonic-gate  * Called by cachefs_read_dir(). Gets a bunch if directory entries into buf and
5880Sstevel@tonic-gate  * packs them into buf.
5890Sstevel@tonic-gate  */
5900Sstevel@tonic-gate 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)5910Sstevel@tonic-gate cachefs_dir_getentrys(struct cnode *dcp, u_offset_t beg_off,
5920Sstevel@tonic-gate 		u_offset_t *last_offp, uint_t *cntp, uint_t bufsize,
5930Sstevel@tonic-gate 				caddr_t buf, int *eofp)
5940Sstevel@tonic-gate {
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate #define	DIR_ENDOFF	0x7fffffffLL
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	struct vattr va;
5990Sstevel@tonic-gate 	struct c_dirent *dep;
6000Sstevel@tonic-gate 	struct fbuf *fbp = NULL;
6010Sstevel@tonic-gate 	struct dirent64 *gdp;
6020Sstevel@tonic-gate 	u_offset_t blockoff;
6030Sstevel@tonic-gate 	uint_t off;
6040Sstevel@tonic-gate 	int error;
6050Sstevel@tonic-gate 	vnode_t *dvp = dcp->c_frontvp;
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate #ifdef CFSDEBUG
6080Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
6090Sstevel@tonic-gate 	printf("cachefs_dir_getentrys: "
6100Sstevel@tonic-gate 	    "ENTER dcp %p beg_off %lld mdflags %x cflags %x\n",
6110Sstevel@tonic-gate 	    dcp, beg_off, dcp->c_metadata.md_flags, dcp->c_flags);
6120Sstevel@tonic-gate #endif
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 	/*
6170Sstevel@tonic-gate 	 * blockoff has the offset of the MAXBSIZE block that contains the
6180Sstevel@tonic-gate 	 * entry  to start with. off contains the offset relative to the
6190Sstevel@tonic-gate 	 * begining of the MAXBSIZE block.
6200Sstevel@tonic-gate 	 */
6210Sstevel@tonic-gate 	if (eofp)
6220Sstevel@tonic-gate 		*eofp = 0;
6230Sstevel@tonic-gate 	gdp = (struct dirent64 *)buf;
6240Sstevel@tonic-gate 	*cntp = bufsize;
6250Sstevel@tonic-gate 	va.va_mask = AT_SIZE;
626*5331Samw 	error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
6270Sstevel@tonic-gate 	if (error) {
6280Sstevel@tonic-gate 		*cntp = 0;
6290Sstevel@tonic-gate 		*last_offp = 0;
6300Sstevel@tonic-gate 		if (eofp)
6310Sstevel@tonic-gate 			*eofp = 1;
6320Sstevel@tonic-gate 		goto out;
6330Sstevel@tonic-gate 	}
6340Sstevel@tonic-gate 	ASSERT(va.va_size != 0LL);
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 	if (beg_off == DIR_ENDOFF) {
6370Sstevel@tonic-gate 		*cntp = 0;
6380Sstevel@tonic-gate 		*last_offp = DIR_ENDOFF;
6390Sstevel@tonic-gate 		if (eofp)
6400Sstevel@tonic-gate 			*eofp = 1;
6410Sstevel@tonic-gate 		goto out;
6420Sstevel@tonic-gate 	}
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	/*
6450Sstevel@tonic-gate 	 * locate the offset where we start reading.
6460Sstevel@tonic-gate 	 */
6470Sstevel@tonic-gate 	for (blockoff = 0; blockoff < va.va_size; blockoff += MAXBSIZE) {
6480Sstevel@tonic-gate 		error =
6490Sstevel@tonic-gate 		    fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
6500Sstevel@tonic-gate 		if (error)
6510Sstevel@tonic-gate 			goto out;
6520Sstevel@tonic-gate 		dep = (struct c_dirent *)fbp->fb_addr;
6530Sstevel@tonic-gate 		off = 0;
6540Sstevel@tonic-gate 		while (off < MAXBSIZE && dep->d_offset <= beg_off) {
6550Sstevel@tonic-gate 			off += dep->d_length;
6560Sstevel@tonic-gate 			dep =
6570Sstevel@tonic-gate 			    (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
6580Sstevel@tonic-gate 		}
6590Sstevel@tonic-gate 		if (off < MAXBSIZE)
6600Sstevel@tonic-gate 			break;
6610Sstevel@tonic-gate 		fbrelse(fbp, S_OTHER);
6620Sstevel@tonic-gate 		fbp = NULL;
6630Sstevel@tonic-gate 	}
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 	if (blockoff >= va.va_size) {
6660Sstevel@tonic-gate 		*cntp = 0;
6670Sstevel@tonic-gate 		*last_offp = DIR_ENDOFF;
6680Sstevel@tonic-gate 		if (eofp)
6690Sstevel@tonic-gate 			*eofp = 1;
6700Sstevel@tonic-gate 		goto out;
6710Sstevel@tonic-gate 	}
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 	/*
6740Sstevel@tonic-gate 	 * Just load up the buffer with directory entries.
6750Sstevel@tonic-gate 	 */
6760Sstevel@tonic-gate 	for (;;) {
6770Sstevel@tonic-gate 		uint_t size;
6780Sstevel@tonic-gate 		int this_reclen;
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 		ASSERT((uintptr_t)dep < ((uintptr_t)fbp->fb_addr + MAXBSIZE));
6810Sstevel@tonic-gate 		if (dep->d_flag & CDE_VALID) {
6820Sstevel@tonic-gate 			this_reclen = DIRENT64_RECLEN(dep->d_namelen);
6830Sstevel@tonic-gate 			size = C_DIRSIZ(dep);
6840Sstevel@tonic-gate 			ASSERT(size < MAXBSIZE);
6850Sstevel@tonic-gate 			if (this_reclen > bufsize)
6860Sstevel@tonic-gate 				break;
6870Sstevel@tonic-gate 			ASSERT(dep->d_namelen <= MAXNAMELEN);
6880Sstevel@tonic-gate 			ASSERT(dep->d_offset > (*last_offp));
6890Sstevel@tonic-gate 			gdp->d_ino = dep->d_id.cid_fileno;
6900Sstevel@tonic-gate 			gdp->d_off = dep->d_offset;
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate 			/* use strncpy(9f) to zero out uninitialized bytes */
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 			ASSERT(strlen(dep->d_name) + 1 <=
6950Sstevel@tonic-gate 			    DIRENT64_NAMELEN(this_reclen));
6960Sstevel@tonic-gate 			(void) strncpy(gdp->d_name, dep->d_name,
6970Sstevel@tonic-gate 			    DIRENT64_NAMELEN(this_reclen));
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 			gdp->d_reclen = (ushort_t)this_reclen;
7000Sstevel@tonic-gate 			bufsize -= this_reclen;
7010Sstevel@tonic-gate 			gdp = (struct dirent64 *)((uintptr_t)gdp +
7020Sstevel@tonic-gate 				gdp->d_reclen);
7030Sstevel@tonic-gate 			*last_offp = dep->d_offset;
7040Sstevel@tonic-gate 		}
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate 		/*
7070Sstevel@tonic-gate 		 * Increment the offset. If we've hit EOF, fill in
7080Sstevel@tonic-gate 		 * the lastoff and current entries d_off field.
7090Sstevel@tonic-gate 		 */
7100Sstevel@tonic-gate 		off += dep->d_length;
7110Sstevel@tonic-gate 		ASSERT(off <= MAXBSIZE);
7120Sstevel@tonic-gate 		if ((blockoff + off) >= va.va_size) {
7130Sstevel@tonic-gate 			*last_offp = DIR_ENDOFF;
7140Sstevel@tonic-gate 			if (eofp)
7150Sstevel@tonic-gate 				*eofp = 1;
7160Sstevel@tonic-gate 			break;
7170Sstevel@tonic-gate 		}
7180Sstevel@tonic-gate 		/*
7190Sstevel@tonic-gate 		 * If off == MAXBSIZE, then we need to adjust our
7200Sstevel@tonic-gate 		 * window to the next MAXBSIZE block of the directory.
7210Sstevel@tonic-gate 		 * Adjust blockoff, off and map it in. Also, increment
7220Sstevel@tonic-gate 		 * the directory and buffer pointers.
7230Sstevel@tonic-gate 		 */
7240Sstevel@tonic-gate 		if (off == MAXBSIZE) {
7250Sstevel@tonic-gate 			fbrelse(fbp, S_OTHER);
7260Sstevel@tonic-gate 			fbp = NULL;
7270Sstevel@tonic-gate 			off = 0;
7280Sstevel@tonic-gate 			blockoff += MAXBSIZE;
7290Sstevel@tonic-gate 			error = fbread(dvp, (offset_t)blockoff, MAXBSIZE,
7300Sstevel@tonic-gate 								S_OTHER, &fbp);
7310Sstevel@tonic-gate 			if (error)
7320Sstevel@tonic-gate 				goto out;
7330Sstevel@tonic-gate 		}
7340Sstevel@tonic-gate 		dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
7350Sstevel@tonic-gate 	}
7360Sstevel@tonic-gate 	*cntp -= bufsize;
7370Sstevel@tonic-gate out:
7380Sstevel@tonic-gate 	/*
7390Sstevel@tonic-gate 	 * Release any buffer and maping that may exist.
7400Sstevel@tonic-gate 	 */
7410Sstevel@tonic-gate 	if (fbp)
7420Sstevel@tonic-gate 		(void) fbrelse(fbp, S_OTHER);
7430Sstevel@tonic-gate #ifdef CFSDEBUG
7440Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
7450Sstevel@tonic-gate 		printf("ccachefs_dir_getentrys: EXIT error = %d\n", error);
7460Sstevel@tonic-gate #endif
7470Sstevel@tonic-gate 	return (error);
7480Sstevel@tonic-gate }
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate /*
7510Sstevel@tonic-gate  * Called by cachefs_readdir(). Fills a directory request from the cache
7520Sstevel@tonic-gate  */
7530Sstevel@tonic-gate int
cachefs_dir_read(struct cnode * dcp,struct uio * uiop,int * eofp)7540Sstevel@tonic-gate cachefs_dir_read(struct cnode *dcp, struct uio *uiop, int *eofp)
7550Sstevel@tonic-gate {
7560Sstevel@tonic-gate 	int error;
7570Sstevel@tonic-gate 	uint_t count;
7580Sstevel@tonic-gate 	uint_t size;
7590Sstevel@tonic-gate 	caddr_t buf;
7600Sstevel@tonic-gate 	u_offset_t next = uiop->uio_loffset;
7610Sstevel@tonic-gate 	struct fscache *fscp = C_TO_FSCACHE(dcp);
7620Sstevel@tonic-gate 	cachefscache_t *cachep = fscp->fs_cache;
7630Sstevel@tonic-gate 	caddr_t chrp, end;
7640Sstevel@tonic-gate 	dirent64_t *de;
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate 	ASSERT(CTOV(dcp)->v_type == VDIR);
7670Sstevel@tonic-gate 	ASSERT(RW_READ_HELD(&dcp->c_rwlock));
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 	ASSERT(next <= MAXOFF_T);
7700Sstevel@tonic-gate #ifdef CFSDEBUG
7710Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
7720Sstevel@tonic-gate 		printf("cachefs_dir_read: ENTER dcp %p\n", (void *)dcp);
7730Sstevel@tonic-gate #endif
7740Sstevel@tonic-gate 	ASSERT((dcp->c_metadata.md_flags & (MD_FILE|MD_POPULATED)) ==
7750Sstevel@tonic-gate 	    (MD_FILE|MD_POPULATED));
7760Sstevel@tonic-gate 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
7770Sstevel@tonic-gate 
7780Sstevel@tonic-gate 	if (dcp->c_frontvp == NULL)
7790Sstevel@tonic-gate 		(void) cachefs_getfrontfile(dcp);
7800Sstevel@tonic-gate 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
7810Sstevel@tonic-gate 		error = ENOTDIR;
7820Sstevel@tonic-gate 		goto out;
7830Sstevel@tonic-gate 	}
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 	size = (uint_t)uiop->uio_resid;
7860Sstevel@tonic-gate 	buf = cachefs_kmem_alloc(size, KM_SLEEP);
7870Sstevel@tonic-gate 	error = cachefs_dir_getentrys(dcp, next, &next, &count, size,
7880Sstevel@tonic-gate 	    buf, eofp);
7890Sstevel@tonic-gate 	if (error == 0 && (int)count > 0) {
7900Sstevel@tonic-gate 		ASSERT(count <= size);
7910Sstevel@tonic-gate 		if (fscp->fs_inum_size > 0) {
7920Sstevel@tonic-gate 			ino64_t newinum;
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 			mutex_exit(&dcp->c_statelock);
7950Sstevel@tonic-gate 			mutex_enter(&fscp->fs_fslock);
7960Sstevel@tonic-gate 			end = (caddr_t)((uintptr_t)buf + count);
7970Sstevel@tonic-gate 			for (chrp = buf; chrp < end; chrp += de->d_reclen) {
7980Sstevel@tonic-gate 				de = (dirent64_t *)chrp;
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 				newinum = cachefs_inum_real2fake(fscp,
8010Sstevel@tonic-gate 				    de->d_ino);
8020Sstevel@tonic-gate 				if (newinum == 0)
8030Sstevel@tonic-gate 					newinum = cachefs_fileno_conflict(fscp,
8040Sstevel@tonic-gate 					    de->d_ino);
8050Sstevel@tonic-gate 				de->d_ino = newinum;
8060Sstevel@tonic-gate 			}
8070Sstevel@tonic-gate 			mutex_exit(&fscp->fs_fslock);
8080Sstevel@tonic-gate 			mutex_enter(&dcp->c_statelock);
8090Sstevel@tonic-gate 		}
8100Sstevel@tonic-gate 		error = uiomove(buf, count, UIO_READ, uiop);
8110Sstevel@tonic-gate 		if (error == 0)
8120Sstevel@tonic-gate 			uiop->uio_loffset = next;
8130Sstevel@tonic-gate 	}
8140Sstevel@tonic-gate 	(void) cachefs_kmem_free(buf, size);
8150Sstevel@tonic-gate out:
8160Sstevel@tonic-gate 	if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR))
8170Sstevel@tonic-gate 		cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp,
8180Sstevel@tonic-gate 		    &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno, 0);
8190Sstevel@tonic-gate #ifdef CFSDEBUG
8200Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
8210Sstevel@tonic-gate 		printf("cachefs_dir_read: EXIT error = %d\n", error);
8220Sstevel@tonic-gate #endif
8230Sstevel@tonic-gate 	return (error);
8240Sstevel@tonic-gate }
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate /*
8270Sstevel@tonic-gate  * Fully (including cookie) populates the directory from the back filesystem.
8280Sstevel@tonic-gate  */
8290Sstevel@tonic-gate int
cachefs_dir_fill(cnode_t * dcp,cred_t * cr)8300Sstevel@tonic-gate cachefs_dir_fill(cnode_t *dcp, cred_t *cr)
8310Sstevel@tonic-gate {
8320Sstevel@tonic-gate 	int error = 0;
8330Sstevel@tonic-gate 	u_offset_t frontsize;
8340Sstevel@tonic-gate 	struct fscache *fscp = C_TO_FSCACHE(dcp);
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate #ifdef CFSDEBUG
8370Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
8380Sstevel@tonic-gate 		printf("cachefs_dir_fill: ENTER dcp %p\n", (void *)dcp);
8390Sstevel@tonic-gate #endif
8400Sstevel@tonic-gate 	ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
8410Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&dcp->c_statelock));
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 	/* XXX for now return success if async populate is scheduled */
8440Sstevel@tonic-gate 	if (dcp->c_flags & CN_ASYNC_POPULATE)
8450Sstevel@tonic-gate 		goto out;
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 	/* get the back vp */
8480Sstevel@tonic-gate 	if (dcp->c_backvp == NULL) {
8490Sstevel@tonic-gate 		error = cachefs_getbackvp(fscp, dcp);
8500Sstevel@tonic-gate 		if (error) {
8510Sstevel@tonic-gate 			goto out;
8520Sstevel@tonic-gate 		}
8530Sstevel@tonic-gate 	}
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 	/* get the front file vp */
8560Sstevel@tonic-gate 	if (dcp->c_frontvp == NULL)
8570Sstevel@tonic-gate 		(void) cachefs_getfrontfile(dcp);
8580Sstevel@tonic-gate 	if (dcp->c_flags & CN_NOCACHE) {
8590Sstevel@tonic-gate 		error = ENOTDIR;
8600Sstevel@tonic-gate 		goto out;
8610Sstevel@tonic-gate 	}
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate 	/* if dir was modified, toss old contents */
8640Sstevel@tonic-gate 	if (dcp->c_metadata.md_flags & MD_INVALREADDIR) {
8650Sstevel@tonic-gate 		cachefs_inval_object(dcp);
8660Sstevel@tonic-gate 		if (dcp->c_flags & CN_NOCACHE) {
8670Sstevel@tonic-gate 			error = ENOTDIR;
8680Sstevel@tonic-gate 			goto out;
8690Sstevel@tonic-gate 		}
8700Sstevel@tonic-gate 	}
8710Sstevel@tonic-gate 
8720Sstevel@tonic-gate 	error = cachefs_dir_fill_common(dcp, cr,
8730Sstevel@tonic-gate 	    dcp->c_frontvp, dcp->c_backvp, &frontsize);
8740Sstevel@tonic-gate 	if (error == 0)
8750Sstevel@tonic-gate 		error = cachefs_dir_complete(fscp, dcp->c_backvp,
8760Sstevel@tonic-gate 		    dcp->c_frontvp, cr, 0);
8770Sstevel@tonic-gate 	if (error != 0)
8780Sstevel@tonic-gate 		goto out;
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	/*
8810Sstevel@tonic-gate 	 * Mark the directory as not empty. Also bang the flag that says that
8820Sstevel@tonic-gate 	 * this directory needs to be sync'ed on inactive.
8830Sstevel@tonic-gate 	 */
8840Sstevel@tonic-gate 	dcp->c_metadata.md_flags |= MD_POPULATED;
8850Sstevel@tonic-gate 	dcp->c_metadata.md_flags &= ~MD_INVALREADDIR;
8860Sstevel@tonic-gate 	dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
8870Sstevel@tonic-gate 	/*LINTED alignment okay*/
8880Sstevel@tonic-gate 	dcp->c_metadata.md_frontblks = frontsize / MAXBSIZE;
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate out:
8910Sstevel@tonic-gate 	if (error) {
8920Sstevel@tonic-gate #ifdef CFSDEBUG
8930Sstevel@tonic-gate 		CFS_DEBUG(CFSDEBUG_INVALIDATE)
8940Sstevel@tonic-gate 			printf("c_dir_fill: invalidating %llu\n",
8950Sstevel@tonic-gate 			    (u_longlong_t)dcp->c_id.cid_fileno);
8960Sstevel@tonic-gate #endif
8970Sstevel@tonic-gate 		cachefs_inval_object(dcp);
8980Sstevel@tonic-gate 	}
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate 	return (error);
9010Sstevel@tonic-gate }
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate /*
9040Sstevel@tonic-gate  * Does work of populating directory.
9050Sstevel@tonic-gate  * Must be called while holding dcp->c_statelock
9060Sstevel@tonic-gate  */
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate static int
cachefs_dir_fill_common(cnode_t * dcp,cred_t * cr,vnode_t * frontvp,vnode_t * backvp,u_offset_t * frontsize)9090Sstevel@tonic-gate cachefs_dir_fill_common(cnode_t *dcp, cred_t *cr,
9100Sstevel@tonic-gate     vnode_t *frontvp, vnode_t *backvp, u_offset_t *frontsize)
9110Sstevel@tonic-gate {
9120Sstevel@tonic-gate 	int error = 0;
9130Sstevel@tonic-gate 	struct uio uio;
9140Sstevel@tonic-gate 	struct iovec iov;
9150Sstevel@tonic-gate 	caddr_t buf = NULL;
9160Sstevel@tonic-gate 	int count;
9170Sstevel@tonic-gate 	int eof = 0;
9180Sstevel@tonic-gate 	u_offset_t frontoff;
9190Sstevel@tonic-gate 	struct fscache *fscp = C_TO_FSCACHE(dcp);
9200Sstevel@tonic-gate 	cachefscache_t *cachep = fscp->fs_cache;
9210Sstevel@tonic-gate #ifdef DEBUG
9220Sstevel@tonic-gate 	int loop_count = 0;
9230Sstevel@tonic-gate #endif
9240Sstevel@tonic-gate #ifdef CFSDEBUG
9250Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
9260Sstevel@tonic-gate 		printf("cachefs_dir_fill_common: ENTER dcp %p\n", (void *)dcp);
9270Sstevel@tonic-gate #endif
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&dcp->c_statelock));
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate 	frontoff = *frontsize = 0LL;
9320Sstevel@tonic-gate 
9330Sstevel@tonic-gate 	buf = cachefs_kmem_alloc(MAXBSIZE, KM_SLEEP);
9340Sstevel@tonic-gate 	uio.uio_iov = &iov;
9350Sstevel@tonic-gate 	uio.uio_iovcnt = 1;
9360Sstevel@tonic-gate 	uio.uio_segflg = UIO_SYSSPACE;
9370Sstevel@tonic-gate 	uio.uio_fmode = 0;
9380Sstevel@tonic-gate 	uio.uio_extflg = UIO_COPY_CACHED;
9390Sstevel@tonic-gate 	uio.uio_loffset = 0;
9400Sstevel@tonic-gate 	for (;;) {
9410Sstevel@tonic-gate #ifdef DEBUG
9420Sstevel@tonic-gate 		loop_count++;
9430Sstevel@tonic-gate #endif
9440Sstevel@tonic-gate 		/*
9450Sstevel@tonic-gate 		 * Read in a buffer's worth of dirents and enter them in to the
9460Sstevel@tonic-gate 		 * directory.
9470Sstevel@tonic-gate 		 */
9480Sstevel@tonic-gate 		uio.uio_resid = MAXBSIZE;
9490Sstevel@tonic-gate 		iov.iov_base = buf;
9500Sstevel@tonic-gate 		iov.iov_len = MAXBSIZE;
9510Sstevel@tonic-gate 		(void) VOP_RWLOCK(backvp, V_WRITELOCK_FALSE, NULL);
952*5331Samw 		error = VOP_READDIR(backvp, &uio, cr, &eof, NULL, 0);
9530Sstevel@tonic-gate 		VOP_RWUNLOCK(backvp, V_WRITELOCK_FALSE, NULL);
9540Sstevel@tonic-gate 		if (error)
9550Sstevel@tonic-gate 			goto out;
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 		/*LINTED alignment okay*/
9580Sstevel@tonic-gate 		count = MAXBSIZE - (int)uio.uio_resid;
9590Sstevel@tonic-gate 		ASSERT(count >= 0);
9600Sstevel@tonic-gate 		if (count > 0) {
9610Sstevel@tonic-gate 			if (error = cachefs_dir_stuff(dcp, count, buf,
9620Sstevel@tonic-gate 			    frontvp, &frontoff, frontsize))
9630Sstevel@tonic-gate 				goto out;
9640Sstevel@tonic-gate 			ASSERT((*frontsize) != 0LL);
9650Sstevel@tonic-gate 		}
9660Sstevel@tonic-gate 		if (eof || count == 0)
9670Sstevel@tonic-gate 			break;
9680Sstevel@tonic-gate 	}
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	if (*frontsize == 0LL) {
9710Sstevel@tonic-gate 		/* keep us from caching an empty directory */
9720Sstevel@tonic-gate 		error = EINVAL;
9730Sstevel@tonic-gate 		goto out;
9740Sstevel@tonic-gate 	}
9750Sstevel@tonic-gate 
9760Sstevel@tonic-gate out:
9770Sstevel@tonic-gate 	if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_FILLDIR))
9780Sstevel@tonic-gate 		cachefs_log_filldir(cachep, error, fscp->fs_cfsvfsp,
9790Sstevel@tonic-gate 		    &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno,
9800Sstevel@tonic-gate 		    *frontsize);
9810Sstevel@tonic-gate 	if (buf)
9820Sstevel@tonic-gate 		cachefs_kmem_free(buf, (uint_t)MAXBSIZE);
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate #ifdef CFSDEBUG
9850Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
9860Sstevel@tonic-gate 		printf("cachefs_dir_fill: EXIT error = %d\n", error);
9870Sstevel@tonic-gate #endif
9880Sstevel@tonic-gate 	return (error);
9890Sstevel@tonic-gate }
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate /*
9920Sstevel@tonic-gate  * If the directory contains only the elements "." and "..", then this returns
9930Sstevel@tonic-gate  * 0, otherwise returns an error.
9940Sstevel@tonic-gate  */
9950Sstevel@tonic-gate int
cachefs_dir_empty(cnode_t * dcp)9960Sstevel@tonic-gate cachefs_dir_empty(cnode_t *dcp)
9970Sstevel@tonic-gate {
9980Sstevel@tonic-gate 	struct vattr va;
9990Sstevel@tonic-gate 	u_offset_t blockoff = 0;
10000Sstevel@tonic-gate 	int offset;
10010Sstevel@tonic-gate 	struct fbuf *fbp;
10020Sstevel@tonic-gate 	int error;
10030Sstevel@tonic-gate 	vnode_t *dvp = dcp->c_frontvp;
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate #ifdef CFSDEBUG
10060Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
10070Sstevel@tonic-gate 		printf("cachefs_dir_empty: ENTER dcp %p\n", (void *)dcp);
10080Sstevel@tonic-gate #endif
10090Sstevel@tonic-gate 	ASSERT(CTOV(dcp)->v_type == VDIR);
10100Sstevel@tonic-gate 	ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
10110Sstevel@tonic-gate 	ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
10120Sstevel@tonic-gate 
10130Sstevel@tonic-gate 	if (dcp->c_frontvp == NULL)
10140Sstevel@tonic-gate 		(void) cachefs_getfrontfile(dcp);
10150Sstevel@tonic-gate 	if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0)
10160Sstevel@tonic-gate 		return (ENOTDIR);
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate 	va.va_mask = AT_SIZE;
1019*5331Samw 	error = VOP_GETATTR(dvp, &va, 0, kcred, NULL);
10200Sstevel@tonic-gate 	if (error)
10210Sstevel@tonic-gate 		return (ENOTDIR);
10220Sstevel@tonic-gate 
10230Sstevel@tonic-gate 	ASSERT(va.va_size != 0LL);
10240Sstevel@tonic-gate 	while (blockoff < va.va_size) {
10250Sstevel@tonic-gate 		offset = 0;
10260Sstevel@tonic-gate 		error =
10270Sstevel@tonic-gate 		    fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
10280Sstevel@tonic-gate 		if (error)
10290Sstevel@tonic-gate 			return (error);
10300Sstevel@tonic-gate 		while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
10310Sstevel@tonic-gate 			struct c_dirent *dep;
10320Sstevel@tonic-gate 
10330Sstevel@tonic-gate 			dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
10340Sstevel@tonic-gate 								offset);
10350Sstevel@tonic-gate 			if ((dep->d_flag & CDE_VALID) &&
10360Sstevel@tonic-gate 				((strcmp(dep->d_name, ".") != 0) &&
10370Sstevel@tonic-gate 				(strcmp(dep->d_name, "..") != 0))) {
10380Sstevel@tonic-gate 				(void) fbrelse(fbp, S_OTHER);
10390Sstevel@tonic-gate 				return (0);
10400Sstevel@tonic-gate 			}
10410Sstevel@tonic-gate 			offset += dep->d_length;
10420Sstevel@tonic-gate 		}
10430Sstevel@tonic-gate 		(void) fbrelse(fbp, S_OTHER);
10440Sstevel@tonic-gate 		blockoff += MAXBSIZE;
10450Sstevel@tonic-gate 	}
10460Sstevel@tonic-gate 	return (EEXIST);
10470Sstevel@tonic-gate }
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate /*
10500Sstevel@tonic-gate  * Called by cachefs_dir_fill() to stuff a buffer of dir entries into
10510Sstevel@tonic-gate  * a front file.  This is more efficient than repeated calls to
10520Sstevel@tonic-gate  * cachefs_dir_enter, and it also allows us to maintain entries in backfs
10530Sstevel@tonic-gate  * order (readdir requires that entry offsets be ascending).
10540Sstevel@tonic-gate  */
10550Sstevel@tonic-gate 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)10560Sstevel@tonic-gate cachefs_dir_stuff(cnode_t *dcp, uint_t count, caddr_t buf,
10570Sstevel@tonic-gate     vnode_t *frontvp, u_offset_t *offsetp, u_offset_t *fsizep)
10580Sstevel@tonic-gate {
10590Sstevel@tonic-gate 	int error = 0;
10600Sstevel@tonic-gate 	struct fbuf *fbp;
10610Sstevel@tonic-gate 	struct c_dirent *cdep, *last;
10620Sstevel@tonic-gate 	struct dirent64 *dep;
10630Sstevel@tonic-gate 	int inblk, entsize;
10640Sstevel@tonic-gate 	u_offset_t blockoff = (*offsetp & (offset_t)MAXBMASK);
10650Sstevel@tonic-gate 	/*LINTED alignment okay*/
10660Sstevel@tonic-gate 	uint_t off = (uint_t)(*offsetp & (offset_t)MAXBOFFSET);
10670Sstevel@tonic-gate 
10680Sstevel@tonic-gate 	/*LINTED want count != 0*/
10690Sstevel@tonic-gate 	ASSERT(count > 0);
10700Sstevel@tonic-gate 
10710Sstevel@tonic-gate 	if (*offsetp >= *fsizep) {
10720Sstevel@tonic-gate 		error = cachefs_dir_extend(dcp, fsizep, 0);
10730Sstevel@tonic-gate 		if (error)
10740Sstevel@tonic-gate 			return (error);
10750Sstevel@tonic-gate 	}
10760Sstevel@tonic-gate 
10770Sstevel@tonic-gate 	ASSERT(*fsizep != 0LL);
10780Sstevel@tonic-gate 	last = NULL;
10790Sstevel@tonic-gate 	error = fbread(frontvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
10800Sstevel@tonic-gate 	if (error)
10810Sstevel@tonic-gate 		return (error);
10820Sstevel@tonic-gate 	cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
10830Sstevel@tonic-gate 	inblk = MAXBSIZE-off;
10840Sstevel@tonic-gate 	if (*offsetp != 0) {
10850Sstevel@tonic-gate 		ASSERT(cdep->d_length == inblk);
10860Sstevel@tonic-gate 		inblk -= C_DIRSIZ(cdep);
10870Sstevel@tonic-gate 		last = cdep;
10880Sstevel@tonic-gate 		last->d_length -= inblk;
10890Sstevel@tonic-gate 		off += last->d_length;
10900Sstevel@tonic-gate 		cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
10910Sstevel@tonic-gate 	}
10920Sstevel@tonic-gate 	dep = (struct dirent64 *)buf;
10930Sstevel@tonic-gate 	/*LINTED want count != 0*/
10940Sstevel@tonic-gate 	while (count > 0) {
10950Sstevel@tonic-gate 		if (last) {
10960Sstevel@tonic-gate 			ASSERT(dep->d_off > last->d_offset);
10970Sstevel@tonic-gate 		}
10980Sstevel@tonic-gate 		entsize = (int)CDE_SIZE(dep->d_name);
10990Sstevel@tonic-gate 		if (entsize > inblk) {
11000Sstevel@tonic-gate 			if (last) {
11010Sstevel@tonic-gate 				last->d_length += inblk;
11020Sstevel@tonic-gate 			}
11030Sstevel@tonic-gate 			(void) fbwrite(fbp);
11040Sstevel@tonic-gate 			error = cachefs_dir_extend(dcp, fsizep, 0);
11050Sstevel@tonic-gate 			if (error)
11060Sstevel@tonic-gate 				return (error);
11070Sstevel@tonic-gate 			ASSERT(*fsizep != 0LL);
11080Sstevel@tonic-gate 			blockoff += MAXBSIZE;
11090Sstevel@tonic-gate 			error = fbread(frontvp, (offset_t)blockoff, MAXBSIZE,
11100Sstevel@tonic-gate 							S_OTHER, &fbp);
11110Sstevel@tonic-gate 			if (error)
11120Sstevel@tonic-gate 				return (error);
11130Sstevel@tonic-gate 			off = 0;
11140Sstevel@tonic-gate 			cdep = (struct c_dirent *)fbp->fb_addr;
11150Sstevel@tonic-gate 			inblk = MAXBSIZE;
11160Sstevel@tonic-gate 			last = NULL;
11170Sstevel@tonic-gate 		}
11180Sstevel@tonic-gate 		cdep->d_length = entsize;
11190Sstevel@tonic-gate 		cdep->d_id.cid_fileno = dep->d_ino;
11200Sstevel@tonic-gate 		cdep->d_id.cid_flags = 0;
11210Sstevel@tonic-gate 		cdep->d_namelen = (ushort_t)strlen(dep->d_name);
11220Sstevel@tonic-gate 		cdep->d_flag = CDE_VALID;
11230Sstevel@tonic-gate 		bcopy(dep->d_name, cdep->d_name, cdep->d_namelen+1);
11240Sstevel@tonic-gate 		cdep->d_offset = dep->d_off;
11250Sstevel@tonic-gate 		inblk -= entsize;
11260Sstevel@tonic-gate 		count -= dep->d_reclen;
11270Sstevel@tonic-gate 		dep = (struct dirent64 *)((uintptr_t)dep + dep->d_reclen);
11280Sstevel@tonic-gate 		*offsetp = blockoff + (u_offset_t)off;
11290Sstevel@tonic-gate 		off += entsize;
11300Sstevel@tonic-gate 		last = cdep;
11310Sstevel@tonic-gate 		cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
11320Sstevel@tonic-gate 	}
11330Sstevel@tonic-gate 	if (last) {
11340Sstevel@tonic-gate 		last->d_length += inblk;
11350Sstevel@tonic-gate 	}
11360Sstevel@tonic-gate 	(void) fbwrite(fbp);
11370Sstevel@tonic-gate 	return (error);
11380Sstevel@tonic-gate }
11390Sstevel@tonic-gate 
11400Sstevel@tonic-gate static int
cachefs_dir_extend(cnode_t * dcp,u_offset_t * cursize,int incr_frontblks)11410Sstevel@tonic-gate cachefs_dir_extend(cnode_t *dcp, u_offset_t *cursize, int incr_frontblks)
11420Sstevel@tonic-gate {
11430Sstevel@tonic-gate 	struct vattr va;
11440Sstevel@tonic-gate 	cachefscache_t *cachep = C_TO_FSCACHE(dcp)->fs_cache;
11450Sstevel@tonic-gate 	int error = 0;
11460Sstevel@tonic-gate 	struct fscache *fscp = VFS_TO_FSCACHE(CTOV(dcp)->v_vfsp);
11470Sstevel@tonic-gate 
11480Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&dcp->c_statelock));
11490Sstevel@tonic-gate 	ASSERT(((*cursize) & (MAXBSIZE-1)) == 0);
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate 	va.va_mask = AT_SIZE;
11520Sstevel@tonic-gate 	va.va_size = (u_offset_t)(*cursize + MAXBSIZE);
11530Sstevel@tonic-gate 	error = cachefs_allocblocks(cachep, 1, dcp->c_metadata.md_rltype);
11540Sstevel@tonic-gate 	if (error)
11550Sstevel@tonic-gate 		return (error);
11560Sstevel@tonic-gate 	error = VOP_SETATTR(dcp->c_frontvp, &va, 0, kcred, NULL);
11570Sstevel@tonic-gate 	if (error) {
11580Sstevel@tonic-gate 		cachefs_freeblocks(cachep, 1, dcp->c_metadata.md_rltype);
11590Sstevel@tonic-gate 		return (error);
11600Sstevel@tonic-gate 	}
11610Sstevel@tonic-gate 	if (incr_frontblks)
11620Sstevel@tonic-gate 		dcp->c_metadata.md_frontblks++;
11630Sstevel@tonic-gate 	if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
11640Sstevel@tonic-gate 		dcp->c_size += MAXBSIZE;
11650Sstevel@tonic-gate 		dcp->c_attr.va_size = dcp->c_size;
11660Sstevel@tonic-gate 	}
11670Sstevel@tonic-gate 	*cursize += MAXBSIZE;
11680Sstevel@tonic-gate 	ASSERT(*cursize != 0LL);
11690Sstevel@tonic-gate 	if (incr_frontblks)
11700Sstevel@tonic-gate 		dcp->c_flags |= CN_UPDATED;
11710Sstevel@tonic-gate 	return (0);
11720Sstevel@tonic-gate }
11730Sstevel@tonic-gate 
11740Sstevel@tonic-gate int
cachefs_async_populate_dir(struct cachefs_populate_req * pop,cred_t * cr,vnode_t * backvp,vnode_t * frontvp)11750Sstevel@tonic-gate cachefs_async_populate_dir(struct cachefs_populate_req *pop, cred_t *cr,
11760Sstevel@tonic-gate     vnode_t *backvp, vnode_t *frontvp)
11770Sstevel@tonic-gate {
11780Sstevel@tonic-gate 	vnode_t *dvp = pop->cpop_vp;
11790Sstevel@tonic-gate 	struct cnode *dcp = VTOC(dvp);
11800Sstevel@tonic-gate 	u_offset_t frontsize;
11810Sstevel@tonic-gate 	int error = 0;
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate #ifdef CFSDEBUG
11840Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
11850Sstevel@tonic-gate 		printf("cachefs_async_populate_dir: ENTER dvp %p\n",
11860Sstevel@tonic-gate 								(void *)dvp);
11870Sstevel@tonic-gate #endif
11880Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&dcp->c_statelock));
11890Sstevel@tonic-gate 	ASSERT(dvp->v_type == VDIR);
11900Sstevel@tonic-gate 	ASSERT((dcp->c_metadata.md_flags & MD_POPULATED) == 0);
11910Sstevel@tonic-gate 	ASSERT(dcp->c_frontvp == frontvp);
11920Sstevel@tonic-gate 	ASSERT(dcp->c_backvp == backvp);
11930Sstevel@tonic-gate 	ASSERT(CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(dcp)) == 0);
11940Sstevel@tonic-gate 
11950Sstevel@tonic-gate 	/* if dir was modified, toss old contents */
11960Sstevel@tonic-gate 	if (dcp->c_metadata.md_flags & MD_INVALREADDIR) {
11970Sstevel@tonic-gate 		cachefs_inval_object(dcp);
11980Sstevel@tonic-gate 		if (dcp->c_flags & CN_NOCACHE) {
11990Sstevel@tonic-gate 			error = ENOTDIR;
12000Sstevel@tonic-gate 			goto out;
12010Sstevel@tonic-gate 		} else {
12020Sstevel@tonic-gate 			dcp->c_metadata.md_flags &= ~MD_INVALREADDIR;
12030Sstevel@tonic-gate 		}
12040Sstevel@tonic-gate 	}
12050Sstevel@tonic-gate 
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 	error = cachefs_dir_fill_common(dcp, cr, frontvp, backvp, &frontsize);
12080Sstevel@tonic-gate 	if (error != 0)
12090Sstevel@tonic-gate 		goto out;
12100Sstevel@tonic-gate 	ASSERT(frontsize != 0LL);
12110Sstevel@tonic-gate 	mutex_exit(&dcp->c_statelock);
12120Sstevel@tonic-gate 	/*
12130Sstevel@tonic-gate 	 * I don't like to break lock here but cachefs_dir_complete()
12140Sstevel@tonic-gate 	 * needs it.
12150Sstevel@tonic-gate 	 */
12160Sstevel@tonic-gate 	error = cachefs_dir_complete(C_TO_FSCACHE(dcp), backvp,
12170Sstevel@tonic-gate 	    frontvp, cr, 1);
12180Sstevel@tonic-gate 	mutex_enter(&dcp->c_statelock);
12190Sstevel@tonic-gate 	if (error != 0)
12200Sstevel@tonic-gate 		goto out;
12210Sstevel@tonic-gate 	/* if went nocache while lock was dropped, get out */
12220Sstevel@tonic-gate 	if ((dcp->c_flags & CN_NOCACHE) || (dcp->c_frontvp == NULL)) {
12230Sstevel@tonic-gate 		error = EINVAL;
12240Sstevel@tonic-gate 	} else {
12250Sstevel@tonic-gate 		/* allocfile and allocblocks have already happened. */
12260Sstevel@tonic-gate 		dcp->c_metadata.md_frontblks = frontsize / MAXBSIZE;
12270Sstevel@tonic-gate 	}
12280Sstevel@tonic-gate 
12290Sstevel@tonic-gate out:
12300Sstevel@tonic-gate 
12310Sstevel@tonic-gate #ifdef CFSDEBUG
12320Sstevel@tonic-gate 	CFS_DEBUG(CFSDEBUG_DIR)
12330Sstevel@tonic-gate 		printf("cachefs_async_populate_dir: EXIT error = %d\n", error);
12340Sstevel@tonic-gate #endif
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate 	return (error);
12370Sstevel@tonic-gate }
12380Sstevel@tonic-gate 
12390Sstevel@tonic-gate static int
cachefs_dir_complete(fscache_t * fscp,vnode_t * backvp,vnode_t * frontvp,cred_t * cr,int acltoo)12400Sstevel@tonic-gate cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp, vnode_t *frontvp,
12410Sstevel@tonic-gate     cred_t *cr, int acltoo)
12420Sstevel@tonic-gate {
12430Sstevel@tonic-gate 	struct c_dirent *dep;
12440Sstevel@tonic-gate 	caddr_t buf = kmem_alloc(MAXBSIZE, KM_SLEEP);
12450Sstevel@tonic-gate 	struct vattr va;
12460Sstevel@tonic-gate 	u_offset_t blockoff;
12470Sstevel@tonic-gate 	int offset;
12480Sstevel@tonic-gate 	u_offset_t dir_size;
12490Sstevel@tonic-gate 	struct fbuf *fbp;
12500Sstevel@tonic-gate 	cnode_t *cp;
12510Sstevel@tonic-gate 	fid_t cookie;
12520Sstevel@tonic-gate 	vnode_t *entry_vp;
12530Sstevel@tonic-gate 	int error = 0;
12540Sstevel@tonic-gate 
12550Sstevel@tonic-gate 	/*
12560Sstevel@tonic-gate 	 * note: caller better not hold a c_statelock if acltoo is set.
12570Sstevel@tonic-gate 	 */
12580Sstevel@tonic-gate 
12590Sstevel@tonic-gate 	va.va_mask = AT_SIZE;
1260*5331Samw 	error = VOP_GETATTR(frontvp, &va, 0, cr, NULL);
12610Sstevel@tonic-gate 	if (error)
12620Sstevel@tonic-gate 		goto out;
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 	ASSERT(va.va_size != 0LL);
12650Sstevel@tonic-gate 	dir_size = va.va_size;
12660Sstevel@tonic-gate 	ASSERT(dir_size <= MAXOFF_T);
12670Sstevel@tonic-gate 
12680Sstevel@tonic-gate 	for (blockoff = 0; blockoff < dir_size; blockoff += MAXBSIZE) {
12690Sstevel@tonic-gate 		if (error = fbread(frontvp, (offset_t)blockoff,
12700Sstevel@tonic-gate 		    MAXBSIZE, S_OTHER, &fbp))
12710Sstevel@tonic-gate 			goto out;
12720Sstevel@tonic-gate 
12730Sstevel@tonic-gate 		/*
12740Sstevel@tonic-gate 		 * We cannot hold any page locks across the below VOP
12750Sstevel@tonic-gate 		 * operations. We thus copy the directory entries into a
12760Sstevel@tonic-gate 		 * staging buffer, and release the page lock on the directory
12770Sstevel@tonic-gate 		 * by calling fbrelse().  Once any necessary cnodes have
12780Sstevel@tonic-gate 		 * been created, we'll reacquire the page lock with fbread()
12790Sstevel@tonic-gate 		 * and copy the staging buffer back into the frontvp at
12800Sstevel@tonic-gate 		 * blockoff.
12810Sstevel@tonic-gate 		 */
12820Sstevel@tonic-gate 		bcopy(fbp->fb_addr, buf, MAXBSIZE);
12830Sstevel@tonic-gate 		fbrelse(fbp, S_OTHER);
12840Sstevel@tonic-gate 
12850Sstevel@tonic-gate 		for (offset = 0;
12860Sstevel@tonic-gate 		    offset < MAXBSIZE &&
12870Sstevel@tonic-gate 		    (blockoff + (u_offset_t)offset) < dir_size;
12880Sstevel@tonic-gate 		    offset += dep->d_length) {
12890Sstevel@tonic-gate 
12900Sstevel@tonic-gate 			dep = (struct c_dirent *)((uintptr_t)buf + offset);
12910Sstevel@tonic-gate 			ASSERT(dep->d_length != 0);
12920Sstevel@tonic-gate 			if ((dep->d_flag & (CDE_VALID | CDE_COMPLETE)) !=
12930Sstevel@tonic-gate 			    CDE_VALID)
12940Sstevel@tonic-gate 				continue;
12950Sstevel@tonic-gate 
12960Sstevel@tonic-gate 			error = VOP_LOOKUP(backvp, dep->d_name,
12970Sstevel@tonic-gate 			    &entry_vp, (struct pathname *)NULL, 0,
1298*5331Samw 			    (vnode_t *)NULL, cr, NULL, NULL, NULL);
12990Sstevel@tonic-gate 			if (error) {
13000Sstevel@tonic-gate 				/* lookup on .. in / on coc gets ENOENT */
13010Sstevel@tonic-gate 				if (error == ENOENT) {
13020Sstevel@tonic-gate 					error = 0;
13030Sstevel@tonic-gate 					continue;
13040Sstevel@tonic-gate 				}
13050Sstevel@tonic-gate 				goto out;
13060Sstevel@tonic-gate 			}
13070Sstevel@tonic-gate 
13080Sstevel@tonic-gate 			error = cachefs_getcookie(entry_vp, &cookie, NULL, cr,
13090Sstevel@tonic-gate 				TRUE);
13100Sstevel@tonic-gate 			if (error) {
13110Sstevel@tonic-gate #ifdef CFSDEBUG
13120Sstevel@tonic-gate 				CFS_DEBUG(CFSDEBUG_DIR)
13130Sstevel@tonic-gate 					printf("\t%s: getcookie error\n",
13140Sstevel@tonic-gate 					    dep->d_name);
13150Sstevel@tonic-gate #endif /* CFSDEBUG */
13160Sstevel@tonic-gate 				VN_RELE(entry_vp);
13170Sstevel@tonic-gate 				goto out;
13180Sstevel@tonic-gate 			}
13190Sstevel@tonic-gate 			CACHEFS_FID_COPY(&cookie, &dep->d_cookie);
13200Sstevel@tonic-gate 			dep->d_flag |= CDE_COMPLETE;
13210Sstevel@tonic-gate 
13220Sstevel@tonic-gate 			if ((! acltoo) ||
13230Sstevel@tonic-gate 			    (! cachefs_vtype_aclok(entry_vp)) ||
13240Sstevel@tonic-gate 			    (fscp->fs_info.fi_mntflags & CFS_NOACL)) {
13250Sstevel@tonic-gate 				VN_RELE(entry_vp);
13260Sstevel@tonic-gate 				continue;
13270Sstevel@tonic-gate 			}
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate 			error = cachefs_cnode_make(&dep->d_id, fscp, &cookie,
13300Sstevel@tonic-gate 			    NULL, entry_vp, cr, 0, &cp);
13310Sstevel@tonic-gate 			VN_RELE(entry_vp);
13320Sstevel@tonic-gate 			if (error != 0)
13330Sstevel@tonic-gate 				goto out;
13340Sstevel@tonic-gate 
13350Sstevel@tonic-gate 			ASSERT(cp != NULL);
13360Sstevel@tonic-gate 			mutex_enter(&cp->c_statelock);
13370Sstevel@tonic-gate 
13380Sstevel@tonic-gate 			if ((cp->c_flags & CN_NOCACHE) ||
13390Sstevel@tonic-gate 			    (cp->c_metadata.md_flags & MD_ACL)) {
13400Sstevel@tonic-gate 				mutex_exit(&cp->c_statelock);
13410Sstevel@tonic-gate 				VN_RELE(CTOV(cp));
13420Sstevel@tonic-gate 				continue;
13430Sstevel@tonic-gate 			}
13440Sstevel@tonic-gate 
13450Sstevel@tonic-gate 			(void) cachefs_cacheacl(cp, NULL);
13460Sstevel@tonic-gate 			mutex_exit(&cp->c_statelock);
13470Sstevel@tonic-gate 			VN_RELE(CTOV(cp));
13480Sstevel@tonic-gate 		}
13490Sstevel@tonic-gate 
13500Sstevel@tonic-gate 		/*
13510Sstevel@tonic-gate 		 * We must now re-lock the page corresponding to the frontvp,
13520Sstevel@tonic-gate 		 * and copy our staging buffer onto it.
13530Sstevel@tonic-gate 		 */
13540Sstevel@tonic-gate 		if (error = fbread(frontvp, (offset_t)blockoff,
13550Sstevel@tonic-gate 		    MAXBSIZE, S_OTHER, &fbp))
13560Sstevel@tonic-gate 			goto out;
13570Sstevel@tonic-gate 
13580Sstevel@tonic-gate 		bcopy(buf, fbp->fb_addr, MAXBSIZE);
13590Sstevel@tonic-gate 		(void) fbdwrite(fbp);
13600Sstevel@tonic-gate 	}
13610Sstevel@tonic-gate 
13620Sstevel@tonic-gate out:
13630Sstevel@tonic-gate 	kmem_free(buf, MAXBSIZE);
13640Sstevel@tonic-gate 	return (error);
13650Sstevel@tonic-gate }
1366