/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fs/fs_subr.h" void cachefs_addhash(struct cnode *); /* * Local functions */ static void sync_metadata(cnode_t *); static void drop_backvp(cnode_t *); static void allow_pendrm(cnode_t *cp); static int cachefs_unpack_common(vnode_t *vp); static int cachefs_unpackall_list(cachefscache_t *cachep, enum cachefs_rl_type type); static void cachefs_modified_fix(fscache_t *fscp); static void cachefs_iosetneedattrs(fscache_t *fscp, cfs_cid_t *cidp); #if (defined(_SYSCALL32_IMPL) || defined(_LP64)) #define CACHEFS_DECL(type, handle) \ type handle #define CACHEFS_TMPPTR_SET(in_addr, tmp_addr, tmp_ptr, type) \ tmp_ptr = (type *)(tmp_addr) #define CACHEFS_FID_COPYOUT(in_fidp, out_fidp) \ CACHEFS_FID_COPY((fid_t *)(in_fidp), (cfs_fid_t *)(out_fidp)) #define CACHEFS_FID_COPYIN(in_fidp, out_fidp) \ CACHEFS_FID_COPY((cfs_fid_t *)(in_fidp), (fid_t *)(out_fidp)) #define CACHEFS_VATTR_COPYOUT(in_vattrp, out_vattrp, error) \ if (!error) { \ CACHEFS_VATTR_TO_CFS_VATTR_COPY((vattr_t *)(in_vattrp), \ (cfs_vattr_t *)(out_vattrp), error); \ } #define CACHEFS_VATTR_COPYIN(in_vattrp, out_vattrp) \ CACHEFS_CFS_VATTR_TO_VATTR_COPY((cfs_vattr_t *)(in_vattrp), \ (vattr_t *)(out_vattrp)) #else /* not _SYSCALL32_IMPL || _LP64 */ #define CACHEFS_DECL(type, handle) #define CACHEFS_TMPPTR_SET(in_addr, tmp_addr, tmp_ptr, type) \ tmp_ptr = (type *)(in_addr) #define CACHEFS_FID_COPYOUT(in_fidp, out_fidp) #define CACHEFS_FID_COPYIN(in_fidp, out_fidp) #define CACHEFS_VATTR_COPYOUT(in_vattrp, out_vattrp, error) #define CACHEFS_VATTR_COPYIN(in_vattrp, out_vattrp) #endif /* _SYSCALL32_IMPL || _LP64 */ /* * Conjure up a credential from the partial credential stored in * a file. This is bogus and cachefs should really be fixed, but * this maintains maximum compatibility. * dl_cred *cr points to a basic credential followed directly by a buffer that * takes a number of groups. */ static cred_t * conj_cred(dl_cred_t *cr) { cred_t *newcr = crget(); (void) crsetresuid(newcr, cr->cr_ruid, cr->cr_uid, cr->cr_suid); (void) crsetresgid(newcr, cr->cr_rgid, cr->cr_gid, cr->cr_sgid); (void) crsetgroups(newcr, MIN(NGROUPS_MAX_DEFAULT, cr->cr_ngroups), cr->cr_groups); return (newcr); } /* * Pack a file in the cache * dvp is the directory the file resides in. * name is the name of the file. * Returns 0 or an error if could not perform the operation. */ int cachefs_pack(struct vnode *dvp, char *name, cred_t *cr) { fscache_t *fscp = C_TO_FSCACHE(VTOC(dvp)); int error = 0; int connected = 0; vnode_t *vp; /* * Return if NFSv4 is the backfs (no caching). */ if (CFS_ISFS_BACKFS_NFSV4(fscp)) { goto out; } for (;;) { /* get access to the file system */ error = cachefs_cd_access(fscp, connected, 0); if (error) break; /* lookup the file name */ error = cachefs_lookup_common(dvp, name, &vp, NULL, 0, NULL, cr); if (error == 0) { error = cachefs_pack_common(vp, cr); VN_RELE(vp); } if (CFS_TIMEOUT(fscp, error)) { if (fscp->fs_cdconnected == CFS_CD_CONNECTED) { cachefs_cd_release(fscp); cachefs_cd_timedout(fscp); connected = 0; continue; } else { cachefs_cd_release(fscp); connected = 1; continue; } } cachefs_cd_release(fscp); break; } out: return (error); } /* * Packs the file belonging to the passed in vnode. */ int cachefs_pack_common(vnode_t *vp, cred_t *cr) { cnode_t *cp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(cp); int error = 0; offset_t off; caddr_t buf; int buflen; rl_entry_t rl_ent; u_offset_t cnode_size; rw_enter(&cp->c_rwlock, RW_WRITER); mutex_enter(&cp->c_statelock); /* done if cannot write to cache */ if ((cp->c_filegrp->fg_flags & CFS_FG_WRITE) == 0) { error = EROFS; goto out; } /* done if not usable */ if (cp->c_flags & (CN_STALE | CN_DESTROY)) { error = ESTALE; goto out; } /* make sure up to date */ error = CFSOP_CHECK_COBJECT(fscp, cp, C_BACK_CHECK, cr); if (error) goto out; /* make it cachable */ cp->c_flags &= ~CN_NOCACHE; /* get a metadata slot if we do not have one yet */ if (cp->c_flags & CN_ALLOC_PENDING) { if (cp->c_filegrp->fg_flags & CFS_FG_ALLOC_ATTR) { (void) filegrp_allocattr(cp->c_filegrp); } error = filegrp_create_metadata(cp->c_filegrp, &cp->c_metadata, &cp->c_id); if (error) goto out; cp->c_flags &= ~CN_ALLOC_PENDING; cp->c_flags |= CN_UPDATED; } /* cache the ACL if necessary */ if (((fscp->fs_info.fi_mntflags & CFS_NOACL) == 0) && (cachefs_vtype_aclok(vp)) && ((cp->c_metadata.md_flags & MD_ACL) == 0)) { error = cachefs_cacheacl(cp, NULL); if (error != 0) goto out; } /* directory */ if (vp->v_type == VDIR) { if (cp->c_metadata.md_flags & MD_POPULATED) goto out; if (error = cachefs_dir_fill(cp, cr)) goto out; } /* regular file */ else if (vp->v_type == VREG) { if (cp->c_metadata.md_flags & MD_POPULATED) goto out; if (cp->c_backvp == NULL) { error = cachefs_getbackvp(fscp, cp); if (error) goto out; } if (cp->c_frontvp == NULL) { error = cachefs_getfrontfile(cp); if (error) goto out; } /* populate the file */ off = (offset_t)0; cnode_size = cp->c_attr.va_size; while (off < cnode_size) { if (!cachefs_check_allocmap(cp, off)) { u_offset_t popoff; size_t popsize; cachefs_cluster_allocmap(off, &popoff, &popsize, (size_t)DEF_POP_SIZE, cp); if (popsize != 0) { error = cachefs_populate(cp, popoff, popsize, cp->c_frontvp, cp->c_backvp, cp->c_size, cr); if (error) goto out; else cp->c_flags |= (CN_UPDATED | CN_NEED_FRONT_SYNC | CN_POPULATION_PENDING); popsize = popsize - (off - popoff); } } off += PAGESIZE; } } /* symbolic link */ else if (vp->v_type == VLNK) { if (cp->c_metadata.md_flags & (MD_POPULATED | MD_FASTSYMLNK)) goto out; /* get the sym link contents from the back fs */ error = cachefs_readlink_back(cp, cr, &buf, &buflen); if (error) goto out; /* try to cache the sym link */ error = cachefs_stuffsymlink(cp, buf, buflen); cachefs_kmem_free(buf, MAXPATHLEN); } /* assume that all other types fit in the attributes */ out: /* get the rl slot if needed */ if ((error == 0) && (cp->c_metadata.md_rlno == 0)) { rl_ent.rl_fileno = cp->c_id.cid_fileno; rl_ent.rl_local = (cp->c_id.cid_flags & CFS_CID_LOCAL) ? 1 : 0; rl_ent.rl_fsid = fscp->fs_cfsid; rl_ent.rl_attrc = 0; cp->c_metadata.md_rltype = CACHEFS_RL_NONE; error = cachefs_rl_alloc(fscp->fs_cache, &rl_ent, &cp->c_metadata.md_rlno); if (error == 0) error = filegrp_ffhold(cp->c_filegrp); } /* mark the file as packed */ if (error == 0) { /* modified takes precedence over packed */ if (cp->c_metadata.md_rltype != CACHEFS_RL_MODIFIED) { cachefs_rlent_moveto(fscp->fs_cache, CACHEFS_RL_PACKED, cp->c_metadata.md_rlno, cp->c_metadata.md_frontblks); cp->c_metadata.md_rltype = CACHEFS_RL_PACKED; } cp->c_metadata.md_flags |= MD_PACKED; cp->c_flags |= CN_UPDATED; } mutex_exit(&cp->c_statelock); rw_exit(&cp->c_rwlock); return (error); } /* * Unpack a file from the cache * dvp is the directory the file resides in. * name is the name of the file. * Returns 0 or an error if could not perform the operation. */ int cachefs_unpack(struct vnode *dvp, char *name, cred_t *cr) { fscache_t *fscp = C_TO_FSCACHE(VTOC(dvp)); int error = 0; int connected = 0; vnode_t *vp; /* Return error if NFSv4 is the backfs (no caching) */ if (CFS_ISFS_BACKFS_NFSV4(fscp)) { goto out; } for (;;) { /* get access to the file system */ error = cachefs_cd_access(fscp, connected, 0); if (error) break; /* lookup the file name */ error = cachefs_lookup_common(dvp, name, &vp, NULL, 0, NULL, cr); if (error == 0) { error = cachefs_unpack_common(vp); VN_RELE(vp); } if (CFS_TIMEOUT(fscp, error)) { if (fscp->fs_cdconnected == CFS_CD_CONNECTED) { cachefs_cd_release(fscp); cachefs_cd_timedout(fscp); connected = 0; continue; } else { cachefs_cd_release(fscp); connected = 1; continue; } } cachefs_cd_release(fscp); break; } out: return (error); } /* * Unpacks the file belonging to the passed in vnode. */ static int cachefs_unpack_common(vnode_t *vp) { cnode_t *cp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(cp); int error = 0; mutex_enter(&cp->c_statelock); /* nothing to do if not packed */ if ((cp->c_metadata.md_flags & MD_PACKED) == 0) goto out; /* nothing to do if cannot modify cache */ if ((cp->c_filegrp->fg_flags & CFS_FG_WRITE) == 0) { error = EROFS; goto out; } /* mark file as no longer packed */ ASSERT(cp->c_metadata.md_rlno); cp->c_metadata.md_flags &= ~MD_PACKED; cp->c_flags |= CN_UPDATED; /* done if file has been modified */ if (cp->c_metadata.md_rltype == CACHEFS_RL_MODIFIED) goto out; /* if there is no front file */ if ((cp->c_metadata.md_flags & MD_FILE) == 0) { /* nuke front file resources */ filegrp_ffrele(cp->c_filegrp); cachefs_rlent_moveto(fscp->fs_cache, CACHEFS_RL_FREE, cp->c_metadata.md_rlno, 0); cp->c_metadata.md_rlno = 0; cp->c_metadata.md_rltype = CACHEFS_RL_NONE; } /* else move the front file to the active list */ else { cachefs_rlent_moveto(fscp->fs_cache, CACHEFS_RL_ACTIVE, cp->c_metadata.md_rlno, cp->c_metadata.md_frontblks); cp->c_metadata.md_rltype = CACHEFS_RL_ACTIVE; } out: mutex_exit(&cp->c_statelock); return (error); } /* * Returns packing information on a file. * dvp is the directory the file resides in. * name is the name of the file. * *statusp is set to the status of the file * Returns 0 or an error if could not perform the operation. */ int cachefs_packinfo(struct vnode *dvp, char *name, int *statusp, cred_t *cr) { fscache_t *fscp = C_TO_FSCACHE(VTOC(dvp)); struct vnode *vp; struct cnode *cp; int error; int connected = 0; *statusp = 0; /* * Return if NFSv4 is the backfs (no caching). */ if (CFS_ISFS_BACKFS_NFSV4(fscp)) { goto out; } for (;;) { /* get access to the file system */ error = cachefs_cd_access(fscp, connected, 0); if (error) break; /* lookup the file name */ error = cachefs_lookup_common(dvp, name, &vp, NULL, 0, NULL, cr); if (CFS_TIMEOUT(fscp, error)) { if (fscp->fs_cdconnected == CFS_CD_CONNECTED) { cachefs_cd_release(fscp); cachefs_cd_timedout(fscp); connected = 0; continue; } else { cachefs_cd_release(fscp); connected = 1; continue; } } if (error) break; cp = VTOC(vp); mutex_enter(&cp->c_statelock); if (cp->c_metadata.md_flags & MD_PACKED) *statusp |= CACHEFS_PACKED_FILE; if (cp->c_metadata.md_flags & (MD_POPULATED | MD_FASTSYMLNK)) *statusp |= CACHEFS_PACKED_DATA; else if ((vp->v_type != VREG) && (vp->v_type != VDIR) && (vp->v_type != VLNK)) *statusp |= CACHEFS_PACKED_DATA; else if (cp->c_size == 0) *statusp |= CACHEFS_PACKED_DATA; if (cp->c_flags & CN_NOCACHE) *statusp |= CACHEFS_PACKED_NOCACHE; mutex_exit(&cp->c_statelock); VN_RELE(vp); cachefs_cd_release(fscp); break; } out: return (error); } /* * Finds all packed files in the cache and unpacks them. */ int cachefs_unpackall(vnode_t *vp) { fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); cachefscache_t *cachep = fscp->fs_cache; int error; /* * Return if NFSv4 is the backfs (no caching). */ if (CFS_ISFS_BACKFS_NFSV4(fscp)) { goto out; } error = cachefs_unpackall_list(cachep, CACHEFS_RL_PACKED); if (error) goto out; error = cachefs_unpackall_list(cachep, CACHEFS_RL_PACKED_PENDING); out: return (error); } /* * Finds all packed files on the specified list and unpacks them. */ static int cachefs_unpackall_list(cachefscache_t *cachep, enum cachefs_rl_type type) { fscache_t *fscp = NULL; cnode_t *cp; int error = 0; rl_entry_t rl_ent; cfs_cid_t cid; rl_ent.rl_current = type; for (;;) { /* get the next entry on the specified resource list */ error = cachefs_rlent_data(cachep, &rl_ent, NULL); if (error) { error = 0; break; } /* if the fscp we have does not match */ if ((fscp == NULL) || (fscp->fs_cfsid != rl_ent.rl_fsid)) { if (fscp) { cachefs_cd_release(fscp); fscache_rele(fscp); fscp = NULL; } /* get the file system cache object for this fsid */ mutex_enter(&cachep->c_fslistlock); fscp = fscache_list_find(cachep, rl_ent.rl_fsid); if (fscp == NULL) { fscp = fscache_create(cachep); error = fscache_activate(fscp, rl_ent.rl_fsid, NULL, NULL, 0); if (error) { cmn_err(CE_WARN, "cachefs: cache error, run fsck\n"); fscache_destroy(fscp); fscp = NULL; mutex_exit(&cachep->c_fslistlock); break; } fscache_list_add(cachep, fscp); } fscache_hold(fscp); mutex_exit(&cachep->c_fslistlock); /* get access to the file system */ error = cachefs_cd_access(fscp, 0, 0); if (error) { fscache_rele(fscp); fscp = NULL; break; } } /* get the cnode for the file */ cid.cid_fileno = rl_ent.rl_fileno; cid.cid_flags = rl_ent.rl_local ? CFS_CID_LOCAL : 0; error = cachefs_cnode_make(&cid, fscp, NULL, NULL, NULL, kcred, 0, &cp); if (error) { #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_IOCTL) printf("cachefs: cul: could not find %llu\n", (u_longlong_t)cid.cid_fileno); delay(5*hz); #endif continue; } /* unpack the file */ (void) cachefs_unpack_common(CTOV(cp)); VN_RELE(CTOV(cp)); } /* free up allocated resources */ if (fscp) { cachefs_cd_release(fscp); fscache_rele(fscp); } return (error); } /* * Identifies this process as the cachefsd. * Stays this way until close is done. */ int /*ARGSUSED*/ cachefs_io_daemonid(vnode_t *vp, void *dinp, void *doutp) { int error = 0; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); cachefscache_t *cachep = fscp->fs_cache; mutex_enter(&fscp->fs_cdlock); /* can only do this on the root of the file system */ if (vp != fscp->fs_rootvp) error = ENOENT; /* else if there already is a daemon running */ else if (fscp->fs_cddaemonid) error = EBUSY; /* else use the pid to identify the daemon */ else { fscp->fs_cddaemonid = ttoproc(curthread)->p_pid; cv_broadcast(&fscp->fs_cdwaitcv); } mutex_exit(&fscp->fs_cdlock); if (error == 0) { /* the daemon that takes care of root is special */ if (fscp->fs_flags & CFS_FS_ROOTFS) { mutex_enter(&cachep->c_contentslock); ASSERT(cachep->c_rootdaemonid == 0); cachep->c_rootdaemonid = fscp->fs_cddaemonid; mutex_exit(&cachep->c_contentslock); } } return (error); } /* * Returns the current state in doutp */ int /*ARGSUSED*/ cachefs_io_stateget(vnode_t *vp, void *dinp, void *doutp) { fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); int *statep = (int *)doutp; int state; /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); mutex_enter(&fscp->fs_cdlock); switch (fscp->fs_cdconnected) { case CFS_CD_CONNECTED: state = CFS_FS_CONNECTED; break; case CFS_CD_DISCONNECTED: state = CFS_FS_DISCONNECTED; break; case CFS_CD_RECONNECTING: state = CFS_FS_RECONNECTING; break; default: ASSERT(0); break; } mutex_exit(&fscp->fs_cdlock); *statep = state; return (0); } /* * Sets the state of the file system. */ int /*ARGSUSED*/ cachefs_io_stateset(vnode_t *vp, void *dinp, void *doutp) { fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); int error = 0; int nosig = 1; int state = *(int *)dinp; /* * State should not be changeable and always be connected if * NFSv4 is in use. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); /* wait until the file system is quiet */ mutex_enter(&fscp->fs_cdlock); if (fscp->fs_cdtransition == 1) { /* if someone is already changing the state */ mutex_exit(&fscp->fs_cdlock); return (0); } fscp->fs_cdtransition = 1; while (nosig && (fscp->fs_cdrefcnt != 0)) { nosig = cv_wait_sig(&fscp->fs_cdwaitcv, &fscp->fs_cdlock); } if (!nosig) { fscp->fs_cdtransition = 0; cv_broadcast(&fscp->fs_cdwaitcv); mutex_exit(&fscp->fs_cdlock); return (EINTR); } mutex_exit(&fscp->fs_cdlock); switch (state) { case CFS_FS_CONNECTED: /* done if already in this state */ if (fscp->fs_cdconnected == CFS_CD_CONNECTED) break; mutex_enter(&fscp->fs_cdlock); fscp->fs_cdconnected = CFS_CD_CONNECTED; mutex_exit(&fscp->fs_cdlock); /* fix up modified files */ cachefs_modified_fix(fscp); #if 0 if (fscp->fs_hostname != NULL) printf("\ncachefs:server - %s", fscp->fs_hostname); if (fscp->fs_mntpt != NULL) printf("\ncachefs:mount point - %s", fscp->fs_mntpt); if (fscp->fs_backfsname != NULL) printf("\ncachefs:back filesystem - %s", fscp->fs_backfsname); printf("\nok\n"); #else if (fscp->fs_hostname && fscp->fs_backfsname) printf("cachefs: %s:%s ok\n", fscp->fs_hostname, fscp->fs_backfsname); else printf("cachefs: server ok\n"); #endif /* allow deletion of renamed open files to proceed */ cachefs_cnode_traverse(fscp, allow_pendrm); break; case CFS_FS_DISCONNECTED: /* done if already in this state */ if (fscp->fs_cdconnected == CFS_CD_DISCONNECTED) break; /* drop all back vps */ cachefs_cnode_traverse(fscp, drop_backvp); mutex_enter(&fscp->fs_cdlock); fscp->fs_cdconnected = CFS_CD_DISCONNECTED; mutex_exit(&fscp->fs_cdlock); #if 0 if (fscp->fs_hostname != NULL) printf("\ncachefs:server - %s", fscp->fs_hostname); if (fscp->fs_mntpt != NULL) printf("\ncachefs:mount point - %s", fscp->fs_mntpt); if (fscp->fs_backfsname != NULL) printf("\ncachefs:back filesystem - %s", fscp->fs_backfsname); printf("\nnot responding still trying\n"); #else if (fscp->fs_hostname && fscp->fs_backfsname) printf("cachefs: %s:%s not responding still trying\n", fscp->fs_hostname, fscp->fs_backfsname); else printf("cachefs: server not responding still trying\n"); #endif break; case CFS_FS_RECONNECTING: /* done if already in this state */ if (fscp->fs_cdconnected == CFS_CD_RECONNECTING) break; /* * Before we enter disconnected state we sync all metadata, * this allows us to read metadata directly in subsequent * calls so we don't need to allocate cnodes when * we just need metadata information. */ /* XXX bob: need to eliminate this */ cachefs_cnode_traverse(fscp, sync_metadata); mutex_enter(&fscp->fs_cdlock); fscp->fs_cdconnected = CFS_CD_RECONNECTING; mutex_exit(&fscp->fs_cdlock); /* no longer need dlog active */ cachefs_dlog_teardown(fscp); break; default: error = ENOTTY; break; } mutex_enter(&fscp->fs_cdlock); fscp->fs_cdtransition = 0; cv_broadcast(&fscp->fs_cdwaitcv); mutex_exit(&fscp->fs_cdlock); return (error); } /* * Blocks until the file system switches * out of the connected state. */ int /*ARGSUSED*/ cachefs_io_xwait(vnode_t *vp, void *dinp, void *doutp) { fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); int nosig = 1; /* * Only called in support of disconnectable operation, so assert * that this is not used when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); mutex_enter(&fscp->fs_cdlock); while (nosig && (fscp->fs_cdconnected == CFS_CD_CONNECTED)) { nosig = cv_wait_sig(&fscp->fs_cdwaitcv, &fscp->fs_cdlock); } mutex_exit(&fscp->fs_cdlock); if (!nosig) return (EINTR); return (0); } #define RL_HEAD(cachep, type) \ (&(cachep->c_rlinfo.rl_items[CACHEFS_RL_INDEX(type)])) /* * Returns some statistics about the cache. */ #define CFS_STAT_FACTOR (MAXBSIZE / 1024) int /*ARGSUSED*/ cachefs_io_getstats(vnode_t *vp, void *dinp, void *doutp) { fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); cachefscache_t *cachep = fscp->fs_cache; struct statvfs64 sb; fsblkcnt64_t avail = 0; fsblkcnt64_t blocks; int error; cachefsio_getstats_t *gsp = (cachefsio_getstats_t *)doutp; /* determine number of blocks available to the cache */ error = VFS_STATVFS(cachep->c_dirvp->v_vfsp, &sb); if (error == 0) { blocks = (fsblkcnt64_t)(cachep->c_label.cl_maxblks - cachep->c_usage.cu_blksused); if ((longlong_t)blocks < (longlong_t)0) blocks = (fsblkcnt64_t)0; avail = (sb.f_bfree * sb.f_frsize) / MAXBSIZE; if (blocks < avail) avail = blocks; } gsp->gs_total = cachep->c_usage.cu_blksused * CFS_STAT_FACTOR; gsp->gs_gc = RL_HEAD(cachep, CACHEFS_RL_GC)->rli_blkcnt * CFS_STAT_FACTOR; gsp->gs_active = RL_HEAD(cachep, CACHEFS_RL_ACTIVE)->rli_blkcnt * CFS_STAT_FACTOR; gsp->gs_packed = RL_HEAD(cachep, CACHEFS_RL_PACKED)->rli_blkcnt * CFS_STAT_FACTOR; gsp->gs_free = (long)(avail * CFS_STAT_FACTOR); gsp->gs_gctime = cachep->c_rlinfo.rl_gctime; return (0); } /* * This looks to see if the specified file exists in the cache. * 0 is returned if it exists * ENOENT is returned if it doesn't exist. */ int /*ARGSUSED*/ cachefs_io_exists(vnode_t *vp, void *dinp, void *doutp) { fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); cnode_t *cp = NULL; int error; cfs_cid_t *cidp = (cfs_cid_t *)dinp; /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); /* find the cnode of the file */ error = cachefs_cnode_make(cidp, fscp, NULL, NULL, NULL, kcred, 0, &cp); if (error) return (ENOENT); if ((cp->c_flags & (CN_DESTROY | CN_NOCACHE)) || !(cp->c_metadata.md_flags & (MD_POPULATED | MD_FASTSYMLNK))) error = ENOENT; VN_RELE(CTOV(cp)); return (error); } /* * Moves the specified file to the lost+found directory for the * cached file system. * Invalidates cached data and attributes. * Returns 0 or an error if could not perform operation. */ int cachefs_io_lostfound(vnode_t *vp, void *dinp, void *doutp) { int error; cnode_t *cp = NULL; fscache_t *fscp; cachefscache_t *cachep; cachefsio_lostfound_arg_t *lfp; cachefsio_lostfound_return_t *rp; lfp = (cachefsio_lostfound_arg_t *)dinp; rp = (cachefsio_lostfound_return_t *)doutp; fscp = C_TO_FSCACHE(VTOC(vp)); cachep = fscp->fs_cache; ASSERT((cachep->c_flags & (CACHE_NOCACHE|CACHE_NOFILL)) == 0); /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); /* find the cnode of the file */ error = cachefs_cnode_make(&lfp->lf_cid, fscp, NULL, NULL, NULL, kcred, 0, &cp); if (error) { error = ENOENT; goto out; } mutex_enter(&cp->c_statelock); /* must be regular file and modified */ if ((cp->c_attr.va_type != VREG) || (cp->c_metadata.md_rltype != CACHEFS_RL_MODIFIED)) { mutex_exit(&cp->c_statelock); error = EINVAL; goto out; } /* move to lost+found */ error = cachefs_cnode_lostfound(cp, lfp->lf_name); mutex_exit(&cp->c_statelock); if (error == 0) (void) strcpy(rp->lf_name, lfp->lf_name); out: if (cp) VN_RELE(CTOV(cp)); return (error); } /* * Given a cid, returns info about the file in the cache. */ int cachefs_io_getinfo(vnode_t *vp, void *dinp, void *doutp) { fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); struct cnode *dcp = NULL; struct cnode *cp = NULL; struct vattr va; u_offset_t blockoff = 0; struct fbuf *fbp; int offset = 0; int error = 0; cfs_cid_t *fcidp; cachefsio_getinfo_t *infop; /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); fcidp = (cfs_cid_t *)dinp; infop = (cachefsio_getinfo_t *)doutp; /* find the cnode of the file */ error = cachefs_cnode_make(fcidp, fscp, NULL, NULL, NULL, kcred, 0, &cp); if (error) { error = ENOENT; goto out; } infop->gi_cid = *fcidp; infop->gi_modified = (cp->c_metadata.md_rltype == CACHEFS_RL_MODIFIED); CACHEFS_VATTR_TO_CFS_VATTR_COPY(&cp->c_attr, &infop->gi_attr, error); infop->gi_pcid = cp->c_metadata.md_parent; infop->gi_name[0] = '\0'; infop->gi_seq = cp->c_metadata.md_seq; if (error || (cp->c_metadata.md_parent.cid_fileno == 0)) goto out; /* try to get the cnode of the parent dir */ error = cachefs_cnode_make(&cp->c_metadata.md_parent, fscp, NULL, NULL, NULL, kcred, 0, &dcp); if (error) { error = 0; goto out; } /* make sure a directory and populated */ if ((((dcp->c_flags & CN_ASYNC_POPULATE) == 0) || ((dcp->c_metadata.md_flags & MD_POPULATED) == 0)) && (CTOV(dcp)->v_type == VDIR)) { error = 0; goto out; } /* get the front file */ if (dcp->c_frontvp == NULL) { mutex_enter(&dcp->c_statelock); error = cachefs_getfrontfile(dcp); mutex_exit(&dcp->c_statelock); if (error) { error = 0; goto out; } /* make sure frontvp is still populated */ if ((dcp->c_metadata.md_flags & MD_POPULATED) == 0) { error = 0; goto out; } } /* Get the length of the directory */ va.va_mask = AT_SIZE; error = VOP_GETATTR(dcp->c_frontvp, &va, 0, kcred, NULL); if (error) { error = 0; goto out; } /* XXX bob: change this to use cachfs_dir_read */ /* We have found the parent, now we open the dir and look for file */ while (blockoff < va.va_size) { offset = 0; error = fbread(dcp->c_frontvp, (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) && (bcmp(&dep->d_id, &infop->gi_cid, sizeof (cfs_cid_t)) == 0)) { /* found the name */ (void) strcpy(infop->gi_name, dep->d_name); fbrelse(fbp, S_OTHER); goto out; } offset += dep->d_length; } fbrelse(fbp, S_OTHER); fbp = NULL; blockoff += MAXBSIZE; } out: if (cp) VN_RELE(CTOV(cp)); if (dcp) VN_RELE(CTOV(dcp)); return (error); } /* * Given a file number, this functions returns the fid * for the back file system. * Returns ENOENT if file does not exist. * Returns ENOMSG if fid is not valid, ie: local file. */ int cachefs_io_cidtofid(vnode_t *vp, void *dinp, void *doutp) { fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); cnode_t *cp = NULL; int error; cfs_cid_t *cidp = (cfs_cid_t *)dinp; cfs_fid_t *fidp = (cfs_fid_t *)doutp; /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); /* get the cnode for the file */ error = cachefs_cnode_make(cidp, fscp, NULL, NULL, NULL, kcred, 0, &cp); if (error) goto out; /* if local file, fid is a local fid and is not valid */ if (cp->c_id.cid_flags & CFS_CID_LOCAL) { error = ENOMSG; goto out; } /* copy out the fid */ CACHEFS_FID_COPY(&cp->c_cookie, fidp); out: if (cp) VN_RELE(CTOV(cp)); return (error); } /* * This performs a getattr on the back file system given * a fid that is passed in. * * The backfid is in gafid->cg_backfid, the creds to use for * this operation are in gafid->cg_cred. The attributes are * returned in gafid->cg_attr * * the error returned is 0 if successful, nozero if not */ int cachefs_io_getattrfid(vnode_t *vp, void *dinp, void *doutp) { vnode_t *backvp = NULL; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); int error = 0; cred_t *cr; cachefsio_getattrfid_t *gafid; fid_t *tmpfidp; vattr_t *tmpvap; cfs_vattr_t *attrp; CACHEFS_DECL(fid_t, tmpfid); CACHEFS_DECL(vattr_t, va); /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); gafid = (cachefsio_getattrfid_t *)dinp; attrp = (cfs_vattr_t *)doutp; /* Get a vnode for the back file */ CACHEFS_TMPPTR_SET(&gafid->cg_backfid, &tmpfid, tmpfidp, fid_t); CACHEFS_FID_COPYIN(&gafid->cg_backfid, tmpfidp); error = VFS_VGET(fscp->fs_backvfsp, &backvp, tmpfidp); if (error) return (error); cr = conj_cred(&gafid->cg_cred); CACHEFS_TMPPTR_SET(attrp, &va, tmpvap, vattr_t); tmpvap->va_mask = AT_ALL; error = VOP_GETATTR(backvp, tmpvap, 0, cr, NULL); CACHEFS_VATTR_COPYOUT(tmpvap, attrp, error); crfree(cr); /* VFS_VGET performs a VN_HOLD on the vp */ VN_RELE(backvp); return (error); } /* * This performs a getattr on the back file system. Instead of * passing the fid to perform the gettr on we are given the * parent directory fid and a name. */ int cachefs_io_getattrname(vnode_t *vp, void *dinp, void *doutp) { vnode_t *pbackvp = NULL; vnode_t *cbackvp = NULL; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); int error = 0; cred_t *cr; fid_t *tmpfidp; vattr_t *tmpvap; cachefsio_getattrname_arg_t *gap; cachefsio_getattrname_return_t *retp; CACHEFS_DECL(fid_t, tmpfid); CACHEFS_DECL(vattr_t, va); /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); gap = (cachefsio_getattrname_arg_t *)dinp; retp = (cachefsio_getattrname_return_t *)doutp; /* Get a vnode for the parent directory */ CACHEFS_TMPPTR_SET(&gap->cg_dir, &tmpfid, tmpfidp, fid_t); CACHEFS_FID_COPYIN(&gap->cg_dir, tmpfidp); error = VFS_VGET(fscp->fs_backvfsp, &pbackvp, tmpfidp); if (error) return (error); /* lookup the file name */ cr = conj_cred(&gap->cg_cred); error = VOP_LOOKUP(pbackvp, gap->cg_name, &cbackvp, (struct pathname *)NULL, 0, (vnode_t *)NULL, cr, NULL, NULL, NULL); if (error) { crfree(cr); VN_RELE(pbackvp); return (error); } CACHEFS_TMPPTR_SET(&retp->cg_attr, &va, tmpvap, vattr_t); tmpvap->va_mask = AT_ALL; error = VOP_GETATTR(cbackvp, tmpvap, 0, cr, NULL); CACHEFS_VATTR_COPYOUT(tmpvap, &retp->cg_attr, error); if (!error) { CACHEFS_TMPPTR_SET(&retp->cg_fid, &tmpfid, tmpfidp, fid_t); tmpfidp->fid_len = MAXFIDSZ; error = VOP_FID(cbackvp, tmpfidp, NULL); CACHEFS_FID_COPYOUT(tmpfidp, &retp->cg_fid); } crfree(cr); VN_RELE(cbackvp); VN_RELE(pbackvp); return (error); } /* * This will return the fid of the root of this mount point. */ int /*ARGSUSED*/ cachefs_io_rootfid(vnode_t *vp, void *dinp, void *doutp) { fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); cfs_fid_t *rootfid = (cfs_fid_t *)doutp; /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); CACHEFS_FID_COPY(&VTOC(fscp->fs_rootvp)->c_metadata.md_cookie, rootfid); return (0); } /* * Pushes the data associated with a file back to the file server. */ int cachefs_io_pushback(vnode_t *vp, void *dinp, void *doutp) { vnode_t *backvp = NULL; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); caddr_t buffer = NULL; int error = 0; cnode_t *cp; size_t amt; u_offset_t size; vattr_t va; offset_t off; cred_t *cr = NULL; fid_t *tmpfidp; cachefsio_pushback_arg_t *pbp; cachefsio_pushback_return_t *retp; CACHEFS_DECL(fid_t, tmpfid); /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); pbp = (cachefsio_pushback_arg_t *)dinp; retp = (cachefsio_pushback_return_t *)doutp; cr = conj_cred(&pbp->pb_cred); /* get the backvp to push to */ CACHEFS_TMPPTR_SET(&pbp->pb_fid, &tmpfid, tmpfidp, fid_t); CACHEFS_FID_COPYIN(&pbp->pb_fid, tmpfidp); error = VFS_VGET(fscp->fs_backvfsp, &backvp, tmpfidp); if (error) { backvp = NULL; goto out; } /* Get the cnode for the file we are to push back */ error = cachefs_cnode_make(&pbp->pb_cid, fscp, NULL, NULL, NULL, cr, 0, &cp); if (error) { goto out; } /* must be a regular file */ if (cp->c_attr.va_type != VREG) { error = EINVAL; goto out; } mutex_enter(&cp->c_statelock); /* get the front file */ if (cp->c_frontvp == NULL) { error = cachefs_getfrontfile(cp); if (error) { mutex_exit(&cp->c_statelock); goto out; } } /* better be populated */ if ((cp->c_metadata.md_flags & MD_POPULATED) == 0) { mutex_exit(&cp->c_statelock); error = EINVAL; goto out; } /* do open so NFS gets correct creds on writes */ error = VOP_OPEN(&backvp, FWRITE, cr, NULL); if (error) { mutex_exit(&cp->c_statelock); goto out; } buffer = cachefs_kmem_alloc(MAXBSIZE, KM_SLEEP); /* Read the data from the cache and write it to the server */ /* XXX why not use segmapio? */ off = 0; for (size = cp->c_size; size != 0; size -= amt) { if (size > MAXBSIZE) amt = MAXBSIZE; else amt = size; /* read a block of data from the front file */ error = vn_rdwr(UIO_READ, cp->c_frontvp, buffer, amt, off, UIO_SYSSPACE, 0, RLIM_INFINITY, cr, 0); if (error) { mutex_exit(&cp->c_statelock); goto out; } /* write the block of data to the back file */ error = vn_rdwr(UIO_WRITE, backvp, buffer, amt, off, UIO_SYSSPACE, 0, RLIM_INFINITY, cr, 0); if (error) { mutex_exit(&cp->c_statelock); goto out; } off += amt; } error = VOP_FSYNC(backvp, FSYNC, cr, NULL); if (error == 0) error = VOP_CLOSE(backvp, FWRITE, 1, (offset_t)0, cr, NULL); if (error) { mutex_exit(&cp->c_statelock); goto out; } cp->c_metadata.md_flags |= MD_PUSHDONE; cp->c_metadata.md_flags &= ~MD_PUTPAGE; cp->c_metadata.md_flags |= MD_NEEDATTRS; cp->c_flags |= CN_UPDATED; mutex_exit(&cp->c_statelock); /* * if we have successfully stored the data, we need the * new ctime and mtimes. */ va.va_mask = AT_ALL; error = VOP_GETATTR(backvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->pb_ctime, error); CACHEFS_TS_TO_CFS_TS_COPY(&va.va_mtime, &retp->pb_mtime, error); out: if (buffer) cachefs_kmem_free(buffer, MAXBSIZE); if (cp) VN_RELE(CTOV(cp)); if (backvp) VN_RELE(backvp); if (cr) crfree(cr); return (error); } /* * Create a file on the back file system. */ int cachefs_io_create(vnode_t *vp, void *dinp, void *doutp) { vnode_t *dvp = NULL; vnode_t *cvp = NULL; cnode_t *cp = NULL; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); vattr_t va, *tmpvap; int error = 0; cred_t *cr = NULL; fid_t *tmpfidp; cachefsio_create_arg_t *crp; cachefsio_create_return_t *retp; CACHEFS_DECL(fid_t, tmpfid); /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); crp = (cachefsio_create_arg_t *)dinp; retp = (cachefsio_create_return_t *)doutp; /* get a vnode for the parent directory */ CACHEFS_TMPPTR_SET(&crp->cr_backfid, &tmpfid, tmpfidp, fid_t); CACHEFS_FID_COPYIN(&crp->cr_backfid, tmpfidp); error = VFS_VGET(fscp->fs_backvfsp, &dvp, tmpfidp); if (error) goto out; cr = conj_cred(&crp->cr_cred); /* do the create */ CACHEFS_TMPPTR_SET(&crp->cr_va, &va, tmpvap, vattr_t); CACHEFS_VATTR_COPYIN(&crp->cr_va, tmpvap); error = VOP_CREATE(dvp, crp->cr_name, tmpvap, crp->cr_exclusive, crp->cr_mode, &cvp, cr, 0, NULL, NULL); if (error) goto out; /* get the fid of the file */ CACHEFS_TMPPTR_SET(&retp->cr_newfid, &tmpfid, tmpfidp, fid_t); tmpfidp->fid_len = MAXFIDSZ; error = VOP_FID(cvp, tmpfidp, NULL); if (error) goto out; CACHEFS_FID_COPYOUT(tmpfidp, &retp->cr_newfid); /* get attributes for the file */ va.va_mask = AT_ALL; error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->cr_ctime, error); CACHEFS_TS_TO_CFS_TS_COPY(&va.va_mtime, &retp->cr_mtime, error); if (error) goto out; /* update the cnode for this file with the new info */ error = cachefs_cnode_make(&crp->cr_cid, fscp, NULL, NULL, NULL, cr, 0, &cp); if (error) { error = 0; goto out; } mutex_enter(&cp->c_statelock); ASSERT(cp->c_id.cid_flags & CFS_CID_LOCAL); cp->c_attr.va_nodeid = va.va_nodeid; cp->c_metadata.md_flags |= MD_CREATEDONE; cp->c_metadata.md_flags |= MD_NEEDATTRS; cp->c_metadata.md_cookie = *tmpfidp; cp->c_flags |= CN_UPDATED; mutex_exit(&cp->c_statelock); out: if (cr) crfree(cr); if (dvp) VN_RELE(dvp); if (cvp) VN_RELE(cvp); if (cp) VN_RELE(CTOV(cp)); return (error); } /* * Remove a file on the back file system. * Returns 0 or an error if could not perform operation. */ int cachefs_io_remove(vnode_t *vp, void *dinp, void *doutp) { vnode_t *dvp = NULL; vnode_t *cvp; cred_t *cr = NULL; vattr_t va; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); int error; fid_t child_fid, *child_fidp; cachefsio_remove_t *rmp = (cachefsio_remove_t *)dinp; cfs_timestruc_t *ctimep = (cfs_timestruc_t *)doutp; /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); /* Get a vnode for the directory */ CACHEFS_TMPPTR_SET(&rmp->rm_fid, &child_fid, child_fidp, fid_t); CACHEFS_FID_COPYIN(&rmp->rm_fid, child_fidp); error = VFS_VGET(fscp->fs_backvfsp, &dvp, child_fidp); if (error) { dvp = NULL; goto out; } cr = conj_cred(&rmp->rm_cred); /* if the caller wants the ctime after the remove */ if (ctimep) { error = VOP_LOOKUP(dvp, rmp->rm_name, &cvp, NULL, 0, NULL, cr, NULL, NULL, NULL); if (error == 0) { child_fid.fid_len = MAXFIDSZ; error = VOP_FID(cvp, &child_fid, NULL); VN_RELE(cvp); } if (error) goto out; } /* do the remove */ error = VOP_REMOVE(dvp, rmp->rm_name, cr, NULL, 0); if (error) goto out; /* get the new ctime if requested */ if (ctimep) { error = VFS_VGET(fscp->fs_backvfsp, &cvp, &child_fid); if (error == 0) { va.va_mask = AT_ALL; error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error == 0) { CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, ctimep, error); } VN_RELE(cvp); } cachefs_iosetneedattrs(fscp, &rmp->rm_cid); } out: if (cr) crfree(cr); if (dvp) VN_RELE(dvp); return (error); } /* * Perform a link on the back file system. * Returns 0 or an error if could not perform operation. */ int cachefs_io_link(vnode_t *vp, void *dinp, void *doutp) { vnode_t *dvp = NULL; vnode_t *lvp = NULL; vattr_t va; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); int error = 0; cred_t *cr = NULL; fid_t *tmpfidp; cachefsio_link_t *linkp = (cachefsio_link_t *)dinp; cfs_timestruc_t *ctimep = (cfs_timestruc_t *)doutp; CACHEFS_DECL(fid_t, tmpfid); /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); /* Get a vnode parent directory */ CACHEFS_TMPPTR_SET(&linkp->ln_dirfid, &tmpfid, tmpfidp, fid_t); CACHEFS_FID_COPYIN(&linkp->ln_dirfid, tmpfidp); error = VFS_VGET(fscp->fs_backvfsp, &dvp, tmpfidp); if (error) { dvp = NULL; goto out; } /* Get a vnode file to link to */ CACHEFS_TMPPTR_SET(&linkp->ln_filefid, &tmpfid, tmpfidp, fid_t); CACHEFS_FID_COPYIN(&linkp->ln_filefid, tmpfidp); error = VFS_VGET(fscp->fs_backvfsp, &lvp, tmpfidp); if (error) { lvp = NULL; goto out; } cr = conj_cred(&linkp->ln_cred); /* do the link */ error = VOP_LINK(dvp, lvp, linkp->ln_name, cr, NULL, 0); if (error) goto out; /* get the ctime */ va.va_mask = AT_ALL; error = VOP_GETATTR(lvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, ctimep, error); if (error) goto out; cachefs_iosetneedattrs(fscp, &linkp->ln_cid); out: if (cr) crfree(cr); if (dvp) VN_RELE(dvp); if (lvp) VN_RELE(lvp); return (error); } /* * Rename the file on the back file system. * Returns 0 or an error if could not perform operation. */ int cachefs_io_rename(vnode_t *vp, void *dinp, void *doutp) { vnode_t *odvp = NULL; vnode_t *ndvp = NULL; cred_t *cr = NULL; vnode_t *cvp = NULL; vattr_t va; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); int error = 0; fid_t child_fid, *child_fidp; cachefsio_rename_arg_t *rnp; cachefsio_rename_return_t *retp; /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); rnp = (cachefsio_rename_arg_t *)dinp; retp = (cachefsio_rename_return_t *)doutp; /* Get vnode of old parent directory */ CACHEFS_TMPPTR_SET(&rnp->rn_olddir, &child_fid, child_fidp, fid_t); CACHEFS_FID_COPYIN(&rnp->rn_olddir, child_fidp); error = VFS_VGET(fscp->fs_backvfsp, &odvp, child_fidp); if (error) { odvp = NULL; goto out; } /* Get vnode of new parent directory */ CACHEFS_TMPPTR_SET(&rnp->rn_newdir, &child_fid, child_fidp, fid_t); CACHEFS_FID_COPYIN(&rnp->rn_newdir, child_fidp); error = VFS_VGET(fscp->fs_backvfsp, &ndvp, child_fidp); if (error) { ndvp = NULL; goto out; } cr = conj_cred(&rnp->rn_cred); /* if the caller wants the ctime of the target after deletion */ if (rnp->rn_del_getctime) { error = VOP_LOOKUP(ndvp, rnp->rn_newname, &cvp, NULL, 0, NULL, cr, NULL, NULL, NULL); if (error) { cvp = NULL; /* paranoia */ goto out; } child_fid.fid_len = MAXFIDSZ; error = VOP_FID(cvp, &child_fid, NULL); if (error) goto out; VN_RELE(cvp); cvp = NULL; } /* do the rename */ error = VOP_RENAME(odvp, rnp->rn_oldname, ndvp, rnp->rn_newname, cr, NULL, 0); if (error) goto out; /* get the new ctime on the renamed file */ error = VOP_LOOKUP(ndvp, rnp->rn_newname, &cvp, NULL, 0, NULL, cr, NULL, NULL, NULL); if (error) goto out; va.va_mask = AT_ALL; error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->rn_ctime, error); VN_RELE(cvp); cvp = NULL; if (error) goto out; cachefs_iosetneedattrs(fscp, &rnp->rn_cid); /* get the new ctime if requested of the deleted target */ if (rnp->rn_del_getctime) { error = VFS_VGET(fscp->fs_backvfsp, &cvp, &child_fid); if (error) { cvp = NULL; goto out; } va.va_mask = AT_ALL; error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->rn_del_ctime, error); VN_RELE(cvp); cvp = NULL; if (error) goto out; cachefs_iosetneedattrs(fscp, &rnp->rn_del_cid); } out: if (cr) crfree(cr); if (cvp) VN_RELE(cvp); if (odvp) VN_RELE(odvp); if (ndvp) VN_RELE(ndvp); return (error); } /* * Make a directory on the backfs. * Returns 0 or an error if could not perform operation. */ int cachefs_io_mkdir(vnode_t *vp, void *dinp, void *doutp) { vnode_t *dvp = NULL; vnode_t *cvp = NULL; cnode_t *cp = NULL; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); int error = 0; cred_t *cr = NULL; fid_t *tmpfidp; vattr_t va, *tmpvap; cachefsio_mkdir_t *mdirp = (cachefsio_mkdir_t *)dinp; cfs_fid_t *fidp = (cfs_fid_t *)doutp; CACHEFS_DECL(fid_t, tmpfid); /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); /* Get vnode of parent directory */ CACHEFS_TMPPTR_SET(&mdirp->md_dirfid, &tmpfid, tmpfidp, fid_t); CACHEFS_FID_COPYIN(&mdirp->md_dirfid, tmpfidp); error = VFS_VGET(fscp->fs_backvfsp, &dvp, tmpfidp); if (error) { dvp = NULL; goto out; } cr = conj_cred(&mdirp->md_cred); /* make the directory */ CACHEFS_TMPPTR_SET(&mdirp->md_vattr, &va, tmpvap, vattr_t); CACHEFS_VATTR_COPYIN(&mdirp->md_vattr, tmpvap); error = VOP_MKDIR(dvp, mdirp->md_name, tmpvap, &cvp, cr, NULL, 0, NULL); if (error) { if (error != EEXIST) goto out; /* if the directory already exists, then use it */ error = VOP_LOOKUP(dvp, mdirp->md_name, &cvp, NULL, 0, NULL, cr, NULL, NULL, NULL); if (error) { cvp = NULL; goto out; } if (cvp->v_type != VDIR) { error = EINVAL; goto out; } } /* get the fid of the directory */ CACHEFS_TMPPTR_SET(fidp, &tmpfid, tmpfidp, fid_t); tmpfidp->fid_len = MAXFIDSZ; error = VOP_FID(cvp, tmpfidp, NULL); if (error) goto out; CACHEFS_FID_COPYOUT(tmpfidp, fidp); /* get attributes of the directory */ va.va_mask = AT_ALL; error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error) goto out; /* update the cnode for this dir with the new fid */ error = cachefs_cnode_make(&mdirp->md_cid, fscp, NULL, NULL, NULL, cr, 0, &cp); if (error) { error = 0; goto out; } mutex_enter(&cp->c_statelock); ASSERT(cp->c_id.cid_flags & CFS_CID_LOCAL); cp->c_metadata.md_cookie = *tmpfidp; cp->c_metadata.md_flags |= MD_CREATEDONE; cp->c_metadata.md_flags |= MD_NEEDATTRS; cp->c_attr.va_nodeid = va.va_nodeid; cp->c_flags |= CN_UPDATED; mutex_exit(&cp->c_statelock); out: if (cr) crfree(cr); if (dvp) VN_RELE(dvp); if (cvp) VN_RELE(cvp); if (cp) VN_RELE(CTOV(cp)); return (error); } /* * Perform a rmdir on the back file system. * Returns 0 or an error if could not perform operation. */ int /*ARGSUSED*/ cachefs_io_rmdir(vnode_t *vp, void *dinp, void *doutp) { vnode_t *dvp = NULL; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); int error; cred_t *cr; fid_t *tmpfidp; cachefsio_rmdir_t *rdp = (cachefsio_rmdir_t *)dinp; CACHEFS_DECL(fid_t, tmpfid); /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); /* Get a vnode for the back file */ CACHEFS_TMPPTR_SET(&rdp->rd_dirfid, &tmpfid, tmpfidp, fid_t); CACHEFS_FID_COPYIN(&rdp->rd_dirfid, tmpfidp); error = VFS_VGET(fscp->fs_backvfsp, &dvp, tmpfidp); if (error) { dvp = NULL; return (error); } cr = conj_cred(&rdp->rd_cred); error = VOP_RMDIR(dvp, rdp->rd_name, dvp, cr, NULL, 0); crfree(cr); VN_RELE(dvp); return (error); } /* * create a symlink on the back file system * Returns 0 or an error if could not perform operation. */ int cachefs_io_symlink(vnode_t *vp, void *dinp, void *doutp) { vnode_t *dvp = NULL; vnode_t *svp = NULL; cnode_t *cp = NULL; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); fid_t *tmpfidp; vattr_t va, *tmpvap; int error = 0; cred_t *cr = NULL; cachefsio_symlink_arg_t *symp; cachefsio_symlink_return_t *retp; CACHEFS_DECL(fid_t, tmpfid); /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); symp = (cachefsio_symlink_arg_t *)dinp; retp = (cachefsio_symlink_return_t *)doutp; /* get a vnode for the back directory */ CACHEFS_TMPPTR_SET(&symp->sy_dirfid, &tmpfid, tmpfidp, fid_t); CACHEFS_FID_COPYIN(&symp->sy_dirfid, tmpfidp); error = VFS_VGET(fscp->fs_backvfsp, &dvp, tmpfidp); if (error) { dvp = NULL; goto out; } cr = conj_cred(&symp->sy_cred); /* create the symlink */ CACHEFS_TMPPTR_SET(&symp->sy_vattr, &va, tmpvap, vattr_t); CACHEFS_VATTR_COPYIN(&symp->sy_vattr, tmpvap); error = VOP_SYMLINK(dvp, symp->sy_name, tmpvap, symp->sy_link, cr, NULL, 0); if (error) goto out; /* get the vnode for the symlink */ error = VOP_LOOKUP(dvp, symp->sy_name, &svp, NULL, 0, NULL, cr, NULL, NULL, NULL); if (error) goto out; /* get the attributes of the symlink */ va.va_mask = AT_ALL; error = VOP_GETATTR(svp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->sy_ctime, error); CACHEFS_TS_TO_CFS_TS_COPY(&va.va_mtime, &retp->sy_mtime, error); if (error) goto out; /* get the fid */ CACHEFS_TMPPTR_SET(&retp->sy_newfid, &tmpfid, tmpfidp, fid_t); tmpfidp->fid_len = MAXFIDSZ; error = VOP_FID(svp, tmpfidp, NULL); if (error) goto out; CACHEFS_FID_COPYOUT(tmpfidp, &retp->sy_newfid); /* update the cnode for this file with the new info */ error = cachefs_cnode_make(&symp->sy_cid, fscp, NULL, NULL, NULL, cr, 0, &cp); if (error) { error = 0; goto out; } mutex_enter(&cp->c_statelock); ASSERT(cp->c_id.cid_flags & CFS_CID_LOCAL); cp->c_metadata.md_cookie = *tmpfidp; cp->c_metadata.md_flags |= MD_CREATEDONE; cp->c_metadata.md_flags |= MD_NEEDATTRS; cp->c_attr.va_nodeid = va.va_nodeid; cp->c_flags |= CN_UPDATED; mutex_exit(&cp->c_statelock); out: if (cr) crfree(cr); if (dvp) VN_RELE(dvp); if (svp) VN_RELE(svp); if (cp) VN_RELE(CTOV(cp)); return (error); } /* * Perform setattr on the back file system. * Returns 0 or an error if could not perform operation. */ int cachefs_io_setattr(vnode_t *vp, void *dinp, void *doutp) { vnode_t *cvp = NULL; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); fid_t *tmpfidp; vattr_t va, *tmpvap; int error = 0; cred_t *cr = NULL; cachefsio_setattr_arg_t *sap; cachefsio_setattr_return_t *retp; CACHEFS_DECL(fid_t, tmpfid); /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); sap = (cachefsio_setattr_arg_t *)dinp; retp = (cachefsio_setattr_return_t *)doutp; /* get a vnode for the back directory */ CACHEFS_TMPPTR_SET(&sap->sa_backfid, &tmpfid, tmpfidp, fid_t); CACHEFS_FID_COPYIN(&sap->sa_backfid, tmpfidp); error = VFS_VGET(fscp->fs_backvfsp, &cvp, tmpfidp); if (error) { cvp = NULL; goto out; } cr = conj_cred(&sap->sa_cred); /* perform the setattr */ CACHEFS_TMPPTR_SET(&sap->sa_vattr, &va, tmpvap, vattr_t); CACHEFS_VATTR_COPYIN(&sap->sa_vattr, tmpvap); error = VOP_SETATTR(cvp, tmpvap, 0, cr, NULL); if (error) goto out; /* get the new ctime and mtime */ va.va_mask = AT_ALL; error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->sa_ctime, error); CACHEFS_TS_TO_CFS_TS_COPY(&va.va_mtime, &retp->sa_mtime, error); if (error) goto out; cachefs_iosetneedattrs(fscp, &sap->sa_cid); out: if (cr) crfree(cr); if (cvp) VN_RELE(cvp); return (error); } int cachefs_io_setsecattr(vnode_t *vp, void *dinp, void *doutp) { int error = 0; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); vnode_t *tvp = NULL; vsecattr_t vsec; vattr_t va; cred_t *cr = NULL; fid_t *tmpfidp; cachefsio_setsecattr_arg_t *ssap; cachefsio_setsecattr_return_t *retp; CACHEFS_DECL(fid_t, tmpfid); /* * Only called in support of disconnectable operation, so assert * that this is not called when NFSv4 is the backfilesytem. */ ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); ssap = (cachefsio_setsecattr_arg_t *)dinp; retp = (cachefsio_setsecattr_return_t *)doutp; /* get vnode of back file to do VOP_SETSECATTR to */ CACHEFS_TMPPTR_SET(&ssap->sc_backfid, &tmpfid, tmpfidp, fid_t); CACHEFS_FID_COPYIN(&ssap->sc_backfid, tmpfidp); error = VFS_VGET(fscp->fs_backvfsp, &tvp, tmpfidp); if (error != 0) { tvp = NULL; goto out; } /* get the creds */ cr = conj_cred(&ssap->sc_cred); /* form the vsecattr_t */ vsec.vsa_mask = ssap->sc_mask; vsec.vsa_aclcnt = ssap->sc_aclcnt; vsec.vsa_dfaclcnt = ssap->sc_dfaclcnt; vsec.vsa_aclentp = ssap->sc_acl; vsec.vsa_dfaclentp = ssap->sc_acl + ssap->sc_aclcnt; /* set the ACL */ (void) VOP_RWLOCK(tvp, V_WRITELOCK_TRUE, NULL); error = VOP_SETSECATTR(tvp, &vsec, 0, cr, NULL); VOP_RWUNLOCK(tvp, V_WRITELOCK_TRUE, NULL); if (error != 0) goto out; /* get the new ctime and mtime */ va.va_mask = AT_ALL; error = VOP_GETATTR(tvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->sc_ctime, error); CACHEFS_TS_TO_CFS_TS_COPY(&va.va_mtime, &retp->sc_mtime, error); if (error) goto out; cachefs_iosetneedattrs(fscp, &ssap->sc_cid); out: if (cr != NULL) crfree(cr); if (tvp != NULL) VN_RELE(tvp); return (error); } static void sync_metadata(cnode_t *cp) { if (cp->c_flags & (CN_STALE | CN_DESTROY)) return; (void) cachefs_sync_metadata(cp); } static void drop_backvp(cnode_t *cp) { if (cp->c_backvp) { mutex_enter(&cp->c_statelock); if (cp->c_backvp) { /* dump any pages, may be a dirty one */ (void) VOP_PUTPAGE(cp->c_backvp, (offset_t)0, 0, B_INVAL | B_TRUNC, kcred, NULL); } mutex_exit(&cp->c_statelock); } } static void allow_pendrm(cnode_t *cp) { if (cp->c_flags & CN_PENDRM) { mutex_enter(&cp->c_statelock); if (cp->c_flags & CN_PENDRM) { cp->c_flags &= ~CN_PENDRM; VN_RELE(CTOV(cp)); } mutex_exit(&cp->c_statelock); } } static void cachefs_modified_fix(fscache_t *fscp) { cnode_t *cp; int error = 0; rl_entry_t rl_ent; cfs_cid_t cid; cachefscache_t *cachep = fscp->fs_cache; enum cachefs_rl_type type; cachefs_metadata_t *mdp; int timedout = 0; struct vattr va; /* XXX just return if fs is in error ro mode */ /* lock out other users of the MF list */ mutex_enter(&cachep->c_mflock); /* move the modified entries for this file system to the MF list */ cachefs_move_modified_to_mf(cachep, fscp); rl_ent.rl_current = CACHEFS_RL_MF; for (;;) { /* get the next entry on the MF list */ error = cachefs_rlent_data(cachep, &rl_ent, NULL); if (error) { error = 0; break; } ASSERT(fscp->fs_cfsid == rl_ent.rl_fsid); /* get the cnode for the file */ cid.cid_fileno = rl_ent.rl_fileno; cid.cid_flags = rl_ent.rl_local ? CFS_CID_LOCAL : 0; error = cachefs_cnode_make(&cid, fscp, NULL, NULL, NULL, kcred, 0, &cp); if (error) { #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_IOCTL) printf("cachefs: mf: could not find %llu\n", (u_longlong_t)cid.cid_fileno); delay(5*hz); #endif /* XXX this will loop forever, maybe put fs in */ /* ro mode */ continue; } mutex_enter(&cp->c_statelock); mdp = &cp->c_metadata; /* if a regular file that has not been pushed */ if ((cp->c_attr.va_type == VREG) && (((mdp->md_flags & (MD_PUSHDONE | MD_PUTPAGE)) == MD_PUTPAGE))) { /* move the file to lost+found */ error = cachefs_cnode_lostfound(cp, NULL); if (error) { /* XXX put fs in ro mode */ /* XXX need to drain MF list */ panic("lostfound failed %d", error); } mutex_exit(&cp->c_statelock); VN_RELE(CTOV(cp)); continue; } /* if a local file */ if (cp->c_id.cid_flags & CFS_CID_LOCAL) { /* if the file was not created */ if ((cp->c_metadata.md_flags & MD_CREATEDONE) == 0) { /* do not allow cnode to be used */ cachefs_cnode_stale(cp); mutex_exit(&cp->c_statelock); VN_RELE(CTOV(cp)); continue; } /* save the local fileno for later getattrs */ mdp->md_localfileno = cp->c_id.cid_fileno; mutex_exit(&cp->c_statelock); /* register the mapping from old to new fileno */ mutex_enter(&fscp->fs_fslock); cachefs_inum_register(fscp, cp->c_attr.va_nodeid, mdp->md_localfileno); cachefs_inum_register(fscp, mdp->md_localfileno, 0); mutex_exit(&fscp->fs_fslock); /* move to new location in the cache */ cachefs_cnode_move(cp); mutex_enter(&cp->c_statelock); } /* else if a modified file that needs to have its mode fixed */ else if ((cp->c_metadata.md_flags & MD_FILE) && (cp->c_attr.va_type == VREG)) { if (cp->c_frontvp == NULL) (void) cachefs_getfrontfile(cp); if (cp->c_frontvp) { /* mark file as no longer modified */ va.va_mode = 0666; va.va_mask = AT_MODE; error = VOP_SETATTR(cp->c_frontvp, &va, 0, kcred, NULL); if (error) { cmn_err(CE_WARN, "Cannot change ff mode.\n"); } } } /* if there is a rl entry, put it on the correct list */ if (mdp->md_rlno) { if (mdp->md_flags & MD_PACKED) { if ((mdp->md_flags & MD_POPULATED) || ((mdp->md_flags & MD_FILE) == 0)) type = CACHEFS_RL_PACKED; else type = CACHEFS_RL_PACKED_PENDING; cachefs_rlent_moveto(fscp->fs_cache, type, mdp->md_rlno, mdp->md_frontblks); mdp->md_rltype = type; } else if (mdp->md_flags & MD_FILE) { type = CACHEFS_RL_ACTIVE; cachefs_rlent_moveto(fscp->fs_cache, type, mdp->md_rlno, mdp->md_frontblks); mdp->md_rltype = type; } else { type = CACHEFS_RL_FREE; cachefs_rlent_moveto(fscp->fs_cache, type, mdp->md_rlno, 0); filegrp_ffrele(cp->c_filegrp); mdp->md_rlno = 0; mdp->md_rltype = CACHEFS_RL_NONE; } } mdp->md_flags &= ~(MD_CREATEDONE | MD_PUTPAGE | MD_PUSHDONE | MD_MAPPING); /* if a directory, populate it */ if (CTOV(cp)->v_type == VDIR) { /* XXX hack for now */ mdp->md_flags |= MD_INVALREADDIR; dnlc_purge_vp(CTOV(cp)); mdp->md_flags |= MD_NEEDATTRS; } if (!timedout) { error = CFSOP_CHECK_COBJECT(fscp, cp, 0, kcred); if (CFS_TIMEOUT(fscp, error)) timedout = 1; else if ((error == 0) && ((fscp->fs_info.fi_mntflags & CFS_NOACL) == 0)) { if (cachefs_vtype_aclok(CTOV(cp)) && ((cp->c_flags & CN_NOCACHE) == 0)) (void) cachefs_cacheacl(cp, NULL); } } cp->c_flags |= CN_UPDATED; mutex_exit(&cp->c_statelock); VN_RELE(CTOV(cp)); } mutex_exit(&cachep->c_mflock); } void cachefs_inum_register(fscache_t *fscp, ino64_t real, ino64_t fake) { cachefs_inum_trans_t *tbl; int toff, thop; int i; ASSERT(MUTEX_HELD(&fscp->fs_fslock)); /* * first, see if an empty slot exists. */ for (i = 0; i < fscp->fs_inum_size; i++) if (fscp->fs_inum_trans[i].cit_real == 0) break; /* * if there are no empty slots, try to grow the table. */ if (i >= fscp->fs_inum_size) { cachefs_inum_trans_t *oldtbl; int oldsize, newsize = 0; /* * try to fetch a new table size that's bigger than * our current size */ for (i = 0; cachefs_hash_sizes[i] != 0; i++) if (cachefs_hash_sizes[i] > fscp->fs_inum_size) { newsize = cachefs_hash_sizes[i]; break; } /* * if we're out of larger twin-primes, give up. thus, * the inode numbers in some directory entries might * change at reconnect, and disagree with what stat() * says. this isn't worth panicing over, but it does * merit a warning message. */ if (newsize == 0) { /* only print hash table warning once */ if ((fscp->fs_flags & CFS_FS_HASHPRINT) == 0) { cmn_err(CE_WARN, "cachefs: inode hash table full\n"); fscp->fs_flags |= CFS_FS_HASHPRINT; } return; } /* set up this fscp with a new hash table */ oldtbl = fscp->fs_inum_trans; oldsize = fscp->fs_inum_size; fscp->fs_inum_size = newsize; fscp->fs_inum_trans = (cachefs_inum_trans_t *) cachefs_kmem_zalloc(sizeof (cachefs_inum_trans_t) * newsize, KM_SLEEP); /* * re-insert all of the old values. this will never * go more than one level into recursion-land. */ for (i = 0; i < oldsize; i++) { tbl = oldtbl + i; if (tbl->cit_real != 0) { cachefs_inum_register(fscp, tbl->cit_real, tbl->cit_fake); } else { ASSERT(0); } } if (oldsize > 0) cachefs_kmem_free(oldtbl, oldsize * sizeof (cachefs_inum_trans_t)); } /* * compute values for the hash table. see ken rosen's * `elementary number theory and its applications' for one * description of double hashing. */ toff = (int)(real % fscp->fs_inum_size); thop = (int)(real % (fscp->fs_inum_size - 2)) + 1; /* * since we know the hash table isn't full when we get here, * this loop shouldn't terminate except via the `break'. */ for (i = 0; i < fscp->fs_inum_size; i++) { tbl = fscp->fs_inum_trans + toff; if ((tbl->cit_real == 0) || (tbl->cit_real == real)) { tbl->cit_real = real; tbl->cit_fake = fake; break; } toff += thop; toff %= fscp->fs_inum_size; } ASSERT(i < fscp->fs_inum_size); } /* * given an inode number, map it to the inode number that should be * put in a directory entry before its copied out. * * don't call this function unless there is a fscp->fs_inum_trans * table that has real entries in it! */ ino64_t cachefs_inum_real2fake(fscache_t *fscp, ino64_t real) { cachefs_inum_trans_t *tbl; ino64_t rc = real; int toff, thop; int i; ASSERT(fscp->fs_inum_size > 0); ASSERT(MUTEX_HELD(&fscp->fs_fslock)); toff = (int)(real % fscp->fs_inum_size); thop = (int)(real % (fscp->fs_inum_size - 2)) + 1; for (i = 0; i < fscp->fs_inum_size; i++) { tbl = fscp->fs_inum_trans + toff; if (tbl->cit_real == 0) { break; } else if (tbl->cit_real == real) { rc = tbl->cit_fake; break; } toff += thop; toff %= fscp->fs_inum_size; } return (rc); } /* * Passed a cid, finds the cnode and sets the MD_NEEDATTRS bit * in the metadata. */ static void cachefs_iosetneedattrs(fscache_t *fscp, cfs_cid_t *cidp) { int error; cnode_t *cp; error = cachefs_cnode_make(cidp, fscp, NULL, NULL, NULL, kcred, 0, &cp); if (error) return; mutex_enter(&cp->c_statelock); cp->c_metadata.md_flags |= MD_NEEDATTRS; cp->c_flags |= CN_UPDATED; mutex_exit(&cp->c_statelock); VN_RELE(CTOV(cp)); }