summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/fs/cachefs/cachefs_dir.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/uts/common/fs/cachefs/cachefs_dir.c
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/uts/common/fs/cachefs/cachefs_dir.c')
-rw-r--r--usr/src/uts/common/fs/cachefs/cachefs_dir.c1366
1 files changed, 1366 insertions, 0 deletions
diff --git a/usr/src/uts/common/fs/cachefs/cachefs_dir.c b/usr/src/uts/common/fs/cachefs/cachefs_dir.c
new file mode 100644
index 0000000000..8e586946a6
--- /dev/null
+++ b/usr/src/uts/common/fs/cachefs/cachefs_dir.c
@@ -0,0 +1,1366 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/cred.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+#include <sys/uio.h>
+#include <sys/tiuser.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/ioctl.h>
+#include <sys/statvfs.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+#include <sys/cmn_err.h>
+#include <sys/utsname.h>
+#include <sys/modctl.h>
+#include <sys/dirent.h>
+#include <sys/fbuf.h>
+#include <rpc/types.h>
+#include <vm/seg.h>
+#include <vm/faultcode.h>
+#include <vm/hat.h>
+#include <vm/seg_map.h>
+#include <sys/fs/cachefs_fs.h>
+#include <sys/fs/cachefs_dir.h>
+#include <sys/fs/cachefs_log.h>
+
+/* forward declarations */
+static int cachefs_dir_getentrys(struct cnode *, u_offset_t, u_offset_t *,
+ uint_t *, uint_t, caddr_t, int *);
+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);
+static int cachefs_dir_extend(cnode_t *, u_offset_t *, int incr_frontblks);
+static int cachefs_dir_fill_common(cnode_t *dcp, cred_t *cr,
+ vnode_t *frontvp, vnode_t *backvp, u_offset_t *frontsize);
+static int cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp,
+ vnode_t *frontvp, cred_t *cr, int acltoo);
+
+
+
+/*
+ * cachefs_dir_look() called mainly by lookup (and create), looks up the cached
+ * directory for an entry and returns the information there. If the directory
+ * entry doesn't exist return ENOENT, if it is incomplete, return EINVAL.
+ * Should only call this routine if the dir is populated.
+ * Returns ENOTDIR if dir gets nuked because of front file problems.
+ */
+int
+cachefs_dir_look(cnode_t *dcp, char *nm, fid_t *cookiep, uint_t *flagp,
+ u_offset_t *d_offsetp, cfs_cid_t *cidp)
+{
+ int error;
+ struct vattr va;
+ u_offset_t blockoff = 0LL;
+ uint_t offset = 0; /* offset inside the block of size MAXBSIZE */
+ vnode_t *dvp;
+ struct fscache *fscp = C_TO_FSCACHE(dcp);
+ cachefscache_t *cachep = fscp->fs_cache;
+ int nmlen;
+ struct fbuf *fbp;
+
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_look: ENTER dcp %p nm %s\n", (void *)dcp,
+ nm);
+#endif
+ ASSERT(CTOV(dcp)->v_type == VDIR);
+ ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
+ ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
+
+ if (dcp->c_frontvp == NULL)
+ (void) cachefs_getfrontfile(dcp);
+ if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
+ error = ENOTDIR;
+ goto out;
+ }
+
+ dvp = dcp->c_frontvp;
+ va.va_mask = AT_SIZE; /* XXX should save dir size */
+ error = VOP_GETATTR(dvp, &va, 0, kcred);
+ if (error) {
+ cachefs_inval_object(dcp);
+ error = ENOTDIR;
+ goto out;
+ }
+
+ ASSERT(va.va_size != 0LL);
+ nmlen = (int)strlen(nm);
+ while (blockoff < va.va_size) {
+ offset = 0;
+ error =
+ fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
+ if (error)
+ goto out;
+
+ while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
+ struct c_dirent *dep;
+
+ dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
+ offset);
+ if ((dep->d_flag & CDE_VALID) &&
+ (nmlen == dep->d_namelen) &&
+ strcmp(dep->d_name, nm) == 0) {
+ if (dep->d_flag & CDE_COMPLETE) {
+ if (cookiep) {
+ CACHEFS_FID_COPY(&dep->d_cookie,
+ cookiep);
+ }
+ if (flagp)
+ *flagp = dep->d_flag;
+ error = 0;
+ } else {
+ error = EINVAL;
+ }
+ if (cidp)
+ *cidp = dep->d_id;
+ if (d_offsetp)
+ *d_offsetp = offset + blockoff;
+ fbrelse(fbp, S_OTHER);
+ goto out;
+ }
+ ASSERT(dep->d_length != 0);
+ offset += dep->d_length;
+ }
+ fbrelse(fbp, S_OTHER);
+ blockoff += MAXBSIZE;
+ }
+ error = ENOENT;
+
+out:
+ if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR))
+ cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp,
+ &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno, 0);
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("c_dir_look: EXIT error = %d\n", error);
+#endif
+ return (error);
+}
+
+/*
+ * creates a new directory and populates it with "." and ".."
+ */
+int
+cachefs_dir_new(cnode_t *dcp, cnode_t *cp)
+{
+ int error = 0;
+ struct c_dirent *dep;
+ u_offset_t size;
+ int len;
+ struct fbuf *fbp;
+#ifdef CFSDEBUG
+ struct vattr va;
+
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("c_dir_new: ENTER dcp %p cp %p\n", (void *)dcp,
+ (void *)cp);
+#endif
+
+ ASSERT(MUTEX_HELD(&cp->c_statelock));
+ ASSERT(CTOV(cp)->v_type == VDIR);
+ ASSERT((cp->c_flags & CN_ASYNC_POPULATE) == 0);
+ ASSERT(CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(dcp)) == 0);
+
+ if (cp->c_frontvp == NULL) {
+ error = cachefs_getfrontfile(cp);
+ if (error)
+ goto out;
+ }
+
+#ifdef CFSDEBUG
+ va.va_mask = AT_SIZE;
+ error = VOP_GETATTR(cp->c_frontvp, &va, 0, kcred);
+ if (error)
+ goto out;
+ ASSERT(va.va_size == 0);
+#endif
+
+ /*
+ * Extend the directory by one MAXBSIZE chunk
+ */
+ size = 0LL;
+ error = cachefs_dir_extend(cp, &size, 1);
+ if (error != 0)
+ goto out;
+ error = fbread(cp->c_frontvp, (offset_t)0, MAXBSIZE, S_OTHER, &fbp);
+ if (error)
+ goto out;
+
+ /*
+ * Insert "." and ".."
+ */
+ len = (int)CDE_SIZE(".");
+ dep = (struct c_dirent *)fbp->fb_addr;
+ dep->d_length = len;
+ dep->d_offset = (offset_t)len;
+ dep->d_flag = CDE_VALID | CDE_COMPLETE;
+ CACHEFS_FID_COPY(&cp->c_cookie, &dep->d_cookie);
+ dep->d_id = cp->c_id;
+ dep->d_namelen = 1;
+ bcopy(".", dep->d_name, 2);
+
+ dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + len);
+ dep->d_length = MAXBSIZE - len;
+ dep->d_offset = MAXBSIZE;
+ dep->d_flag = CDE_VALID | CDE_COMPLETE;
+ CACHEFS_FID_COPY(&dcp->c_cookie, &dep->d_cookie);
+ dep->d_id = dcp->c_id;
+ dep->d_namelen = 2;
+ bcopy("..", dep->d_name, 3);
+
+ (void) fbdwrite(fbp);
+#ifdef INVALREADDIR
+ cp->c_metadata.md_flags |= MD_POPULATED | MD_INVALREADDIR;
+#else
+ cp->c_metadata.md_flags |= MD_POPULATED;
+#endif
+ cp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
+out:
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_new: EXIT error = %d\n", error);
+#endif
+ return (error);
+}
+
+/*
+ * cachefs_dir_enter adds a new directory entry. Takes as input a fid,
+ * fileno and a sync flag. Most of the time, the caller is content with the
+ * write to the (front) directory being done async. The exception being - for
+ * local files, we should make sure that the directory entry is made
+ * synchronously. That is notified by the caller.
+ * issync == 0 || issync == SM_ASYNC !
+ *
+ * The new entry is inserted at the end, so that we can generate local offsets
+ * which are compatible with the backfs offsets (which are used when
+ * disconnected.
+ */
+int
+cachefs_dir_enter(cnode_t *dcp, char *nm, fid_t *cookiep, cfs_cid_t *cidp,
+ int issync)
+{
+ struct vattr va;
+ int offset;
+ u_offset_t blockoff = 0LL;
+ u_offset_t prev_offset;
+ int error = 0;
+ vnode_t *dvp;
+ struct c_dirent *dep;
+ uint_t esize;
+ u_offset_t dirsize;
+ struct fbuf *fbp;
+
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("c_dir_enter: ENTER dcp %p nm %s dirflg %x\n",
+ (void *)dcp, nm, dcp->c_metadata.md_flags);
+#endif
+
+ ASSERT(MUTEX_HELD(&dcp->c_statelock));
+ ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
+ ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
+ ASSERT(CTOV(dcp)->v_type == VDIR);
+ ASSERT(issync == 0 || issync == SM_ASYNC);
+ ASSERT(strlen(nm) <= MAXNAMELEN);
+
+ if (dcp->c_frontvp == NULL)
+ (void) cachefs_getfrontfile(dcp);
+ if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
+ error = ENOTDIR;
+ goto out;
+ }
+ dvp = dcp->c_frontvp;
+
+ /*
+ * Get the current EOF for the directory(data file)
+ */
+ va.va_mask = AT_SIZE;
+ error = VOP_GETATTR(dvp, &va, 0, kcred);
+ if (error) {
+ cachefs_inval_object(dcp);
+ error = ENOTDIR;
+ goto out;
+ }
+
+ /*
+ * Get the last block of the directory
+ */
+ dirsize = va.va_size;
+ ASSERT(dirsize != 0LL);
+ ASSERT(!(dirsize & MAXBOFFSET));
+ ASSERT(dirsize <= MAXOFF_T);
+ blockoff = dirsize - MAXBSIZE;
+ error = fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
+ if (error)
+ goto out;
+
+ /*
+ * Find the last entry
+ */
+ offset = 0;
+ prev_offset = blockoff;
+ for (;;) {
+ dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + offset);
+ if (offset + dep->d_length == MAXBSIZE)
+ break;
+ prev_offset = dep->d_offset;
+ offset += dep->d_length;
+ ASSERT(offset < MAXBSIZE);
+ }
+ esize = C_DIRSIZ(dep);
+
+ if (dep->d_length - esize >= CDE_SIZE(nm)) {
+ /*
+ * It has room. If the entry is not valid, we can just use
+ * it. Otherwise, we need to adjust its length and offset
+ */
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR) {
+ if (prev_offset >= dep->d_offset) {
+ printf("cachefs_dir_enter: looks like "
+ "we might fail the assert\n");
+ printf("addr %p, offset %x, "
+ "prev_offset %llx, dep->d_offset %llx\n",
+ (void *)fbp->fb_addr, offset, prev_offset,
+ dep->d_offset);
+ offset = 0;
+ prev_offset = blockoff;
+ for (;;) {
+ dep = (struct c_dirent *)
+ ((uintptr_t)fbp->fb_addr + offset);
+ printf("offset %x, prev_offset %llx\n",
+ offset, prev_offset);
+ printf("dep->d_offset %llx, "
+ "dep->d_length %x\n",
+ dep->d_offset, dep->d_length);
+ if (offset + dep->d_length == MAXBSIZE)
+ break;
+ prev_offset = dep->d_offset;
+ offset += dep->d_length;
+ }
+ }
+ }
+#endif /* CFSDEBUG */
+
+ if (offset)
+ ASSERT(prev_offset < dep->d_offset);
+ if (dep->d_flag & CDE_VALID) {
+ dep->d_length = esize;
+ dep->d_offset = prev_offset + (u_offset_t)esize;
+ dep = (struct c_dirent *)((uintptr_t)dep + esize);
+ }
+ dep->d_length = (int)((offset_t)MAXBSIZE -
+ ((uintptr_t)dep - (uintptr_t)fbp->fb_addr));
+ } else {
+ /*
+ * No room - so extend the file by one more
+ * MAXBSIZE chunk, and fit the entry there.
+ */
+ fbrelse(fbp, S_OTHER);
+ error = cachefs_dir_extend(dcp, &dirsize, 1);
+ if (error != 0)
+ goto out;
+ error =
+ fbread(dvp, (offset_t)va.va_size, MAXBSIZE, S_OTHER, &fbp);
+ if (error)
+ goto out;
+ dep = (struct c_dirent *)fbp->fb_addr;
+ dep->d_length = MAXBSIZE;
+ }
+
+ /*
+ * Fill in the rest of the new entry
+ */
+ dep->d_offset = dirsize;
+ dep->d_flag = CDE_VALID;
+ if (cookiep) {
+ dep->d_flag |= CDE_COMPLETE;
+ CACHEFS_FID_COPY(cookiep, &dep->d_cookie);
+ }
+ dep->d_id = *cidp;
+ dep->d_namelen = (ushort_t)strlen(nm);
+ (void) bcopy(nm, dep->d_name, dep->d_namelen + 1);
+
+#ifdef INVALREADDIR
+ dcp->c_metadata.md_flags |= MD_INVALREADDIR;
+#endif
+ dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
+ if (issync)
+ (void) fbwrite(fbp);
+ else
+ (void) fbdwrite(fbp);
+out:
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_enter: EXIT error = %d\n", error);
+#endif
+ return (error);
+}
+
+/*
+ * Quite simple, if the deleted entry is the first in the MAXBSIZE block,
+ * we simply mark it invalid. Otherwise, the deleted entries d_length is
+ * just added to the previous entry.
+ */
+int
+cachefs_dir_rmentry(cnode_t *dcp, char *nm)
+{
+ u_offset_t blockoff = 0LL;
+ int offset = 0;
+ struct vattr va;
+ int error = ENOENT;
+ vnode_t *dvp;
+ int nmlen;
+ struct fbuf *fbp;
+
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_rmentry: ENTER dcp %p nm %s\n",
+ (void *)dcp, nm);
+#endif
+ ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
+ ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
+
+ if (dcp->c_frontvp == NULL)
+ (void) cachefs_getfrontfile(dcp);
+ if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
+ error = ENOTDIR;
+ goto out;
+ }
+ dvp = dcp->c_frontvp;
+
+ ASSERT(CTOV(dcp)->v_type == VDIR);
+ ASSERT((dcp->c_flags & CN_NOCACHE) == 0);
+ ASSERT(dvp != NULL);
+ va.va_mask = AT_SIZE;
+ error = VOP_GETATTR(dvp, &va, 0, kcred);
+ if (error) {
+ cachefs_inval_object(dcp);
+ error = ENOTDIR;
+ goto out;
+ }
+ ASSERT(va.va_size != 0LL);
+
+ nmlen = (int)strlen(nm);
+ while (blockoff < va.va_size) {
+ uint_t *last_len;
+
+ offset = 0;
+ last_len = NULL;
+ error =
+ fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
+ if (error)
+ goto out;
+ while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
+ struct c_dirent *dep;
+
+ dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
+ offset);
+ if ((dep->d_flag & CDE_VALID) &&
+ (nmlen == dep->d_namelen) &&
+ strcmp(dep->d_name, nm) == 0) {
+ /*
+ * Found the entry. If this was the first entry
+ * in the MAXBSIZE block, Mark it invalid. Else
+ * add it's length to the previous entry's
+ * length.
+ */
+ if (last_len == NULL) {
+ ASSERT(offset == 0);
+ dep->d_flag = 0;
+ } else
+ *last_len += dep->d_length;
+ (void) fbdwrite(fbp);
+ dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
+ goto out;
+ }
+ last_len = &dep->d_length;
+ offset += dep->d_length;
+ }
+ fbrelse(fbp, S_OTHER);
+ blockoff += MAXBSIZE;
+ }
+ error = ENOENT;
+
+out:
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_rmentry: EXIT error = %d\n", error);
+#endif
+ return (error);
+}
+
+#if 0
+/*
+ * This function is used only in cachefs_lookup_back() routine but
+ * is inside #if 0 directive in this routine. So I am keeping this
+ * routine also in #if 0 directive.
+ */
+
+/*
+ * This function fills in the cookie and file no of the directory entry
+ * at the offset specified by offset - In other words, makes the entry
+ * "complete".
+ */
+int
+cachefs_dir_modentry(cnode_t *dcp, u_offset_t offset, fid_t *cookiep,
+ cfs_cid_t *cidp)
+{
+ struct c_dirent *dep;
+ u_offset_t blockoff = (offset & (offset_t)MAXBMASK);
+ uint_t off = (uint_t)(offset & (offset_t)MAXBOFFSET);
+ struct fbuf *fbp;
+ vnode_t *dvp;
+ int error = 0;
+
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_modentry: ENTER dcp %p offset %lld\n",
+ (void *)dcp, offset);
+#endif
+ ASSERT(CTOV(dcp)->v_type == VDIR);
+ ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
+ ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
+
+ if (dcp->c_frontvp == NULL)
+ (void) cachefs_getfrontfile(dcp);
+ if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
+ return;
+ }
+ dvp = dcp->c_frontvp;
+
+ error = fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
+ if (error)
+ goto out;
+ dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
+ if (cookiep) {
+ dep->d_flag |= CDE_COMPLETE;
+ CACHEFS_FID_COPY(cookiep, &dep->d_cookie);
+ }
+ if (cidp)
+ dep->d_id = *cidp;
+ (void) fbdwrite(fbp);
+ dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
+
+out:
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_modentry: EXIT\n");
+#endif
+ return (error);
+}
+
+#endif /* of #if 0 */
+
+/*
+ * Called by cachefs_read_dir(). Gets a bunch if directory entries into buf and
+ * packs them into buf.
+ */
+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)
+{
+
+#define DIR_ENDOFF 0x7fffffffLL
+
+ struct vattr va;
+ struct c_dirent *dep;
+ struct fbuf *fbp = NULL;
+ struct dirent64 *gdp;
+ u_offset_t blockoff;
+ uint_t off;
+ int error;
+ vnode_t *dvp = dcp->c_frontvp;
+
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_getentrys: "
+ "ENTER dcp %p beg_off %lld mdflags %x cflags %x\n",
+ dcp, beg_off, dcp->c_metadata.md_flags, dcp->c_flags);
+#endif
+
+ ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
+
+ /*
+ * blockoff has the offset of the MAXBSIZE block that contains the
+ * entry to start with. off contains the offset relative to the
+ * begining of the MAXBSIZE block.
+ */
+ if (eofp)
+ *eofp = 0;
+ gdp = (struct dirent64 *)buf;
+ *cntp = bufsize;
+ va.va_mask = AT_SIZE;
+ error = VOP_GETATTR(dvp, &va, 0, kcred);
+ if (error) {
+ *cntp = 0;
+ *last_offp = 0;
+ if (eofp)
+ *eofp = 1;
+ goto out;
+ }
+ ASSERT(va.va_size != 0LL);
+
+ if (beg_off == DIR_ENDOFF) {
+ *cntp = 0;
+ *last_offp = DIR_ENDOFF;
+ if (eofp)
+ *eofp = 1;
+ goto out;
+ }
+
+ /*
+ * locate the offset where we start reading.
+ */
+ for (blockoff = 0; blockoff < va.va_size; blockoff += MAXBSIZE) {
+ error =
+ fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
+ if (error)
+ goto out;
+ dep = (struct c_dirent *)fbp->fb_addr;
+ off = 0;
+ while (off < MAXBSIZE && dep->d_offset <= beg_off) {
+ off += dep->d_length;
+ dep =
+ (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
+ }
+ if (off < MAXBSIZE)
+ break;
+ fbrelse(fbp, S_OTHER);
+ fbp = NULL;
+ }
+
+ if (blockoff >= va.va_size) {
+ *cntp = 0;
+ *last_offp = DIR_ENDOFF;
+ if (eofp)
+ *eofp = 1;
+ goto out;
+ }
+
+ /*
+ * Just load up the buffer with directory entries.
+ */
+ for (;;) {
+ uint_t size;
+ int this_reclen;
+
+ ASSERT((uintptr_t)dep < ((uintptr_t)fbp->fb_addr + MAXBSIZE));
+ if (dep->d_flag & CDE_VALID) {
+ this_reclen = DIRENT64_RECLEN(dep->d_namelen);
+ size = C_DIRSIZ(dep);
+ ASSERT(size < MAXBSIZE);
+ if (this_reclen > bufsize)
+ break;
+ ASSERT(dep->d_namelen <= MAXNAMELEN);
+ ASSERT(dep->d_offset > (*last_offp));
+ gdp->d_ino = dep->d_id.cid_fileno;
+ gdp->d_off = dep->d_offset;
+
+ /* use strncpy(9f) to zero out uninitialized bytes */
+
+ ASSERT(strlen(dep->d_name) + 1 <=
+ DIRENT64_NAMELEN(this_reclen));
+ (void) strncpy(gdp->d_name, dep->d_name,
+ DIRENT64_NAMELEN(this_reclen));
+
+ gdp->d_reclen = (ushort_t)this_reclen;
+ bufsize -= this_reclen;
+ gdp = (struct dirent64 *)((uintptr_t)gdp +
+ gdp->d_reclen);
+ *last_offp = dep->d_offset;
+ }
+
+ /*
+ * Increment the offset. If we've hit EOF, fill in
+ * the lastoff and current entries d_off field.
+ */
+ off += dep->d_length;
+ ASSERT(off <= MAXBSIZE);
+ if ((blockoff + off) >= va.va_size) {
+ *last_offp = DIR_ENDOFF;
+ if (eofp)
+ *eofp = 1;
+ break;
+ }
+ /*
+ * If off == MAXBSIZE, then we need to adjust our
+ * window to the next MAXBSIZE block of the directory.
+ * Adjust blockoff, off and map it in. Also, increment
+ * the directory and buffer pointers.
+ */
+ if (off == MAXBSIZE) {
+ fbrelse(fbp, S_OTHER);
+ fbp = NULL;
+ off = 0;
+ blockoff += MAXBSIZE;
+ error = fbread(dvp, (offset_t)blockoff, MAXBSIZE,
+ S_OTHER, &fbp);
+ if (error)
+ goto out;
+ }
+ dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
+ }
+ *cntp -= bufsize;
+out:
+ /*
+ * Release any buffer and maping that may exist.
+ */
+ if (fbp)
+ (void) fbrelse(fbp, S_OTHER);
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("ccachefs_dir_getentrys: EXIT error = %d\n", error);
+#endif
+ return (error);
+}
+
+/*
+ * Called by cachefs_readdir(). Fills a directory request from the cache
+ */
+int
+cachefs_dir_read(struct cnode *dcp, struct uio *uiop, int *eofp)
+{
+ int error;
+ uint_t count;
+ uint_t size;
+ caddr_t buf;
+ u_offset_t next = uiop->uio_loffset;
+ struct fscache *fscp = C_TO_FSCACHE(dcp);
+ cachefscache_t *cachep = fscp->fs_cache;
+ caddr_t chrp, end;
+ dirent64_t *de;
+
+ ASSERT(CTOV(dcp)->v_type == VDIR);
+ ASSERT(RW_READ_HELD(&dcp->c_rwlock));
+
+ ASSERT(next <= MAXOFF_T);
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_read: ENTER dcp %p\n", (void *)dcp);
+#endif
+ ASSERT((dcp->c_metadata.md_flags & (MD_FILE|MD_POPULATED)) ==
+ (MD_FILE|MD_POPULATED));
+ ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
+
+ if (dcp->c_frontvp == NULL)
+ (void) cachefs_getfrontfile(dcp);
+ if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) {
+ error = ENOTDIR;
+ goto out;
+ }
+
+ size = (uint_t)uiop->uio_resid;
+ buf = cachefs_kmem_alloc(size, KM_SLEEP);
+ error = cachefs_dir_getentrys(dcp, next, &next, &count, size,
+ buf, eofp);
+ if (error == 0 && (int)count > 0) {
+ ASSERT(count <= size);
+ if (fscp->fs_inum_size > 0) {
+ ino64_t newinum;
+
+ mutex_exit(&dcp->c_statelock);
+ mutex_enter(&fscp->fs_fslock);
+ end = (caddr_t)((uintptr_t)buf + count);
+ for (chrp = buf; chrp < end; chrp += de->d_reclen) {
+ de = (dirent64_t *)chrp;
+
+ newinum = cachefs_inum_real2fake(fscp,
+ de->d_ino);
+ if (newinum == 0)
+ newinum = cachefs_fileno_conflict(fscp,
+ de->d_ino);
+ de->d_ino = newinum;
+ }
+ mutex_exit(&fscp->fs_fslock);
+ mutex_enter(&dcp->c_statelock);
+ }
+ error = uiomove(buf, count, UIO_READ, uiop);
+ if (error == 0)
+ uiop->uio_loffset = next;
+ }
+ (void) cachefs_kmem_free(buf, size);
+out:
+ if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_RFDIR))
+ cachefs_log_rfdir(cachep, error, fscp->fs_cfsvfsp,
+ &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno, 0);
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_read: EXIT error = %d\n", error);
+#endif
+ return (error);
+}
+
+/*
+ * Fully (including cookie) populates the directory from the back filesystem.
+ */
+int
+cachefs_dir_fill(cnode_t *dcp, cred_t *cr)
+{
+ int error = 0;
+ u_offset_t frontsize;
+ struct fscache *fscp = C_TO_FSCACHE(dcp);
+
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_fill: ENTER dcp %p\n", (void *)dcp);
+#endif
+ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
+ ASSERT(MUTEX_HELD(&dcp->c_statelock));
+
+ /* XXX for now return success if async populate is scheduled */
+ if (dcp->c_flags & CN_ASYNC_POPULATE)
+ goto out;
+
+ /* get the back vp */
+ if (dcp->c_backvp == NULL) {
+ error = cachefs_getbackvp(fscp, dcp);
+ if (error) {
+ goto out;
+ }
+ }
+
+ /* get the front file vp */
+ if (dcp->c_frontvp == NULL)
+ (void) cachefs_getfrontfile(dcp);
+ if (dcp->c_flags & CN_NOCACHE) {
+ error = ENOTDIR;
+ goto out;
+ }
+
+ /* if dir was modified, toss old contents */
+ if (dcp->c_metadata.md_flags & MD_INVALREADDIR) {
+ cachefs_inval_object(dcp);
+ if (dcp->c_flags & CN_NOCACHE) {
+ error = ENOTDIR;
+ goto out;
+ }
+ }
+
+ error = cachefs_dir_fill_common(dcp, cr,
+ dcp->c_frontvp, dcp->c_backvp, &frontsize);
+ if (error == 0)
+ error = cachefs_dir_complete(fscp, dcp->c_backvp,
+ dcp->c_frontvp, cr, 0);
+ if (error != 0)
+ goto out;
+
+ /*
+ * Mark the directory as not empty. Also bang the flag that says that
+ * this directory needs to be sync'ed on inactive.
+ */
+ dcp->c_metadata.md_flags |= MD_POPULATED;
+ dcp->c_metadata.md_flags &= ~MD_INVALREADDIR;
+ dcp->c_flags |= CN_UPDATED | CN_NEED_FRONT_SYNC;
+ /*LINTED alignment okay*/
+ dcp->c_metadata.md_frontblks = frontsize / MAXBSIZE;
+
+out:
+ if (error) {
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_INVALIDATE)
+ printf("c_dir_fill: invalidating %llu\n",
+ (u_longlong_t)dcp->c_id.cid_fileno);
+#endif
+ cachefs_inval_object(dcp);
+ }
+
+ return (error);
+}
+
+/*
+ * Does work of populating directory.
+ * Must be called while holding dcp->c_statelock
+ */
+
+static int
+cachefs_dir_fill_common(cnode_t *dcp, cred_t *cr,
+ vnode_t *frontvp, vnode_t *backvp, u_offset_t *frontsize)
+{
+ int error = 0;
+ struct uio uio;
+ struct iovec iov;
+ caddr_t buf = NULL;
+ int count;
+ int eof = 0;
+ u_offset_t frontoff;
+ struct fscache *fscp = C_TO_FSCACHE(dcp);
+ cachefscache_t *cachep = fscp->fs_cache;
+#ifdef DEBUG
+ int loop_count = 0;
+#endif
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_fill_common: ENTER dcp %p\n", (void *)dcp);
+#endif
+
+ ASSERT(MUTEX_HELD(&dcp->c_statelock));
+
+ frontoff = *frontsize = 0LL;
+
+ buf = cachefs_kmem_alloc(MAXBSIZE, KM_SLEEP);
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_fmode = 0;
+ uio.uio_extflg = UIO_COPY_CACHED;
+ uio.uio_loffset = 0;
+ for (;;) {
+#ifdef DEBUG
+ loop_count++;
+#endif
+ /*
+ * Read in a buffer's worth of dirents and enter them in to the
+ * directory.
+ */
+ uio.uio_resid = MAXBSIZE;
+ iov.iov_base = buf;
+ iov.iov_len = MAXBSIZE;
+ (void) VOP_RWLOCK(backvp, V_WRITELOCK_FALSE, NULL);
+ error = VOP_READDIR(backvp, &uio, cr, &eof);
+ VOP_RWUNLOCK(backvp, V_WRITELOCK_FALSE, NULL);
+ if (error)
+ goto out;
+
+ /*LINTED alignment okay*/
+ count = MAXBSIZE - (int)uio.uio_resid;
+ ASSERT(count >= 0);
+ if (count > 0) {
+ if (error = cachefs_dir_stuff(dcp, count, buf,
+ frontvp, &frontoff, frontsize))
+ goto out;
+ ASSERT((*frontsize) != 0LL);
+ }
+ if (eof || count == 0)
+ break;
+ }
+
+ if (*frontsize == 0LL) {
+ /* keep us from caching an empty directory */
+ error = EINVAL;
+ goto out;
+ }
+
+out:
+ if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_FILLDIR))
+ cachefs_log_filldir(cachep, error, fscp->fs_cfsvfsp,
+ &dcp->c_metadata.md_cookie, dcp->c_id.cid_fileno,
+ *frontsize);
+ if (buf)
+ cachefs_kmem_free(buf, (uint_t)MAXBSIZE);
+
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_fill: EXIT error = %d\n", error);
+#endif
+ return (error);
+}
+
+/*
+ * If the directory contains only the elements "." and "..", then this returns
+ * 0, otherwise returns an error.
+ */
+int
+cachefs_dir_empty(cnode_t *dcp)
+{
+ struct vattr va;
+ u_offset_t blockoff = 0;
+ int offset;
+ struct fbuf *fbp;
+ int error;
+ vnode_t *dvp = dcp->c_frontvp;
+
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_dir_empty: ENTER dcp %p\n", (void *)dcp);
+#endif
+ ASSERT(CTOV(dcp)->v_type == VDIR);
+ ASSERT(dcp->c_metadata.md_flags & MD_POPULATED);
+ ASSERT((dcp->c_flags & CN_ASYNC_POPULATE) == 0);
+
+ if (dcp->c_frontvp == NULL)
+ (void) cachefs_getfrontfile(dcp);
+ if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0)
+ return (ENOTDIR);
+
+ va.va_mask = AT_SIZE;
+ error = VOP_GETATTR(dvp, &va, 0, kcred);
+ if (error)
+ return (ENOTDIR);
+
+ ASSERT(va.va_size != 0LL);
+ while (blockoff < va.va_size) {
+ offset = 0;
+ error =
+ fbread(dvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
+ if (error)
+ return (error);
+ while (offset < MAXBSIZE && (blockoff + offset) < va.va_size) {
+ struct c_dirent *dep;
+
+ dep = (struct c_dirent *)((uintptr_t)fbp->fb_addr +
+ offset);
+ if ((dep->d_flag & CDE_VALID) &&
+ ((strcmp(dep->d_name, ".") != 0) &&
+ (strcmp(dep->d_name, "..") != 0))) {
+ (void) fbrelse(fbp, S_OTHER);
+ return (0);
+ }
+ offset += dep->d_length;
+ }
+ (void) fbrelse(fbp, S_OTHER);
+ blockoff += MAXBSIZE;
+ }
+ return (EEXIST);
+}
+
+/*
+ * Called by cachefs_dir_fill() to stuff a buffer of dir entries into
+ * a front file. This is more efficient than repeated calls to
+ * cachefs_dir_enter, and it also allows us to maintain entries in backfs
+ * order (readdir requires that entry offsets be ascending).
+ */
+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)
+{
+ int error = 0;
+ struct fbuf *fbp;
+ struct c_dirent *cdep, *last;
+ struct dirent64 *dep;
+ int inblk, entsize;
+ u_offset_t blockoff = (*offsetp & (offset_t)MAXBMASK);
+ /*LINTED alignment okay*/
+ uint_t off = (uint_t)(*offsetp & (offset_t)MAXBOFFSET);
+
+ /*LINTED want count != 0*/
+ ASSERT(count > 0);
+
+ if (*offsetp >= *fsizep) {
+ error = cachefs_dir_extend(dcp, fsizep, 0);
+ if (error)
+ return (error);
+ }
+
+ ASSERT(*fsizep != 0LL);
+ last = NULL;
+ error = fbread(frontvp, (offset_t)blockoff, MAXBSIZE, S_OTHER, &fbp);
+ if (error)
+ return (error);
+ cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
+ inblk = MAXBSIZE-off;
+ if (*offsetp != 0) {
+ ASSERT(cdep->d_length == inblk);
+ inblk -= C_DIRSIZ(cdep);
+ last = cdep;
+ last->d_length -= inblk;
+ off += last->d_length;
+ cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
+ }
+ dep = (struct dirent64 *)buf;
+ /*LINTED want count != 0*/
+ while (count > 0) {
+ if (last) {
+ ASSERT(dep->d_off > last->d_offset);
+ }
+ entsize = (int)CDE_SIZE(dep->d_name);
+ if (entsize > inblk) {
+ if (last) {
+ last->d_length += inblk;
+ }
+ (void) fbwrite(fbp);
+ error = cachefs_dir_extend(dcp, fsizep, 0);
+ if (error)
+ return (error);
+ ASSERT(*fsizep != 0LL);
+ blockoff += MAXBSIZE;
+ error = fbread(frontvp, (offset_t)blockoff, MAXBSIZE,
+ S_OTHER, &fbp);
+ if (error)
+ return (error);
+ off = 0;
+ cdep = (struct c_dirent *)fbp->fb_addr;
+ inblk = MAXBSIZE;
+ last = NULL;
+ }
+ cdep->d_length = entsize;
+ cdep->d_id.cid_fileno = dep->d_ino;
+ cdep->d_id.cid_flags = 0;
+ cdep->d_namelen = (ushort_t)strlen(dep->d_name);
+ cdep->d_flag = CDE_VALID;
+ bcopy(dep->d_name, cdep->d_name, cdep->d_namelen+1);
+ cdep->d_offset = dep->d_off;
+ inblk -= entsize;
+ count -= dep->d_reclen;
+ dep = (struct dirent64 *)((uintptr_t)dep + dep->d_reclen);
+ *offsetp = blockoff + (u_offset_t)off;
+ off += entsize;
+ last = cdep;
+ cdep = (struct c_dirent *)((uintptr_t)fbp->fb_addr + off);
+ }
+ if (last) {
+ last->d_length += inblk;
+ }
+ (void) fbwrite(fbp);
+ return (error);
+}
+
+static int
+cachefs_dir_extend(cnode_t *dcp, u_offset_t *cursize, int incr_frontblks)
+{
+ struct vattr va;
+ cachefscache_t *cachep = C_TO_FSCACHE(dcp)->fs_cache;
+ int error = 0;
+ struct fscache *fscp = VFS_TO_FSCACHE(CTOV(dcp)->v_vfsp);
+
+ ASSERT(MUTEX_HELD(&dcp->c_statelock));
+ ASSERT(((*cursize) & (MAXBSIZE-1)) == 0);
+
+ va.va_mask = AT_SIZE;
+ va.va_size = (u_offset_t)(*cursize + MAXBSIZE);
+ error = cachefs_allocblocks(cachep, 1, dcp->c_metadata.md_rltype);
+ if (error)
+ return (error);
+ error = VOP_SETATTR(dcp->c_frontvp, &va, 0, kcred, NULL);
+ if (error) {
+ cachefs_freeblocks(cachep, 1, dcp->c_metadata.md_rltype);
+ return (error);
+ }
+ if (incr_frontblks)
+ dcp->c_metadata.md_frontblks++;
+ if (fscp->fs_cdconnected != CFS_CD_CONNECTED) {
+ dcp->c_size += MAXBSIZE;
+ dcp->c_attr.va_size = dcp->c_size;
+ }
+ *cursize += MAXBSIZE;
+ ASSERT(*cursize != 0LL);
+ if (incr_frontblks)
+ dcp->c_flags |= CN_UPDATED;
+ return (0);
+}
+
+int
+cachefs_async_populate_dir(struct cachefs_populate_req *pop, cred_t *cr,
+ vnode_t *backvp, vnode_t *frontvp)
+{
+ vnode_t *dvp = pop->cpop_vp;
+ struct cnode *dcp = VTOC(dvp);
+ u_offset_t frontsize;
+ int error = 0;
+
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_async_populate_dir: ENTER dvp %p\n",
+ (void *)dvp);
+#endif
+ ASSERT(MUTEX_HELD(&dcp->c_statelock));
+ ASSERT(dvp->v_type == VDIR);
+ ASSERT((dcp->c_metadata.md_flags & MD_POPULATED) == 0);
+ ASSERT(dcp->c_frontvp == frontvp);
+ ASSERT(dcp->c_backvp == backvp);
+ ASSERT(CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(dcp)) == 0);
+
+ /* if dir was modified, toss old contents */
+ if (dcp->c_metadata.md_flags & MD_INVALREADDIR) {
+ cachefs_inval_object(dcp);
+ if (dcp->c_flags & CN_NOCACHE) {
+ error = ENOTDIR;
+ goto out;
+ } else {
+ dcp->c_metadata.md_flags &= ~MD_INVALREADDIR;
+ }
+ }
+
+
+ error = cachefs_dir_fill_common(dcp, cr, frontvp, backvp, &frontsize);
+ if (error != 0)
+ goto out;
+ ASSERT(frontsize != 0LL);
+ mutex_exit(&dcp->c_statelock);
+ /*
+ * I don't like to break lock here but cachefs_dir_complete()
+ * needs it.
+ */
+ error = cachefs_dir_complete(C_TO_FSCACHE(dcp), backvp,
+ frontvp, cr, 1);
+ mutex_enter(&dcp->c_statelock);
+ if (error != 0)
+ goto out;
+ /* if went nocache while lock was dropped, get out */
+ if ((dcp->c_flags & CN_NOCACHE) || (dcp->c_frontvp == NULL)) {
+ error = EINVAL;
+ } else {
+ /* allocfile and allocblocks have already happened. */
+ dcp->c_metadata.md_frontblks = frontsize / MAXBSIZE;
+ }
+
+out:
+
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("cachefs_async_populate_dir: EXIT error = %d\n", error);
+#endif
+
+ return (error);
+}
+
+static int
+cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp, vnode_t *frontvp,
+ cred_t *cr, int acltoo)
+{
+ struct c_dirent *dep;
+ caddr_t buf = kmem_alloc(MAXBSIZE, KM_SLEEP);
+ struct vattr va;
+ u_offset_t blockoff;
+ int offset;
+ u_offset_t dir_size;
+ struct fbuf *fbp;
+ cnode_t *cp;
+ fid_t cookie;
+ vnode_t *entry_vp;
+ int error = 0;
+
+ /*
+ * note: caller better not hold a c_statelock if acltoo is set.
+ */
+
+ va.va_mask = AT_SIZE;
+ error = VOP_GETATTR(frontvp, &va, 0, cr);
+ if (error)
+ goto out;
+
+ ASSERT(va.va_size != 0LL);
+ dir_size = va.va_size;
+ ASSERT(dir_size <= MAXOFF_T);
+
+ for (blockoff = 0; blockoff < dir_size; blockoff += MAXBSIZE) {
+ if (error = fbread(frontvp, (offset_t)blockoff,
+ MAXBSIZE, S_OTHER, &fbp))
+ goto out;
+
+ /*
+ * We cannot hold any page locks across the below VOP
+ * operations. We thus copy the directory entries into a
+ * staging buffer, and release the page lock on the directory
+ * by calling fbrelse(). Once any necessary cnodes have
+ * been created, we'll reacquire the page lock with fbread()
+ * and copy the staging buffer back into the frontvp at
+ * blockoff.
+ */
+ bcopy(fbp->fb_addr, buf, MAXBSIZE);
+ fbrelse(fbp, S_OTHER);
+
+ for (offset = 0;
+ offset < MAXBSIZE &&
+ (blockoff + (u_offset_t)offset) < dir_size;
+ offset += dep->d_length) {
+
+ dep = (struct c_dirent *)((uintptr_t)buf + offset);
+ ASSERT(dep->d_length != 0);
+ if ((dep->d_flag & (CDE_VALID | CDE_COMPLETE)) !=
+ CDE_VALID)
+ continue;
+
+ error = VOP_LOOKUP(backvp, dep->d_name,
+ &entry_vp, (struct pathname *)NULL, 0,
+ (vnode_t *)NULL, cr);
+ if (error) {
+ /* lookup on .. in / on coc gets ENOENT */
+ if (error == ENOENT) {
+ error = 0;
+ continue;
+ }
+ goto out;
+ }
+
+ error = cachefs_getcookie(entry_vp, &cookie, NULL, cr,
+ TRUE);
+ if (error) {
+#ifdef CFSDEBUG
+ CFS_DEBUG(CFSDEBUG_DIR)
+ printf("\t%s: getcookie error\n",
+ dep->d_name);
+#endif /* CFSDEBUG */
+ VN_RELE(entry_vp);
+ goto out;
+ }
+ CACHEFS_FID_COPY(&cookie, &dep->d_cookie);
+ dep->d_flag |= CDE_COMPLETE;
+
+ if ((! acltoo) ||
+ (! cachefs_vtype_aclok(entry_vp)) ||
+ (fscp->fs_info.fi_mntflags & CFS_NOACL)) {
+ VN_RELE(entry_vp);
+ continue;
+ }
+
+ error = cachefs_cnode_make(&dep->d_id, fscp, &cookie,
+ NULL, entry_vp, cr, 0, &cp);
+ VN_RELE(entry_vp);
+ if (error != 0)
+ goto out;
+
+ ASSERT(cp != NULL);
+ mutex_enter(&cp->c_statelock);
+
+ if ((cp->c_flags & CN_NOCACHE) ||
+ (cp->c_metadata.md_flags & MD_ACL)) {
+ mutex_exit(&cp->c_statelock);
+ VN_RELE(CTOV(cp));
+ continue;
+ }
+
+ (void) cachefs_cacheacl(cp, NULL);
+ mutex_exit(&cp->c_statelock);
+ VN_RELE(CTOV(cp));
+ }
+
+ /*
+ * We must now re-lock the page corresponding to the frontvp,
+ * and copy our staging buffer onto it.
+ */
+ if (error = fbread(frontvp, (offset_t)blockoff,
+ MAXBSIZE, S_OTHER, &fbp))
+ goto out;
+
+ bcopy(buf, fbp->fb_addr, MAXBSIZE);
+ (void) fbdwrite(fbp);
+ }
+
+out:
+ kmem_free(buf, MAXBSIZE);
+ return (error);
+}