cachefs_vnops.c revision 6f5f1c638c7bce3a35e88526a88fc78bdfd58ffe
/*
* 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
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/vfs_opreg.h>
#include <sys/pathname.h>
#include <sys/sysmacros.h>
#include <sys/bootconf.h>
int cachefs_dnlc; /* use dnlc, debugging */
static char *cachefs_newname(void);
int *eofp);
caller_context_t *);
cred_t *, caller_context_t *);
caller_context_t *);
caller_context_t *);
int *, caller_context_t *);
cred_t *, caller_context_t *);
int, cred_t *, caller_context_t *);
caller_context_t *);
caller_context_t *, int *, pathname_t *);
caller_context_t *, vsecattr_t *);
caller_context_t *, int);
cred_t *, caller_context_t *, int);
char *, cred_t *, caller_context_t *, int);
static int cachefs_mkdir(struct vnode *, char *, struct
int, vsecattr_t *);
cred_t *, caller_context_t *, int);
cred_t *, int *, caller_context_t *, int);
char *, cred_t *, caller_context_t *, int);
caller_context_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
cred_t *, caller_context_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
cred_t *, caller_context_t *);
caller_context_t *);
caller_context_t *);
struct vnodeops *cachefs_vnodeops;
static const fs_operation_def_t cachefs_vnodeops_template[] = {
};
/* forward declarations of statics */
int
cachefs_init_vnops(char *name)
{
return (vn_make_ops(name,
}
struct vnodeops *
cachefs_getvnodeops(void)
{
return (cachefs_vnodeops);
}
static int
{
int error = 0;
int held = 0;
int type;
int connected = 0;
#ifdef CFSDEBUG
printf("cachefs_open: ENTER vpp %p flag %x\n",
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the open operation.
*/
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
goto out;
held = 1;
/* grab creds if we do not have any yet */
}
/* if we are disconnected */
/* if we cannot write to the file system */
connected = 1;
continue;
}
/*
* Allow read only requests to continue
*/
/* track the flag for opening the backvp */
error = 0;
break;
}
/*
* check credentials - if this procs
* credentials don't match the creds in the
* cnode disallow writing while disconnected.
*/
connected = 1;
continue;
}
/* to get here, we know that the WRITE flag is on */
}
/* else if we are connected */
else {
/* if cannot use the cached copy of the file */
/* pass open to the back file */
("cachefs_open (nfsv4): cnode %p, "
held = 0;
continue;
} else if (error) {
break;
}
} else {
/* backvp will be VOP_OPEN'd later */
}
/*
* Now perform a consistency check on the file.
* If strict consistency then force a check to
* the backfs even if the timeout has not expired
* for close-to-open consistency.
*/
type = 0;
type = C_BACK_CHECK;
held = 0;
continue;
}
}
break;
}
if (held)
out:
#ifdef CFS_CD_DEBUG
#endif
#ifdef CFSDEBUG
printf("cachefs_open: EXIT vpp %p error %d\n",
#endif
return (error);
}
/* ARGSUSED */
static int
{
int error = 0;
int held = 0;
int connected = 0;
int close_cnt = 1;
#ifdef CFSDEBUG
#endif
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the close operation.
*/
/*
* File could have been passed in or inherited from the global zone, so
* we don't want to flat out reject the request; we'll just leave things
* the way they are and let the backfs (NFS) deal with it.
*/
/* get rid of any local locks */
if (CFS_ISFS_LLOCK(fscp)) {
}
/* clean up if this is the daemon closing down */
(count == 1)) {
fscp->fs_cddaemonid = 0;
if (fscp->fs_dlogfile)
else
cachep->c_rootdaemonid = 0;
}
return (0);
}
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
goto out;
held = 1;
connected = 0;
/* if not the last close */
if (count > 1) {
goto out;
("cachefs_close (nfsv4): cnode %p, "
held = 0;
continue;
}
}
goto out;
}
/*
* If the file is an unlinked file, then flush the lookup
* cache so that inactive will be called if this is
* the last reference. It will invalidate all of the
* cached pages, without writing them out. Writing them
* out is not required because they will be written to a
* file which will be immediately removed.
*/
/* always call VOP_CLOSE() for back fs vnode */
}
/* force dirty data to stable storage */
/* clean the cachefs pages synchronously */
0, 0, cr);
held = 0;
continue;
} else {
connected = 1;
continue;
}
}
/* if no space left in cache, wait until connected */
connected = 1;
continue;
}
/* clear the cnode error if putpage worked */
}
/* if any other important error */
/* get rid of the pages */
(void) cachefs_putpage_common(vp,
}
}
held = 0;
/* don't decrement the vnode counts again */
close_cnt = 0;
continue;
}
}
break;
}
if (!error)
out:
if (held)
#ifdef CFS_CD_DEBUG
#endif
#ifdef CFSDEBUG
#endif
return (error);
}
/*ARGSUSED*/
static int
{
register u_offset_t off;
register int mapoff;
int n;
int error = 0;
#if 0
#endif
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
return (EISDIR);
return (0);
return (EINVAL);
/*
* Call backfilesystem to read if NFSv4, the cachefs code
* does the read from the back filesystem asynchronously
* which is not supported by pass-through functionality.
*/
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
if (error)
return (error);
}
/*
* Sit in a loop and transfer (uiomove) the data in up to
* MAXBSIZE chunks. Each chunk is mapped into the kernel's
* address space as needed and then released.
*/
do {
/*
* off Offset of current MAXBSIZE chunk
* mapoff Offset within the current chunk
* n Number of bytes to move from this chunk
* base kernel address of mapped in chunk
*/
/* perform consistency check */
if (error)
break;
error = 0;
continue;
}
if (error)
break;
break;
n = diff;
F_SOFTLOCK, S_READ);
if (error) {
else
break;
}
if (error == 0) {
/*
* if we read a whole page(s), or to eof,
* we won't need this page(s) again soon.
*/
flags |= SM_DONTNEED;
}
out:
#ifdef CFSDEBUG
#endif
return (error);
}
/*
* cachefs_read_backfs_nfsv4
*
* Call NFSv4 back filesystem to handle the read (cachefs
* pass-through support for NFSv4).
*/
static int
{
int error;
/*
* For NFSv4 pass-through to work, only connected operation
* is supported, the cnode backvp must exist, and cachefs
* optional (eg., disconnectable) flags are turned off. Assert
* these conditions for the read operation.
*/
/* Call backfs vnode op after extracting backvp */
/* Increment cache miss counter */
return (error);
}
/*ARGSUSED*/
static int
{
int error = 0;
int n, on;
#ifdef CFSDEBUG
"cachefs_write: ENTER vp %p offset %llu count %ld cflags %x\n",
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
goto out;
}
goto out;
}
/* Call backfilesystem to write if NFSv4 */
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out2;
}
if (error)
goto out;
}
for (;;) {
/* do consistency check to get correct file size */
if (error)
goto out;
continue;
}
if (error)
goto out;
break;
}
}
limit = MAXOFFSET_T;
mutex_enter(&p->p_lock);
p, RCA_UNSAFE_SIGINFO);
mutex_exit(&p->p_lock);
goto out;
}
goto out;
}
goto out;
}
/*
* Check to make sure that the process will not exceed
* its limit on file size. It is okay to write up to
* the limit, but not beyond. Thus, the write which
* reaches the limit will be short and the next write
* will return an error.
*/
remainder = 0;
mutex_enter(&p->p_lock);
p->p_rctls, p, RCA_UNSAFE_SIGINFO);
mutex_exit(&p->p_lock);
goto out;
}
}
/* loop around and do the write in MAXBSIZE chunks */
do {
/* mapping offset */
/*
* Touch the page and fault it in if it is not in
* core before segmap_getmapflt can lock it. This
* is to avoid the deadlock if the buffer is mapped
* to the same file through mmap which we want to
* write to.
*/
uio_prefaultpages((long)n, uiop);
if (error == 0) {
flags = 0;
/*
* Have written a whole block.Start an
* asynchronous write and mark the buffer to
* indicate that it won't be needed again
* soon.
*/
}
#if 0
/* XXX need to understand this */
}
#else
}
#endif
} else {
}
out:
} else
out2:
#ifdef CFSDEBUG
#endif
return (error);
}
/*
* cachefs_write_backfs_nfsv4
*
* Call NFSv4 back filesystem to handle the write (cachefs
* pass-through support for NFSv4).
*/
static int
{
int error;
/*
* For NFSv4 pass-through to work, only connected operation
* is supported, the cnode backvp must exist, and cachefs
* optional (eg., disconnectable) flags are turned off. Assert
* these conditions for the read operation.
*/
/* Call backfs vnode op after extracting the backvp */
return (error);
}
/*
* see if we've charged ourselves for frontfile data at
* the given offset. If not, allocate a block for it now.
*/
static int
{
int error;
int inc;
/*LINTED*/
error = 0;
/* get the front file if necessary so allocblocks works */
(void) cachefs_getfrontfile(cp);
}
return (1);
return (0);
return (0);
if (error == 0) {
}
return (error);
}
/*
* Called only by cachefs_write to write 1 page or less of data.
* base - base address kernel addr space
* tcount - Total bytes to move - < MAXBSIZE
*/
static int
{
register int n;
register u_offset_t offset;
int pagecreate = 0;
int newpage;
#ifdef CFSDEBUG
"cachefs_writepage: ENTER vp %p offset %llu len %ld\\\n",
#endif
/*
* Move bytes in PAGESIZE chunks. We must avoid spanning pages in
* uiomove() because page faults may cause the cache to be invalidated
* out from under us.
*/
do {
/*
* If not connected then need to make sure we have space
* to perform the write. We could make this check
* a little tighter by only doing it if we are growing the file.
*/
if (error)
break;
}
/*
* n is the number of bytes required to satisfy the request
* or the number of bytes to fill out the page.
*/
if (n > tcount)
n = tcount;
/*
* The number of bytes of data in the last page can not
* be accurately be determined while page is being
* uiomove'd to and the size of the file being updated.
* Thus, inform threads which need to know accurately
* how much data is in the last page of the file. They
* will not do the i/o immediately, but will arrange for
* the i/o to happen later when this modify operation
* will have finished.
*
* in similar NFS code, this is done right before the
* uiomove(), which is best. but here in cachefs, we
* have two uiomove()s, so we must do it here.
*/
/*
* Check to see if we can skip reading in the page
* and just allocate the memory. We can do this
* if we are going to rewrite the entire mapping
* or if we are going to write to or beyond the current
* end of file from the beginning of the mapping.
*/
pagecreate = 1;
/*
* segmap_pagecreate() returns 1 if it calls
* page_create_va() to allocate any pages.
*/
PAGESIZE, 0);
/* do not zero page if we are overwriting all of it */
(n == PAGESIZE))) {
(void) kzero((void *)
PAGESIZE);
}
/*
* Unlock the page allocated by page_create_va()
* in segmap_pagecreate()
*/
if (newpage)
} else {
/*
* KLUDGE ! Use segmap_fault instead of faulting and
* using as_fault() to avoid a recursive readers lock
* on kas.
*/
if (error) {
else
break;
}
}
base += n;
tcount -= n;
/* get access to the file system */
break;
}
/*
* cp->c_attr.va_size is the maximum number of
* bytes known to be in the file.
* Make sure it is at least as high as the
* last byte we just wrote into the buffer.
*/
}
}
/* c_size is now correct, so we can clear modinprog */
if (error == 0) {
/*
* if we're not in NOCACHE mode
* (i.e., single-writer), we update the
* allocmap here rather than waiting until
* cachefspush is called. This prevents
* getpage from clustering up pages from
* the backfile and stomping over the changes
* we make here.
*/
}
/* else we ran out of space */
else {
/* nocache file if connected */
if (fscp->fs_cdconnected ==
/*
* If disconnected then cannot
* nocache the file. Let it have
* the space.
*/
else {
}
}
}
}
/* XXX assert error != 0? FC_ERRNO() makes this more risky. */
}
#ifdef CFS_CD_DEBUG
#endif
#ifdef CFSDEBUG
#endif
return (error);
}
/*
*/
static int
{
int error;
/*
* Find a kluster that fits in one block, or in
* one page if pages are bigger than blocks. If
* there is less file space allocated than a whole
* page, we'll shorten the i/o request below.
*/
/*
* The CN_CMODINPROG flag makes sure that we use a correct
* value of c_size, below. CN_CMODINPROG is set in
* cachefs_writepage(). When CN_CMODINPROG is set it
* indicates that a uiomove() is in progress and the c_size
* has not been made consistent with the new size of the
* file. When the uiomove() completes the c_size is updated
* and the CN_CMODINPROG flag is cleared.
*
* The CN_CMODINPROG flag makes sure that cachefs_push_front
* and cachefs_push_connected see a consistent value of
* c_size. Without this handshaking, it is possible that
* these routines will pick up the old value of c_size before
* the uiomove() in cachefs_writepage() completes. This will
* result in the vn_rdwr() being too small, and data loss.
*
* More precisely, there is a window between the time the
* uiomove() completes and the time the c_size is updated. If
* a VOP_PUTPAGE() operation intervenes in this window, the
* page will be picked up, because it is dirty; it will be
* unlocked, unless it was pagecreate'd. When the page is
* picked up as dirty, the dirty bit is reset
* (pvn_getdirty()). In cachefs_push_connected(), c_size is
* checked. This will still be the old size. Therefore, the
* page will not be written out to the correct length, and the
* page will be clean, so the data may disappear.
*/
/*
* A write is in progress for this region of
* the file. If we did not detect
* CN_CMODINPROG here then this path through
* cachefs_push_connected() would eventually
* do the vn_rdwr() and may not write out all
* of the data in the pages. We end up losing
* data. So we decide to set the modified bit
* on each page in the page list and mark the
* cnode with CDIRTY. This push will be
* restarted at some later time.
*/
hat_setmod(pp);
}
if (offp)
if (lenp)
return (0);
}
}
/*
* Set the pages up for pageout.
*/
/*
* currently, there is no way for pageio_setup() to
* return NULL, since it uses its own scheme for
* kmem_alloc()ing that shouldn't return NULL, and
* since pageio_setup() itself dereferences the thing
* it's about to return. still, we need to be ready
* in case this ever does start happening.
*/
goto writedone;
}
/*
* pageio_setup should have set b_addr to 0. This
* is correct since we want to do I/O on a page
* boundary. bp_mapin will use this addr to calculate
* an offset, and then set b_addr to the kernel virtual
* address it allocated for us.
*/
/* if connected */
/* write to the back file first */
/* write to the front file if allowed */
/* try to write to the front file */
}
}
/* else if disconnected */
else {
/* try to write to the front file */
}
if (offp)
if (lenp)
/* XXX ask bob mastors how to fix this someday */
if (error) {
CFS_ISFS_SOFT(fscp)) {
}
}
}
return (error);
}
/*
* Pushes out pages to the back file system.
*/
static int
{
int error = 0;
int mode = 0;
/* get the back file if necessary */
if (error) {
goto out;
}
}
/* write to the back file */
if (error) {
#ifdef CFSDEBUG
printf("cachefspush: error %d cr %p\n",
#endif
}
out:
return (error);
}
/*
* Pushes out pages to the front file system.
* Called for both connected and disconnected states.
*/
static int
{
int error = 0;
enum cachefs_rl_type type;
if (!CFS_ISFS_NONSHARED(fscp)) {
goto out;
}
/* get the front file if necessary */
(void) cachefs_getfrontfile(cp);
}
goto out;
}
/* if disconnected, needs to be populated and have good attributes */
goto out;
}
goto out;
} else {
goto out;
}
}
}
/* log the first putpage to a file */
/* uses open's creds if we have them */
if (error) {
goto out;
}
}
if (commit == 0) {
/* out of space */
goto out;
}
}
/* subsequent putpages just get a new sequence number */
else {
/* but only if it matters */
if (seq == 0) {
goto out;
}
/* XXX maybe should do write_metadata here */
}
}
}
if (error) {
error = 0;
goto out;
} else {
goto out;
}
}
}
out:
if (commit) {
/* commit the log record */
/*EMPTY*/
/* XXX fix on panic */
}
}
}
return (error);
}
/*ARGSUSED*/
static int
{
return (ENOSYS); /* should we panic if we get here? */
}
/*ARGSUSED*/
static int
{
int error;
struct cachefscache *cachep;
extern kmutex_t cachefs_cachelock;
extern cachefscache_t *cachefs_cachelist;
int (*dcmd_routine)(vnode_t *, void *, void *);
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions which ensure
* that only a subset of the ioctls are "truly supported"
* for NFSv4 (these are CFSDCMD_DAEMONID and CFSDCMD_GETSTATS.
* The packing operations are meaningless since there is
* no caching for NFSv4, and the called functions silently
* return if the backfilesystem is NFSv4. The daemon
* commands except for those above are essentially used
* for disconnectable operation support (including log
* rolling), so in each called function, we assert that
* NFSv4 is not in use. The _FIO* calls (except _FIOCOD)
* are from "cfsfstype" which is not a documented
* command. However, the command is visible in
* through (don't seem to impact pass-through functionality).
*/
switch (cmd) {
case CACHEFSIO_PACK:
if (!error)
break;
case CACHEFSIO_UNPACK:
if (!error)
break;
case CACHEFSIO_PACKINFO:
if (!error)
if (!error)
sizeof (cachefsio_pack_t));
break;
case CACHEFSIO_UNPACKALL:
break;
case CACHEFSIO_DCMD:
/*
* This is a private interface between the cachefsd and
* this file system.
*/
/* must be root to use these commands */
return (EPERM);
/* get the command packet */
if (error)
return (error);
/* copy in the data for the operation */
inlen);
if (error)
return (error);
}
/* allocate space for the result */
/*
* Assert NFSv4 only allows the daemonid and getstats
* daemon requests
*/
/* get the routine to execute */
dcmd_routine = NULL;
case CFSDCMD_DAEMONID:
break;
case CFSDCMD_STATEGET:
break;
case CFSDCMD_STATESET:
break;
case CFSDCMD_XWAIT:
break;
case CFSDCMD_EXISTS:
break;
case CFSDCMD_LOSTFOUND:
break;
case CFSDCMD_GETINFO:
break;
case CFSDCMD_CIDTOFID:
break;
case CFSDCMD_GETATTRFID:
break;
case CFSDCMD_GETATTRNAME:
break;
case CFSDCMD_GETSTATS:
break;
case CFSDCMD_ROOTFID:
break;
case CFSDCMD_CREATE:
break;
case CFSDCMD_REMOVE:
break;
case CFSDCMD_LINK:
break;
case CFSDCMD_RENAME:
break;
case CFSDCMD_MKDIR:
break;
case CFSDCMD_RMDIR:
break;
case CFSDCMD_SYMLINK:
break;
case CFSDCMD_SETATTR:
break;
case CFSDCMD_SETSECATTR:
break;
case CFSDCMD_PUSHBACK:
break;
default:
break;
}
/* execute the routine */
if (dcmd_routine)
/* copy out the result */
outlen);
/* free allocated memory */
if (dinp)
if (doutp)
break;
case _FIOCOD:
break;
}
if (arg) {
/* non-zero arg means do all filesystems */
if (CFS_ISFS_CODCONST(fscp)) {
error = 0;
}
}
}
} else {
if (CFS_ISFS_CODCONST(fscp)) {
error = 0;
}
}
break;
case _FIOSTOPCACHE:
break;
default:
break;
}
/* return the result */
return (error);
}
{
for (;;) {
break;
}
return (new);
}
/*ARGSUSED*/
static int
{
int error = 0;
int held = 0;
int connected = 0;
#ifdef CFSDEBUG
#endif
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
/* Call backfilesystem getattr if NFSv4 */
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
/*
* If it has been specified that the return value will
* just be used as a hint, and we are only being asked
* for size, fsid or rdevid, then return the client's
* notion of these values without checking to make sure
* that the attribute cache is up to date.
* The whole point is to avoid an over the wire GETATTR
* call.
*/
/*
* Return the FSID of the cachefs filesystem,
* not the back filesystem
*/
return (0);
}
}
/*
* Only need to flush pages if asking for the mtime
* and if there any dirty pages.
*/
/*EMPTY*/
#if 0
/*
* XXX bob: stolen from nfs code, need to do something similar
*/
#endif
}
for (;;) {
/* get (or renew) access to the file system */
if (held) {
held = 0;
}
if (error)
goto out;
held = 1;
/*
* If it has been specified that the return value will
* just be used as a hint, and we are only being asked
* for size, fsid or rdevid, then return the client's
* notion of these values without checking to make sure
* that the attribute cache is up to date.
* The whole point is to avoid an over the wire GETATTR
* call.
*/
/*
* Return the FSID of the cachefs filesystem,
* not the back filesystem
*/
goto out;
}
}
connected = 1;
continue;
}
held = 0;
continue;
}
if (error) {
break;
}
/* check for fileno conflict */
if ((fscp->fs_inum_size > 0) &&
if (fakenum == 0) {
}
}
/* copy out the attributes */
/*
* return the FSID of the cachefs filesystem,
* not the back filesystem
*/
/* return our idea of the size */
/* overwrite with our version of fileno and timestamps */
break;
}
out:
if (held)
#ifdef CFS_CD_DEBUG
#endif
#ifdef CFSDEBUG
#endif
return (error);
}
/*
* cachefs_getattr_backfs_nfsv4
*
* Call NFSv4 back filesystem to handle the getattr (cachefs
* pass-through support for NFSv4).
*/
static int
{
int error;
/*
* For NFSv4 pass-through to work, only connected operation
* is supported, the cnode backvp must exist, and cachefs
* optional (eg., disconnectable) flags are turned off. Assert
* these conditions for the getattr operation.
*/
/* Call backfs vnode op after extracting backvp */
/* Update attributes */
/*
* return the FSID of the cachefs filesystem,
* not the back filesystem
*/
return (error);
}
/*ARGSUSED4*/
static int
int flags,
{
int error;
int connected;
int held = 0;
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the setattr operation.
*/
connected = 0;
for (;;) {
/* drop hold on file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
/* acquire access to the file system */
if (error)
break;
held = 1;
/* perform the setattr */
if (error) {
/* if connected */
held = 0;
connected = 0;
continue;
}
}
/* else must be disconnected */
else {
connected = 1;
continue;
}
}
}
break;
}
if (held) {
}
#ifdef CFS_CD_DEBUG
#endif
return (error);
}
static int
int flags,
{
int error = 0;
/* Cannot set these attributes. */
return (EINVAL);
/*
* Truncate file. Must have write permission and not be a directory.
*/
return (EISDIR);
}
}
/*
* Gotta deal with one special case here, where we're setting the
* size of the file. First, we zero out part of the page after the
* new size of the file. Then we toss (not write) all pages after
* page in which the new offset occurs. Note that the NULL passed
* in instead of a putapage() fn parameter is correct, since
* no dirty pages will be found (B_TRUNC | B_INVAL).
*/
/* sync dirty pages */
if (!CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
error = 0;
/* if connected */
}
/* else must be disconnected */
else {
}
if (error)
goto out;
/*
* If the file size has been changed then
* toss whole pages beyond the end of the file and zero
* the portion of the last page that is beyond the end of the file.
*/
if (bcnt)
}
out:
return (error);
}
static int
int flags,
{
int error = 0;
int setsize;
if (error)
goto out;
}
if (error)
goto out;
if (error) {
goto out;
}
/* if the size of the file is being changed */
error = 0;
setsize = 0;
/* see if okay to try to set the file size */
/* okay to set size if file is populated */
setsize = 1;
/*
* Okay to set size if front file exists and setting
* file size to zero.
*/
setsize = 1;
}
/* if okay to try to set the file size */
if (setsize) {
error = 0;
if (error == 0)
/* make sure file gets nocached */
}
/* if we have to nocache the file */
if (error) {
error = 0;
}
}
/* XXX bob: given what modify_cobject does this seems unnecessary */
if (error)
goto out;
out:
return (error);
}
/*
* perform the setattr on the local file system
*/
/*ARGSUSED4*/
static int
int flags,
{
int mask;
int error;
int newfile;
if (CFS_ISFS_WRITE_AROUND(fscp))
return (ETIMEDOUT);
/* if we do not have good attributes */
return (ETIMEDOUT);
/* primary concern is to keep this routine as much like ufs_setattr */
if (error)
goto out;
/* if changing the size of the file */
goto out;
}
error = 0;
goto out;
}
goto out;
}
goto out;
}
/* if the file is not populated and we are not truncating it */
goto out;
}
if (error) {
goto out;
}
}
/* log the operation */
if (commit == 0) {
goto out;
}
/* special case truncating fast sym links */
/* XXX how can we get here */
/* XXX should update mtime */
error = 0;
goto out;
}
/* get the front file, this may create one */
if (error)
goto out;
}
/* allocate space for the metadata */
== 0);
if (error)
goto out;
}
/* change the size of the front file */
if (error)
goto out;
}
/* mark as modified */
if (cachefs_modified_alloc(cp)) {
goto out;
}
if (error) {
goto out;
}
}
/* log the operation if not already logged */
if (commit == 0) {
if (commit == 0) {
goto out;
}
}
}
/* mark as modified */
if (cachefs_modified_alloc(cp)) {
goto out;
}
if (error) {
goto out;
}
}
/* log the operation if not already logged */
if (commit == 0) {
if (commit == 0) {
goto out;
}
}
}
/* mark as modified */
if (cachefs_modified_alloc(cp)) {
goto out;
}
if (error) {
goto out;
}
}
/* log the operation if not already logged */
if (commit == 0) {
if (commit == 0) {
goto out;
}
}
}
}
out:
/* commit the log entry */
if (commit) {
/*EMPTY*/
/* XXX bob: fix on panic */
}
}
return (error);
}
/* ARGSUSED */
static int
{
int error;
int held = 0;
int connected = 0;
#ifdef CFSDEBUG
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the access operation.
*/
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
cr);
held = 0;
connected = 0;
continue;
}
} else {
if (cachefs_cd_access_miss(fscp)) {
(void) cachefs_getbackvp(fscp,
cp);
}
break;
connected = 0;
continue;
}
connected = 1;
continue;
}
}
break;
}
if (held)
#ifdef CFS_CD_DEBUG
#endif
out:
#ifdef CFSDEBUG
#endif
return (error);
}
static int
{
int error = 0;
/* Make sure the cnode attrs are valid first. */
if (error)
goto out;
/* see if can do a local file system check */
goto out;
}
/* else do a remote file system check */
else {
if (error)
goto out;
}
("cachefs_access (nfsv4): cnode %p, backvp %p\n",
/*
* even though we don't `need' the ACL to do access
* via the backvp, we should cache it here to make our
* behavior more reasonable if we go disconnected.
*/
(cachefs_vtype_aclok(vp)) &&
(!CFS_ISFS_BACKFS_NFSV4(fscp)) &&
}
out:
/*
* If NFS returned ESTALE, mark this cnode as stale, so that
* the vn_open retry will read the file anew from backfs
*/
return (error);
}
/*
* CFS has a fastsymlink scheme. If the size of the link is < C_FSL_SIZE, then
* the link is placed in the metadata itself (no front file is allocated).
*/
/*ARGSUSED*/
static int
{
int error = 0;
int held = 0;
int connected = 0;
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
return (EINVAL);
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the readlink operation.
*/
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
/*
* since readlink_connected will call stuffsymlink
* on success, have to serialize access
*/
if (error) {
held = 0;
break;
}
}
held = 0;
connected = 0;
continue;
}
} else {
if (cachefs_cd_access_miss(fscp)) {
/* as above */
RW_WRITER)) {
connected, 0);
if (error) {
held = 0;
break;
}
}
break;
connected = 0;
continue;
}
connected = 1;
continue;
}
}
break;
}
if (held)
#ifdef CFS_CD_DEBUG
#endif
/*
* The over the wire error for attempting to readlink something
* other than a symbolic link is ENXIO. However, we need to
* return EINVAL instead of ENXIO, so we map it here.
*/
}
static int
{
int error;
int buflen;
int readcache = 0;
if (error)
goto out;
/* if the sym link is cached as a fast sym link */
#ifdef CFSDEBUG
readcache = 1;
goto out;
#else /* CFSDEBUG */
/* XXX KLUDGE! correct for insidious 0-len symlink */
readcache = 1;
goto out;
}
#endif /* CFSDEBUG */
}
/* if the sym link is cached in a front file */
(void) cachefs_getfrontfile(cp);
}
/* read symlink data from frontfile */
uiop->uio_offset = 0;
/* XXX KLUDGE! correct for insidious 0-len symlink */
readcache = 1;
goto out;
}
}
}
/* get the sym link contents from the back fs */
if (error)
goto out;
/* copy the contents out to the user */
/*
* try to cache the sym link, note that its a noop if NOCACHE is set
* or if NFSv4 pass-through is enabled.
*/
}
out:
if (error == 0) {
if (readcache)
else
}
return (error);
}
static int
{
int error;
int readcache = 0;
/* if the sym link is cached as a fast sym link */
readcache = 1;
goto out;
}
/* if the sym link is cached in a front file */
(void) cachefs_getfrontfile(cp);
}
/* read symlink data from frontfile */
uiop->uio_offset = 0;
readcache = 1;
goto out;
}
}
out:
if (error == 0) {
if (readcache)
else
}
return (error);
}
/*ARGSUSED*/
static int
{
int error = 0;
int held = 0;
int connected = 0;
#ifdef CFSDEBUG
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
goto out;
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the fsync operation.
*/
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
connected = 0;
/* if a regular file, write out the pages */
0, 0, cr);
held = 0;
continue;
} else {
connected = 1;
continue;
}
}
/* if no space left in cache, wait until connected */
connected = 1;
continue;
}
/* clear the cnode error if putpage worked */
}
if (error)
break;
}
/* if connected, sync the backvp */
("cachefs_fsync (nfsv4): cnode %p, "
ct);
held = 0;
continue;
}
}
/* sync the metadata and the front file to the front fs */
if (!CFS_ISFS_BACKFS_NFSV4(fscp)) {
if (error &&
error = 0;
}
break;
}
if (error == 0)
if (held)
out:
#ifdef CFS_CD_DEBUG
#endif
#ifdef CFSDEBUG
#endif
return (error);
}
/*
* Called from cachefs_inactive(), to make sure all the data goes out to disk.
*/
int
{
int error = 0;
#ifdef CFSDEBUG
printf("c_sync_metadata: ENTER cp %p cflag %x\n",
#endif
goto out;
goto out;
goto out;
if (CFS_ISFS_BACKFS_NFSV4(fscp))
goto out;
if (error) {
error = 0;
goto out;
}
}
if (error)
goto out;
}
if (error) {
} else {
if (error)
goto out;
}
} else {
}
}
/*
* XXX tony: How can CN_ALLOC_PENDING still be set??
* XXX tony: How can CN_UPDATED not be set?????
*/
&cp->c_metadata);
if (error)
goto out;
}
out:
if (error) {
/* XXX modified files? */
}
}
}
/*
* we clear the updated bit even on errors because a retry
* will probably fail also.
*/
#ifdef CFSDEBUG
printf("c_sync_metadata: EXIT cp %p cflag %x\n",
#endif
return (error);
}
/*
* This is the vop entry point for inactivating a vnode.
* It just queues the request for the async thread which
* calls cachefs_inactive.
* Because of the dnlc, it is not safe to grab most locks here.
*/
/*ARGSUSED*/
static void
{
struct cachefs_req *rp;
#ifdef CFSDEBUG
#endif
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the inactive operation.
*/
/* vn_rele() set the v_count == 1 */
#ifdef CFSDEBUG
#endif
}
/* ARGSUSED */
static int
{
int error = 0;
int held = 0;
int connected = 0;
#ifdef CFSDEBUG
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the lookup operation.
*/
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
held = 0;
connected = 0;
continue;
} else {
if (cachefs_cd_access_miss(fscp)) {
break;
connected = 0;
continue;
}
connected = 1;
continue;
}
}
break;
}
if (held)
} else {
}
}
#ifdef CFS_CD_DEBUG
#endif
out:
#ifdef CFSDEBUG
#endif
return (error);
}
/* ARGSUSED */
int
{
int error = 0;
struct cachefs_req *rp;
/*
* If lookup is for "", just return dvp. Don't need
* to send it over the wire, look it up in the dnlc,
* or perform any access checks.
*/
if (*nm == '\0') {
return (0);
}
/* can't do lookups in non-directories */
return (ENOTDIR);
/* perform access check, also does consistency check if connected */
} else {
}
if (error)
return (error);
/*
* If lookup is for ".", just return dvp. Don't need
* to send it over the wire or look it up in the dnlc,
* just need to check access.
*/
return (0);
}
/* check the dnlc */
if (*vpp)
return (0);
/* read lock the dir before starting the search */
/* if front file is not usable, lookup on the back fs */
else
goto out;
}
/* if the front file is not populated, try to populate it */
goto out;
}
if (cachefs_async_okay()) {
/* cannot populate if cache is not writable */
(CN_ASYNC_POPULATE | CN_NOCACHE)) == 0);
if (error != 0) {
goto out;
}
}
/* no populate if too many asyncs and we have to cache ACLs */
else
goto out;
}
/* by now we have a valid cached front file that we can search */
if (error) {
/* if the entry does not have the fid, go get it */
else
}
/* errors other than does not exist */
else
}
goto out;
}
/*
* Else we found the entry in the cached directory.
* Make a cnode for it.
*/
uncached = 1;
} else
} else if (error == 0) {
}
out:
if (error == 0) {
/* put the entry in the dnlc */
if (cachefs_dnlc)
/* save the cid of the parent so can find the name */
sizeof (cfs_cid_t)) != 0) {
}
}
return (error);
}
/*
* Called from cachefs_lookup_common when the back file system needs to be
* examined to perform the lookup.
*/
static int
{
int error = 0;
/* do a lookup on the back FS to get the back vnode */
if (error)
goto out;
}
("cachefs_lookup (nfsv4): dcp %p, dbackvp %p, name %s\n",
if (error)
goto out;
}
}
/* get the fid and attrs from the back fs */
if (error)
goto out;
#if 0
/* XXX bob: this is probably no longer necessary */
/* if the directory entry was incomplete, we can complete it now */
}
#endif
out:
/* create the cnode */
if (error == 0) {
if (error == 0) {
}
}
if (backvp)
return (error);
}
/*ARGSUSED7*/
static int
{
int error;
int connected = 0;
int held = 0;
#ifdef CFSDEBUG
printf("cachefs_create: ENTER dvp %p, nm %s\n",
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the create operation.
*/
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
/*
* if we are connected, perform the remote portion of the
* create.
*/
held = 0;
connected = 0;
continue;
} else if (error) {
break;
}
}
/* else we must be disconnected */
else {
connected = 1;
continue;
} else if (error) {
break;
}
}
break;
}
if (error == 0)
}
}
if (held)
} else {
}
}
#ifdef CFS_CD_DEBUG
#endif
out:
#ifdef CFSDEBUG
#endif
return (error);
}
static int
{
int error;
/* special case if file already exists */
return (error);
if (error == 0) {
else if ((error =
}
}
if (error) {
} else
return (error);
}
/* consistency check the directory */
if (error) {
goto out;
}
/* get the backvp if necessary */
if (error) {
goto out;
}
}
/* create the file on the back fs */
("cachefs_create (nfsv4): dcp %p, dbackvp %p,"
if (error)
goto out;
} else {
}
/* get the fid and attrs from the back fs */
if (error)
goto out;
/* make the cnode */
if (error)
goto out;
/* enter it in the parent directory */
if (CFS_ISFS_NONSHARED(fscp) &&
/* see if entry already exists */
/* entry, does not exist, add the new file */
if (error) {
error = 0;
}
/* XXX should this be done elsewhere, too? */
} else {
/* entry exists or some other problem */
error = 0;
}
}
out:
if (tvp)
return (error);
}
static int
{
int error = 0;
/* give up if the directory is not populated */
return (ETIMEDOUT);
}
/* special case if file already exists */
return (ETIMEDOUT);
}
if (error == 0) {
if (error) {
return (error);
}
else {
if (!error) {
}
}
}
if (error) {
} else
return (error);
}
/* give up if cannot modify the cache */
if (CFS_ISFS_WRITE_AROUND(fscp)) {
goto out;
}
/* check access */
goto out;
}
/* mark dir as modified */
/* must be privileged to set sticky bit */
/* make up a reasonable set of attributes */
/* create the cnode */
if (error)
goto out;
/* get the front file now instead of later */
if (error) {
goto out;
}
} else {
}
if (error) {
goto out;
}
}
/* set times on the file */
/* reserve space for the daemon cid mapping */
if (error) {
goto out;
}
/* mark the new file as modified */
if (cachefs_modified_alloc(ncp)) {
goto out;
}
/*
* write the metadata now rather than waiting until
* inactive so that if there's no space we can let
* the caller know.
*/
if (error) {
goto out;
}
/* log the operation */
if (commit == 0) {
goto out;
}
/* update parent dir times */
/* enter new file name in the parent directory */
if (error) {
goto out;
}
} else {
goto out;
}
out:
if (commit) {
/*EMPTY*/
/* XXX bob: fix on panic */
}
}
if (error) {
/* destroy the cnode we created */
if (ncp) {
}
} else {
}
return (error);
}
/*ARGSUSED*/
static int
int flags)
{
int error = 0;
int held = 0;
int connected = 0;
int vfslock = 0;
#ifdef CFSDEBUG
printf("cachefs_remove: ENTER dvp %p name %s\n",
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the remove operation.
*/
for (;;) {
if (vfslock) {
vfslock = 0;
}
if (vp) {
}
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
/* if disconnected, do some extra error checking */
/* check permissions */
connected = 1;
continue;
}
if (error)
break;
if (namlen == 0) {
break;
}
/* cannot remove . and .. */
if (nm[0] == '.') {
if (namlen == 1) {
break;
break;
}
}
}
/* get the cnode of the file to delete */
if (error) {
held = 0;
connected = 0;
continue;
}
} else {
connected = 1;
continue;
}
}
}
break;
}
/* must be privileged to remove dirs with unlink() */
break;
/* see ufs_dirremove for why this is done, mount race */
if (vn_vfswlock(vp)) {
break;
}
vfslock = 1;
break;
}
}
held = 0;
connected = 0;
continue;
}
} else {
vp);
connected = 1;
continue;
}
}
break;
}
#if 0
#endif
if (held)
if (vfslock)
if (vp)
#ifdef CFS_CD_DEBUG
#endif
out:
#ifdef CFSDEBUG
#endif
return (error);
}
int
{
int error = 0;
/*
* Acquire the rwlock (WRITER) on the directory to prevent other
* activity on the directory.
*/
/* purge dnlc of this entry so can get accurate vnode count */
/*
* If the cnode is active, make a link to the file
* so operations on the file will continue.
*/
if (error)
goto out;
}
/* else call backfs NFSv4 handler if NFSv4 */
else if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
/* else drop the backvp so nfs does not do rename */
}
}
/* get the backvp */
if (error) {
goto out;
}
}
/* check directory consistency */
if (error) {
goto out;
}
/* perform the remove on the back fs */
if (error) {
goto out;
}
/* the dir has been modified */
/* remove the entry from the populated directory */
if (CFS_ISFS_NONSHARED(fscp) &&
if (error) {
error = 0;
}
}
/* fix up the file we deleted */
else
out:
return (error);
}
/*
* cachefs_remove_backfs_nfsv4
*
* Call NFSv4 back filesystem to handle the remove (cachefs
* pass-through support for NFSv4).
*/
int
{
int error = 0;
/*
* For NFSv4 pass-through to work, only connected operation
* is supported, the cnode backvp must exist, and cachefs
* optional (eg., disconnectable) flags are turned off. Assert
* these conditions for the getattr operation.
*/
/* Should hold the directory readwrite lock to update directory */
/*
* Update attributes for directory. Note that
* CFSOP_CHECK_COBJECT asserts for c_statelock being
* held, so grab it before calling the routine.
*/
if (error)
goto out;
/*
* Update attributes for cp. Note that CFSOP_CHECK_COBJECT
* asserts for c_statelock being held, so grab it before
* calling the routine.
*/
if (error) {
goto out;
}
/*
* Drop the backvp so nfs if the link count is 1 so that
* nfs does not do rename. Ensure that we will destroy the cnode
* since this cnode no longer contains the backvp. Note that we
* maintain lock on this cnode to prevent change till the remove
* completes, otherwise other operations will encounter an ESTALE
* if they try to use the cnode with CN_DESTROY set (see
* cachefs_get_backvp()), or change the state of the cnode
* while we're removing it.
*/
/*
* The unldvp information is created for the case
* when there is more than one reference on the
* vnode when a remove operation is called. If the
* remove itself was holding a reference to the
* vnode, then a subsequent remove will remove the
* backvp, so we need to get rid of the unldvp
* before removing the backvp. An alternate would
* be to simply ignore the remove and let the
* inactivation routine do the deletion of the
* unldvp.
*/
}
}
/* perform the remove on back fs after extracting directory backvp */
("cachefs_remove (nfsv4): dcp %p, dbackvp %p, name %s\n",
if (error) {
goto out;
}
/* fix up the file we deleted, if not destroying the cnode */
}
out:
return (error);
}
int
{
int error = 0;
if (CFS_ISFS_WRITE_AROUND(fscp))
return (ETIMEDOUT);
return (ETIMEDOUT);
/*
* Acquire the rwlock (WRITER) on the directory to prevent other
* activity on the directory.
*/
/* dir must be populated */
goto out;
}
if (error)
goto out;
/* purge dnlc of this entry so can get accurate vnode count */
/*
* If the cnode is active, make a link to the file
* so operations on the file will continue.
*/
if (error)
goto out;
}
if (cachefs_modified_alloc(cp)) {
goto out;
}
if (error) {
goto out;
}
}
}
/* log the remove */
if (commit == 0) {
goto out;
}
/* remove the file from the dir */
goto out;
}
if (error) {
goto out;
}
/* update parent dir times */
/* adjust file we are deleting */
} else {
}
out:
if (commit) {
/* commit the log entry */
/*EMPTY*/
/* XXX bob: fix on panic */
}
}
return (error);
}
/*ARGSUSED*/
static int
{
int error = 0;
int held = 0;
int connected = 0;
#ifdef CFSDEBUG
printf("cachefs_link: ENTER fvp %p tdvp %p tnm %s\n",
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the link operation.
*/
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
held = 0;
connected = 0;
continue;
}
} else {
cr);
connected = 1;
continue;
}
}
break;
}
if (held) {
}
#ifdef CFS_CD_DEBUG
#endif
out:
#ifdef CFSDEBUG
printf("cachefs_link: EXIT fvp %p tdvp %p tnm %s\n",
#endif
return (error);
}
static int
{
int error = 0;
if (error) {
goto out;
}
}
if (error) {
goto out;
}
}
/* get backvp of target directory */
if (error) {
goto out;
}
}
/* consistency check target directory */
if (error) {
goto out;
}
}
/* perform the link on the back fs */
("cachefs_link (nfsv4): tdcp %p, tdbackvp %p, "
if (error) {
goto out;
}
/* if the dir is populated, add the new link */
if (CFS_ISFS_NONSHARED(fscp) &&
if (error) {
error = 0;
}
}
/* get the new link count on the file */
if (error) {
goto out;
}
}
/* XXX bob: given what modify_cobject does this seems unnecessary */
out:
if (backvp)
return (error);
}
static int
{
int error = 0;
return (EPERM);
if (CFS_ISFS_WRITE_AROUND(fscp))
return (ETIMEDOUT);
return (ETIMEDOUT);
/* check permissions */
goto out;
}
/* the directory front file must be populated */
goto out;
}
/* make sure tnm does not already exist in the directory */
goto out;
}
goto out;
}
/* create a mapping for the file if necessary */
if (error) {
goto out;
}
}
/* mark file as modified */
if (cachefs_modified_alloc(fcp)) {
goto out;
}
/* log the operation */
if (commit == 0) {
goto out;
}
/* make the new link */
if (error) {
error = 0;
goto out;
}
/* update the file we linked to */
out:
if (commit) {
/* commit the log entry */
/*EMPTY*/
/* XXX bob: fix on panic */
}
}
return (error);
}
/*
* Serialize all renames in CFS, to avoid deadlocks - We have to hold two
* cnodes atomically.
*/
/*ARGSUSED*/
static int
{
int error = 0;
int held = 0;
int connected = 0;
int vfslock = 0;
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
/*
* if the fs NOFILL or NOCACHE flags are on, then the old and new
* directory cnodes better indicate NOCACHE mode as well.
*/
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the rename operation.
*/
for (;;) {
if (vfslock) {
vfslock = 0;
}
if (delvp) {
}
/* get (or renew) access to the file system */
if (held) {
/* Won't loop for NFSv4 connected support */
held = 0;
}
if (error)
break;
held = 1;
/* sanity check */
break;
}
/* cannot rename from or to . or .. */
break;
}
/*
* if moving a directory, its notion
* of ".." will change
*/
if (error == 0) {
}
} else {
held = 0;
connected = 0;
continue;
}
} else {
connected = 1;
continue;
}
}
break;
}
}
/* get the cnode if file being deleted */
if (error) {
held = 0;
connected = 0;
continue;
}
} else {
connected = 1;
continue;
}
}
break;
}
/* see ufs_dirremove for why this is done, mount race */
if (vn_vfswlock(delvp)) {
break;
}
vfslock = 1;
break;
}
}
held = 0;
connected = 0;
continue;
}
} else {
connected = 1;
continue;
}
}
break;
}
}
if (held)
if (vfslock)
if (delvp)
if (tvp)
#ifdef CFS_CD_DEBUG
#endif
return (error);
}
static int
{
int error = 0;
int gotdirent;
/* find the file we are renaming */
if (error)
return (error);
/*
* To avoid deadlock, we acquire this global rename lock before
* we try to get the locks for the source and target directories.
*/
}
if (error) {
goto out;
}
}
if (error) {
goto out;
}
if (error) {
goto out;
}
}
if (error) {
goto out;
}
}
/* if a file is being deleted because of this rename */
if (delvp) {
/* if src and dest file are same */
error = 0;
goto out;
}
/*
* If the cnode is active, make a link to the file
* so operations on the file will continue.
*/
if (error)
goto out;
}
}
/* do the rename on the back fs */
("cachefs_rename (nfsv4): odcp %p, odbackvp %p, "
" ndcp %p, ndbackvp %p, onm %s, nnm %s\n",
0);
if (error)
goto out;
/* purge mappings to file in the old directory */
/* purge mappings in the new dir if we deleted a file */
/* update the file we just deleted */
if (delvp) {
} else {
}
}
/* find the entry in the old directory */
gotdirent = 0;
if (CFS_ISFS_NONSHARED(fscp) &&
gotdirent = 1;
if (error == 0)
} else {
}
}
error = 0;
/* remove the directory entry from the old directory */
if (gotdirent) {
if (error) {
error = 0;
}
}
/* install the directory entry in the new directory */
if (CFS_ISFS_NONSHARED(fscp) &&
error = 1;
if (gotdirent) {
error = 0;
if (delvp) {
}
if (error == 0) {
}
}
if (error) {
error = 0;
}
}
/* ctime of renamed file has changed */
out:
return (error);
}
static int
{
int error = 0;
if (CFS_ISFS_WRITE_AROUND(fscp))
return (ETIMEDOUT);
/* find the file we are renaming */
if (error)
return (error);
/*
* To avoid deadlock, we acquire this global rename lock before
* we try to get the locks for the source and target directories.
*/
}
goto out;
}
if (error) {
goto out;
}
}
}
/* check permissions */
/* XXX clean up this mutex junk sometime */
if (error != 0)
goto out;
if (error != 0)
goto out;
if (error != 0)
goto out;
/* dirs must be populated */
goto out;
}
/* for now do not allow moving dirs because could cause cycles */
goto out;
}
/* if a file is being deleted because of this rename */
if (delvp) {
/* if src and dest file are the same */
error = 0;
goto out;
}
goto out;
}
/* if there are hard links to this file */
if (cachefs_modified_alloc(delcp)) {
goto out;
}
if (error) {
goto out;
}
}
}
/* make sure we can delete file */
if (error != 0)
goto out;
/*
* If the cnode is active, make a link to the file
* so operations on the file will continue.
*/
if (error)
goto out;
}
}
/* purge mappings to file in the old directory */
/* purge mappings in the new dir if we deleted a file */
/* find the entry in the old directory */
goto out;
}
if (error == 0)
} else {
goto out;
}
error = 0;
/* write the log entry */
if (commit == 0) {
goto out;
}
/* remove the directory entry from the old directory */
if (error) {
goto out;
}
/* install the directory entry in the new directory */
error = 0;
if (delvp) {
}
if (error == 0) {
}
}
if (error) {
goto out;
}
/* update the file we just deleted */
if (delvp) {
} else {
}
}
/* update the file we renamed */
/* update the source directory */
/* update the destination directory */
}
out:
if (commit) {
/* commit the log entry */
/*EMPTY*/
/* XXX bob: fix on panic */
}
}
return (error);
}
/*ARGSUSED*/
static int
{
int error = 0;
int held = 0;
int connected = 0;
#ifdef CFSDEBUG
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the mkdir operation.
*/
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
held = 0;
connected = 0;
continue;
}
} else {
connected = 1;
continue;
}
}
break;
}
if (error == 0)
}
}
if (held) {
}
#ifdef CFS_CD_DEBUG
#endif
out:
#ifdef CFSDEBUG
#endif
return (error);
}
static int
{
int error = 0;
/* get backvp of dir */
if (error) {
goto out;
}
}
/* consistency check the directory */
if (error) {
goto out;
}
/* make the dir on the back fs */
("cachefs_mkdir (nfsv4): dcp %p, dbackvp %p, "
if (error) {
goto out;
}
/* get the cookie and make the cnode */
if (error) {
goto out;
}
if (error) {
goto out;
}
/* if the dir is populated, add the new entry */
if (CFS_ISFS_NONSHARED(fscp) &&
SM_ASYNC);
if (error) {
error = 0;
}
}
/* XXX bob: should we do a filldir here? or just add . and .. */
/* maybe should kick off an async filldir so caller does not wait */
/* put the entry in the dnlc */
if (cachefs_dnlc)
/* save the fileno of the parent so can find the name */
sizeof (cfs_cid_t)) != 0) {
}
out:
if (vp)
return (error);
}
static int
{
int error;
char *s;
int namlen;
/* don't allow '/' characters in pathname component */
if (*s == '/')
return (EACCES);
if (namlen == 0)
return (EINVAL);
if (CFS_ISFS_WRITE_AROUND(fscp))
return (ETIMEDOUT);
/* check permissions */
goto out;
}
/* the directory front file must be populated */
goto out;
}
/* make sure nm does not already exist in the directory */
goto out;
}
goto out;
}
/* make up a reasonable set of attributes */
/* create the cnode */
if (error)
goto out;
if (error) {
goto out;
}
/* make a front file for the new directory, add . and .. */
if (error) {
goto out;
}
/*
* write the metadata now rather than waiting until
* inactive so that if there's no space we can let
* the caller know.
*/
if (error) {
goto out;
}
/* log the operation */
if (commit == 0) {
goto out;
}
/* make sure directory is still populated */
goto out;
}
/* enter the new file in the directory */
if (error) {
goto out;
}
/* update parent dir times */
out:
if (commit) {
/* commit the log entry */
/*EMPTY*/
/* XXX bob: fix on panic */
}
}
if (error) {
if (newcp) {
}
} else {
}
return (error);
}
/*ARGSUSED*/
static int
{
int error = 0;
int held = 0;
int connected = 0;
int vfslock = 0;
#ifdef CFSDEBUG
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the rmdir operation.
*/
for (;;) {
if (vfslock) {
vfslock = 0;
}
if (vp) {
}
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
/* if disconnected, do some extra error checking */
/* check permissions */
connected = 1;
continue;
}
if (error)
break;
if (namlen == 0) {
break;
}
/* cannot remove . and .. */
if (nm[0] == '.') {
if (namlen == 1) {
break;
break;
}
}
}
/* get the cnode of the dir to remove */
if (error) {
held = 0;
connected = 0;
continue;
}
} else {
connected = 1;
continue;
}
}
break;
}
/* must be a dir */
break;
}
/* must not be current dir */
break;
}
/* see ufs_dirremove for why this is done, mount race */
if (vn_vfswlock(vp)) {
break;
}
vfslock = 1;
break;
}
held = 0;
connected = 0;
continue;
}
} else {
connected = 1;
continue;
}
}
break;
}
if (vp)
}
}
if (held) {
}
if (vfslock)
if (vp)
#ifdef CFS_CD_DEBUG
#endif
out:
#ifdef CFSDEBUG
#endif
return (error);
}
static int
{
int error = 0;
if (error) {
goto out;
}
}
if (error)
goto out;
/* rmdir on the back fs */
("cachefs_rmdir (nfsv4): dcp %p, dbackvp %p, "
if (error)
goto out;
/* if the dir is populated, remove the entry from it */
if (CFS_ISFS_NONSHARED(fscp) &&
if (error) {
error = 0;
}
}
/*
* *if* the (hard) link count goes to 0, then we set the CDESTROY
* flag on the cnode. The cached object will then be destroyed
* at inactive time where the chickens come home to roost :-)
* The link cnt for directories is bumped down by 2 'cause the "."
* entry has to be elided too ! The link cnt for the parent goes down
* by 1 (because of "..").
*/
} else {
}
out:
return (error);
}
static int
/*ARGSUSED*/
{
int error = 0;
if (CFS_ISFS_WRITE_AROUND(fscp))
return (ETIMEDOUT);
/* both directories must be populated */
goto out;
}
/* if sticky bit set on the dir, more access checks to perform */
goto out;
}
/* make sure dir is empty */
if (error) {
goto out;
}
}
/* log the operation */
if (commit == 0) {
goto out;
}
/* remove name from parent dir */
goto out;
}
if (error)
goto out;
/* update deleted dir values */
else {
}
/* update parent values */
out:
if (commit) {
/* commit the log entry */
/*EMPTY*/
/* XXX bob: fix on panic */
}
}
return (error);
}
/*ARGSUSED*/
static int
{
int error = 0;
int held = 0;
int connected = 0;
#ifdef CFSDEBUG
printf("cachefs_symlink: ENTER dvp %p lnm %s tnm %s\n",
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the symlink operation.
*/
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
held = 0;
connected = 0;
continue;
}
} else {
connected = 1;
continue;
}
}
break;
}
if (held) {
}
#ifdef CFS_CD_DEBUG
#endif
out:
#ifdef CFSDEBUG
#endif
return (error);
}
static int
{
int error = 0;
if (error) {
goto out;
}
}
if (error) {
goto out;
}
("cachefs_symlink (nfsv4): dcp %p, dbackvp %p, "
if (error) {
goto out;
}
goto out;
}
/* lookup the symlink we just created and get its fid and attrs */
if (CFS_ISFS_BACKFS_NFSV4(fscp) == 0)
goto out;
}
if (error) {
error = 0;
goto out;
}
/* if the dir is cached, add the symlink to it */
if (CFS_ISFS_NONSHARED(fscp) &&
if (error) {
error = 0;
}
}
/* make the cnode for the sym link */
if (error) {
error = 0;
goto out;
}
/* try to cache the symlink contents */
/*
* try to cache the sym link, note that its a noop if NOCACHE
* or NFSv4 is set
*/
if (error) {
error = 0;
}
out:
if (backvp)
if (newcp)
return (error);
}
static int
{
int error;
if (CFS_ISFS_WRITE_AROUND(fscp))
return (ETIMEDOUT);
/* check permissions */
goto out;
}
/* the directory front file must be populated */
goto out;
}
/* make sure lnm does not already exist in the directory */
goto out;
}
goto out;
}
/* make up a reasonable set of attributes */
/* create the cnode */
if (error)
goto out;
if (error) {
goto out;
}
/* log the operation */
if (commit == 0) {
goto out;
}
/* store the symlink contents */
if (error) {
goto out;
}
if (cachefs_modified_alloc(newcp)) {
goto out;
}
/*
* write the metadata now rather than waiting until
* inactive so that if there's no space we can let
* the caller know.
*/
}
if (error) {
goto out;
}
}
if (error) {
goto out;
}
/* enter the new file in the directory */
goto out;
}
if (error) {
goto out;
}
/* update parent dir times */
out:
if (commit) {
/* commit the log entry */
/*EMPTY*/
/* XXX bob: fix on panic */
}
}
if (error) {
if (newcp) {
}
}
if (newcp) {
}
return (error);
}
/*ARGSUSED*/
static int
{
int error = 0;
int held = 0;
int connected = 0;
#ifdef CFSDEBUG
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the readdir operation.
*/
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
/* quit if link count of zero (posix) */
if (eofp)
*eofp = 1;
error = 0;
break;
}
eofp);
held = 0;
connected = 0;
continue;
}
} else {
eofp);
if (cachefs_cd_access_miss(fscp)) {
break;
connected = 0;
continue;
}
connected = 1;
continue;
}
}
break;
}
if (held) {
}
#ifdef CFS_CD_DEBUG
#endif
out:
#ifdef CFSDEBUG
#endif
return (error);
}
static int
{
int error;
struct cachefs_req *rp;
/* check directory consistency */
if (error)
goto out;
/* if dir was modified, toss old contents */
}
error = 0;
if (cachefs_async_okay()) {
/*
* Set up asynchronous request to fill this
* directory.
*/
} else {
if (error != 0)
}
}
/* if front file is populated */
if (error == 0)
}
/* if front file could not be used */
if ((error != 0) ||
/* get the back vp */
if (error)
goto out;
}
if (fscp->fs_inum_size > 0) {
} else {
/* do the dir read from the back fs */
("cachefs_readdir (nfsv4): "
NULL, 0);
}
if (error == 0)
}
out:
return (error);
}
static int
{
int error = 0;
if (error != 0)
goto out;
if (newinum == 0)
}
out:
return (error);
}
static int
/*ARGSUSED*/
int *eofp)
{
int error;
} else {
}
return (error);
}
/*ARGSUSED*/
static int
{
int error = 0;
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions, then bail
* as NFSv4 doesn't support VOP_FID.
*/
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
return (ENOTSUP);
}
} else {
}
return (error);
}
/* ARGSUSED2 */
static int
{
/*
* XXX - This is ifdef'ed out for now. The problem -
* getdents() acquires the read version of rwlock, then we come
* into cachefs_readdir() and that wants to acquire the write version
* of this lock (if its going to populate the directory). This is
* a problem, this can be solved by introducing another lock in the
* cnode.
*/
/* XXX */
return (-1);
if (write_lock)
else
return (write_lock);
}
/* ARGSUSED */
static void
{
return;
}
/* ARGSUSED */
static int
{
return (0);
}
static int cachefs_lostpage = 0;
/*
* Return all the pages from [off..off+len] in file
*/
/*ARGSUSED*/
static int
{
int error;
int held = 0;
int connected = 0;
#ifdef CFSDEBUG
printf("cachefs_getpage: ENTER vp %p off %lld len %lu rw %d\n",
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
goto out;
}
/* Call backfilesystem if NFSv4 */
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
/* XXX sam: make this do an async populate? */
error = 0;
goto out;
}
for (;;) {
/* get (or renew) access to the file system */
if (held) {
held = 0;
}
if (error)
break;
held = 1;
/*
* If we are getting called as a side effect of a
* cachefs_write()
* operation the local file size might not be extended yet.
* In this case we want to be able to return pages of zeroes.
*/
break;
}
}
else
if (error == 0)
break;
connected = 0;
continue;
}
held = 0;
connected = 0;
continue;
}
} else {
if (cachefs_cd_access_miss(fscp)) {
else
break;
connected = 0;
continue;
}
connected = 1;
continue;
}
}
break;
}
if (held) {
}
out:
#ifdef CFS_CD_DEBUG
#endif
#ifdef CFSDEBUG
printf("cachefs_getpage: EXIT vp %p error %d\n",
#endif
return (error);
}
/*
* cachefs_getpage_backfs_nfsv4
*
* Call NFSv4 back filesystem to handle the getpage (cachefs
* pass-through support for NFSv4).
*/
static int
{
int error;
/*
* For NFSv4 pass-through to work, only connected operation is
* supported, the cnode backvp must exist, and cachefs optional
* (eg., disconnectable) flags are turned off. Assert these
* conditions for the getpage operation.
*/
/* Call backfs vnode op after extracting backvp */
("cachefs_getpage_backfs_nfsv4: cnode %p, backvp %p\n",
return (error);
}
/*
* Called from pvn_getpages or cachefs_getpage to get a particular page.
*/
/*ARGSUSED*/
static int
{
int error = 0;
int index = 0;
int downgrade;
int have_statelock = 0;
/*LINTED*/
else
ourpl = ourstackpl;
/*
* Look for the page
*/
/*
* Need to do work to get the page.
* Grab our lock because we are going to
* modify the state of the cnode.
*/
if (! have_statelock) {
have_statelock = 1;
}
/*
* If we're in NOCACHE mode, we will need a backvp
*/
goto out;
}
if (error)
goto out;
}
/*
* backfs returns EFAULT when we are trying for a
* page beyond EOF but cachefs has the knowledge that
* it is not beyond EOF be cause cp->c_size is
* greater then the offset requested.
*/
error = 0;
goto again;
goto out;
}
if (error)
goto out;
goto getpages;
}
/*
* We need a front file. If we can't get it,
* put the cnode in NOCACHE mode and try again.
*/
if (error) {
goto out;
}
}
/*
* Check if the front file needs population.
* If population is necessary, make sure we have a
* backvp as well. We will get the page from the backvp.
* bug 4152459-
* But if the file system is in disconnected mode
* and the file is a local file then do not check the
* allocmap.
*/
goto out;
}
if (error)
goto out;
}
&popsize,
if (popsize != 0) {
if (error) {
goto out;
} else {
}
} else {
}
}
/* else XXX assert CN_NOCACHE? */
if (error)
goto out;
} else {
NULL);
if (error) {
goto out;
}
}
/*
* File was populated so we get the page from the
* frontvp
*/
if (error) {
goto out;
}
}
if (have_statelock) {
have_statelock = 0;
}
downgrade = 0;
index++;
page_unlock(*ppp);
continue;
}
if (PAGE_SHARED(*ppp)) {
if (page_tryupgrade(*ppp) == 0) {
page_unlock(*ppp);
goto out;
}
downgrade = 1;
}
}
if (downgrade) {
}
/* Unlock the rest of the pages from the cluster */
page_unlock(*ppp);
} else {
ASSERT(! have_statelock);
if (have_statelock) {
have_statelock = 0;
}
/* XXX SE_SHARED probably isn't what we *always* want */
goto again;
}
/* XXX increment st_hits? i don't think so, but... */
}
out:
if (have_statelock) {
have_statelock = 0;
}
return (error);
}
/* gets a page but only from the back fs */
/*ARGSUSED*/
static int
{
int error = 0;
int index = 0;
int have_statelock = 0;
int downgrade;
/*
* Grab the cnode statelock so the cnode state won't change
* while we're in here.
*/
if (! have_statelock) {
have_statelock = 1;
}
if (error)
goto out;
}
if (error)
goto out;
if (have_statelock) {
have_statelock = 0;
}
downgrade = 0;
index++;
page_unlock(*ppp);
continue;
}
if (PAGE_SHARED(*ppp)) {
if (page_tryupgrade(*ppp) == 0) {
page_unlock(*ppp);
goto out;
}
downgrade = 1;
}
}
if (downgrade) {
}
/* Unlock the rest of the pages from the cluster */
page_unlock(*ppp);
} else {
ASSERT(! have_statelock);
if (have_statelock) {
have_statelock = 0;
}
goto again;
}
}
out:
if (have_statelock) {
have_statelock = 0;
}
return (error);
}
/*ARGSUSED*/
static int
{
int error = 0;
int held = 0;
int connected = 0;
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
/* Call backfilesytem if NFSv4 */
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
for (;;) {
/* get (or renew) access to the file system */
if (held) {
held = 0;
}
if (error)
break;
held = 1;
if (error == 0)
break;
held = 0;
connected = 0;
continue;
}
} else {
if (NOMEMWAIT()) {
error = 0;
goto out;
}
connected = 1;
continue;
}
}
break;
}
out:
if (held) {
}
#ifdef CFS_CD_DEBUG
#endif
return (error);
}
/*
* cachefs_putpage_backfs_nfsv4
*
* Call NFSv4 back filesystem to handle the putpage (cachefs
* pass-through support for NFSv4).
*/
static int
{
int error;
/*
* For NFSv4 pass-through to work, only connected operation is
* supported, the cnode backvp must exist, and cachefs optional
* (eg., disconnectable) flags are turned off. Assert these
* conditions for the putpage operation.
*/
/* Call backfs vnode op after extracting backvp */
("cachefs_putpage_backfs_nfsv4: cnode %p, backvp %p\n",
return (error);
}
/*
* Flags are composed of {B_INVAL, B_FREE, B_DONTNEED, B_FORCE}
* If len == 0, do from off to EOF.
*
* The normal cases should be len == 0 & off == 0 (entire vp list),
* len == MAXBSIZE (from segmap_release actions), and len == PAGESIZE
* (from pageout).
*/
/*ARGSUSED*/
int
{
int error = 0;
return (0);
}
return (0);
/*
* Should never have cached data for the cachefs vnode
* if NFSv4 is in use.
*/
/*
* If this is an async putpage let a thread handle it.
*/
struct cachefs_req *rp;
/*
* If this is the page daemon we
* do the push synchronously (Dangerous!) and hope
* we can free enough to keep running...
*/
goto again;
}
if (! cachefs_async_okay()) {
/*
* this is somewhat like NFS's behavior. keep
* the system from thrashing. we've seen
* cases where async queues get out of
* control, especially if
* madvise(MADV_SEQUENTIAL) is done on a large
* mmap()ed file that is read sequentially.
*/
goto again;
}
/*
* if no flags other than B_ASYNC were set,
* we coalesce putpage requests into a single one for the
* whole file (len = off = 0). If such a request is
* already queued, we're done.
*
* If there are other flags set (e.g., B_INVAL), we don't
* attempt to coalesce and we use the specified length and
* offset.
*/
if (tflags == 0) {
}
} else {
}
return (0);
}
if (len == 0) {
/*
* Search the entire vp list for pages >= off
*/
} else {
/*
* Do a range from [off...off + len] looking for pages
* to deal with.
*/
/*
* If we are not invalidating, synchronously
* freeing or writing pages use the routine
* page_lookup_nowait() to prevent reclaiming
* them from the free list.
*/
} else {
/* XXX this looks like dead code */
}
else {
if (error != 0)
break;
/*
* "io_off" and "io_len" are returned as
* the range of pages we actually wrote.
* This allows us to skip ahead more quickly
* since several pages may've been dealt
* with by this iteration of the loop.
*/
}
}
}
}
return (error);
}
/*ARGSUSED*/
static int
{
struct segvn_crargs vn_a;
int error;
int held = 0;
int writing;
int connected = 0;
#ifdef CFSDEBUG
printf("cachefs_map: ENTER vp %p off %lld len %lu flags %d\n",
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
goto out;
}
goto out;
}
goto out;
}
/*
* Check to see if the vnode is currently marked as not cachable.
* If so, we have to refuse the map request as this violates the
* don't cache attribute.
*/
return (EAGAIN);
#ifdef OBSOLETE
/*
* If file is being locked, disallow mapping.
*/
if (vn_has_flocks(vp)) {
goto out;
}
#endif
/* call backfilesystem if NFSv4 */
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
for (;;) {
/* get (or renew) access to the file system */
if (held) {
held = 0;
}
if (error)
break;
held = 1;
if (writing) {
if (CFS_ISFS_WRITE_AROUND(fscp)) {
connected = 1;
continue;
} else {
}
}
/*
* CN_MAPWRITE is for an optimization in cachefs_delmap.
* If CN_MAPWRITE is not set then cachefs_delmap does
* not need to try to push out any pages.
* This bit gets cleared when the cnode goes inactive.
*/
}
break;
}
if (held) {
}
if (error != 0) {
goto out;
}
/*
* package up all the data passed in into a segvn_args struct and
* call as_map with segvn_create function to create a new segment
* in the address space.
*/
out:
#ifdef CFS_CD_DEBUG
#endif
#ifdef CFSDEBUG
#endif
return (error);
}
/*
* cachefs_map_backfs_nfsv4
*
* Call NFSv4 back filesystem to handle the map (cachefs
* pass-through support for NFSv4).
*/
static int
{
int error;
/*
* For NFSv4 pass-through to work, only connected operation is
* supported, the cnode backvp must exist, and cachefs optional
* (eg., disconnectable) flags are turned off. Assert these
* conditions for the map operation.
*/
/* Call backfs vnode op after extracting backvp */
("cachefs_map_backfs_nfsv4: cnode %p, backvp %p\n",
NULL);
return (error);
}
/*ARGSUSED*/
static int
{
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
return (ENOSYS);
/*
* Check this is not an NFSv4 filesystem, as the mapping
* is not done on the cachefs filesystem if NFSv4 is in
* use.
*/
return (0);
}
/*ARGSUSED*/
static int
{
int error;
int connected = 0;
int held = 0;
/*
* The file may be passed in to (or inherited into) the zone, so we
* need to let this operation go through since it happens as part of
* exiting.
*/
return (ENOSYS);
/*
* Check this is not an NFSv4 filesystem, as the mapping
* is not done on the cachefs filesystem if NFSv4 is in
* use.
*/
return (0);
for (;;) {
/* get (or renew) access to the file system */
if (held) {
held = 0;
}
if (error)
break;
held = 1;
connected = 0;
held = 0;
continue;
} else {
connected = 1;
continue;
}
}
/* if no space left in cache, wait until connected */
connected = 1;
continue;
}
if (!error)
break;
}
if (held)
#ifdef CFS_CD_DEBUG
#endif
return (error);
}
/* ARGSUSED */
static int
{
int error;
int held = 0;
int connected = 0;
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
return (EINVAL);
/* Disallow locking of files that are currently mapped */
return (EAGAIN);
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the frlock operation.
*/
/* XXX bob: nfs does a bunch more checks than we do */
if (CFS_ISFS_LLOCK(fscp)) {
}
for (;;) {
/* get (or renew) access to the file system */
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
/* if not connected, quit or wait */
connected = 1;
continue;
}
/* nocache the file */
}
/*
* XXX bob: probably should do a consistency check
* Pass arguments unchanged if NFSv4 is the backfs.
*/
}
/* get the back vp */
if (error) {
break;
}
}
/*
* make sure we can flush currently dirty pages before
* allowing the lock
*/
if (error) {
break;
}
}
/* do lock on the back file */
("cachefs_frlock (nfsv4): cp %p, backvp %p\n",
ct);
connected = 1;
continue;
}
break;
}
if (held) {
}
/*
* If we are setting a lock mark the vnode VNOCACHE so the page
* cache does not give inconsistent results on locked files shared
* between clients. The VNOCACHE flag is never turned off as long
* as the vnode is active because it is hard to figure out when the
* last lock is gone.
* XXX - what if some already has the vnode mapped in?
* XXX bob: see nfs3_frlock, do not allow locking if vnode mapped in.
*/
#ifdef CFS_CD_DEBUG
#endif
return (error);
}
/*
* Free storage space associated with the specified vnode. The portion
* to be freed is specified by bfp->l_start and bfp->l_len (already
* normalized to a "whence" of 0).
*
* This is an experimental facility whose continued existence is not
* guaranteed. Currently, we only support the special case
* of l_len == 0, meaning free to end of file.
*/
/* ARGSUSED */
static int
{
int error;
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
return (EINVAL);
/* call backfilesystem if NFSv4 */
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
} else
}
out:
return (error);
}
/*
* cachefs_space_backfs_nfsv4
*
* Call NFSv4 back filesystem to handle the space (cachefs
* pass-through support for NFSv4).
*/
static int
{
int error;
/*
* For NFSv4 pass-through to work, only connected operation is
* supported, the cnode backvp must exist, and cachefs optional
* (eg., disconnectable) flags are turned off. Assert these
* conditions for the space operation.
*/
/* Call backfs vnode op after extracting backvp */
("cachefs_space_backfs_nfsv4: cnode %p, backvp %p\n",
return (error);
}
/*ARGSUSED*/
static int
{
return (EINVAL);
}
/*ARGSUSED*/
static int
{
return (ENOSYS);
}
static int
{
int error = 0;
if (error) {
goto out;
}
}
if (error)
goto out;
/* only owner can set acl */
goto out;
}
("cachefs_setsecattr (nfsv4): cp %p, backvp %p",
if (error) {
goto out;
}
goto out;
}
/* acl may have changed permissions -- handle this. */
if (!CFS_ISFS_BACKFS_NFSV4(fscp))
if (error != 0) {
#ifdef CFSDEBUG
printf("cachefs_setacl: cacheacl: error %d\n",
error);
#endif /* CFSDEBUG */
error = 0;
}
}
out:
return (error);
}
static int
{
int error = 0;
if (CFS_ISFS_WRITE_AROUND(fscp))
return (ETIMEDOUT);
/* only owner can set acl */
goto out;
}
goto out;
}
/* XXX do i need this? is this right? */
}
if (error) {
goto out;
}
}
/* XXX is this right? */
if (error) {
goto out;
}
}
if (commit == 0)
goto out;
/* fix modes in metadata */
if (error != 0) {
goto out;
}
}
/* XXX is this right? */
if (cachefs_modified_alloc(cp)) {
goto out;
}
out:
if (error != 0)
if (commit) {
/*EMPTY*/
/* XXX fix on panic? */
}
}
return (error);
}
/*ARGSUSED*/
static int
{
int connected = 0;
int held = 0;
int error = 0;
#ifdef CFSDEBUG
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
goto out;
}
if (! cachefs_vtype_aclok(vp)) {
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the setsecattr operation.
*/
for (;;) {
/* drop hold on file system */
if (held) {
/* Won't loop with NFSv4 connected operation */
held = 0;
}
/* acquire access to the file system */
if (error)
break;
held = 1;
/* perform the setattr */
else
if (error) {
/* if connected */
held = 0;
connected = 0;
continue;
}
}
/* else must be disconnected */
else {
connected = 1;
continue;
}
}
}
break;
}
if (held) {
}
return (error);
out:
#ifdef CFS_CD_DEBUG
#endif
#ifdef CFSDEBUG
#endif
return (error);
}
/*
* call this BEFORE calling cachefs_cacheacl(), as the latter will
* sanitize the acl.
*/
static void
{
int i;
for (i = 0; i < vsec->vsa_aclcnt; i++) {
case USER_OBJ:
break;
case GROUP_OBJ:
break;
case OTHER_OBJ:
break;
case CLASS_OBJ:
break;
}
}
}
static int
{
int error = 0;
#ifdef CFSDEBUG
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the getsecattr operation.
*/
goto out;
}
for (;;) {
if (held) {
/* Won't loop with NFSv4 connected behavior */
held = 0;
}
if (error)
break;
held = 1;
cr);
held = 0;
connected = 0;
continue;
}
} else {
cr);
if (cachefs_cd_access_miss(fscp)) {
break;
connected = 0;
continue;
}
connected = 1;
continue;
}
}
break;
}
out:
if (held)
#ifdef CFS_CD_DEBUG
#endif
#ifdef CFSDEBUG
#endif
return (error);
}
static int
{
int error = 0;
#ifdef CFSDEBUG
#endif
if (getzoneid() != GLOBAL_ZONEID) {
goto out;
}
/*
* Cachefs only provides pass-through support for NFSv4,
* and all vnode operations are passed through to the
* back file system. For NFSv4 pass-through to work, only
* connected operation is supported, the cnode backvp must
* exist, and cachefs optional (eg., disconnectable) flags
* are turned off. Assert these conditions to ensure that
* the backfilesystem is called for the shrlock operation.
*/
if (error == 0) {
("cachefs_shrlock (nfsv4): cp %p, backvp %p",
}
out:
#ifdef CFSDEBUG
#endif
return (error);
}
static int
{
int hit = 0;
int error = 0;
if (error)
goto out;
/* read from the cache if we can */
if (error) {
error = 0;
} else {
hit = 1;
goto out;
}
}
if (error)
goto out;
("cachefs_getsecattr (nfsv4): cp %p, backvp %p",
if (error)
goto out;
(cachefs_vtype_aclok(vp)) &&
if (error) {
error = 0;
}
}
out:
if (error == 0) {
if (hit)
else
}
return (error);
}
static int
/*ARGSUSED*/
{
int hit = 0;
int error = 0;
/* read from the cache if we can */
if (error) {
error = 0;
} else {
hit = 1;
goto out;
}
}
out:
if (error == 0) {
if (hit)
else
}
return (error);
}
/*
* cachefs_cacheacl() -- cache an ACL, which we do by applying it to
* the frontfile if possible; otherwise, the adjunct directory.
*
* inputs:
* cp - the cnode, with its statelock already held
* vsecp - a pointer to a vsecattr_t you'd like us to cache as-is,
* or NULL if you want us to do the VOP_GETSECATTR(backvp).
*
* returns:
* 0 - all is well
* nonzero - errno
*/
int
{
int gotvsec = 0;
int error = 0;
int i;
goto out;
}
if (error != 0)
goto out;
if (error != 0) {
goto out;
}
gotvsec = 1;
sizeof (aclent_t));
/* unless there's real data, we can cache nothing. */
return (0);
}
/*
* prevent the ACL from chmoding our frontfile, and
* snarf the class info
*/
(VSA_ACL | VSA_ACLCNT)) {
for (i = 0; i < vsecp->vsa_aclcnt; i++) {
case CLASS_OBJ:
/*FALLTHROUGH*/
case USER_OBJ:
case GROUP_OBJ:
case OTHER_OBJ:
}
}
}
/*
* if the frontfile exists, then we always do the work. but,
* if there's no frontfile, and the ACL isn't a `real' ACL,
* then we don't want to do the work. otherwise, an `ls -l'
* will create tons of emtpy frontfiles.
*/
<= MIN_ACL_ENTRIES)) {
goto out;
}
/*
* if we have a default ACL, then we need a
* real live directory in the frontfs that we
* can apply the ACL to. if not, then we just
* use the frontfile. we get the frontfile
* regardless -- that way, we know the
* directory for the frontfile exists.
*/
if (vsecp->vsa_dfaclcnt > 0) {
if (error != 0)
goto out;
} else {
if (error != 0)
goto out;
}
if (error != 0) {
#ifdef CFSDEBUG
printf("cachefs_cacheacl: setsecattr: error %d\n",
error);
#endif /* CFSDEBUG */
/*
* If there was an error, we don't want to call
* cachefs_nocache(); so, set error to 0.
* We will call cachefs_purgeacl(), in order to
* clean such things as adjunct ACL directories.
*/
error = 0;
goto out;
}
out:
if (gotvsec) {
if (vsec.vsa_aclcnt)
if (vsec.vsa_dfaclcnt)
}
return (error);
}
void
{
}
}
}
static int
{
int error = 0;
if (error != 0)
goto out;
goto out;
if (error != 0) {
error =
if (error != 0)
goto out;
}
out:
if (error != 0)
return (error);
}
static int
{
int error = 0;
int i;
if (error != 0)
goto out;
if (error != 0)
goto out;
} else {
/*
* if we get here, then we know that MD_ACL is on,
* meaning an ACL was successfully cached. we also
* know that neither MD_ACLDIR nor MD_FILE are on, so
* this has to be an entry without a `real' ACL.
* thus, we forge whatever is necessary.
*/
vsec->vsa_aclentp =
++aclp;
++aclp;
++aclp;
}
}
#ifdef CFSDEBUG
printf("cachefs_getaclfromcache: error %d\n",
error);
#endif /* CFSDEBUG */
goto out;
}
}
for (i = 0; i < vsec->vsa_aclcnt; i++) {
case USER_OBJ:
break;
case GROUP_OBJ:
break;
case OTHER_OBJ:
break;
case CLASS_OBJ:
break;
}
}
}
out:
if (error != 0)
return (error);
}
/*
* Fills in targp with attribute information from srcp, cp
* and if necessary the system.
*/
static void
{
/*
* Add code to fill in the va struct. We use the fields from
* the srcp struct if they are populated, otherwise we guess
*/
else
else
else
now = gethrestime_sec();
else
else
else
else
/*
* the remaing fields are set by the fs and not changable.
* we populate these entries useing the parent directory
* values. It's a small hack, but should work.
*/
}
/*
* set the gid for a newly created file. The algorithm is as follows:
*
* 1) If the gid is set in the attribute list, then use it if
* the caller is privileged, belongs to the target group, or
* the group is the same as the parent directory.
*
* 2) If the parent directory's set-gid bit is clear, then use
* the process gid
*
* 3) Otherwise, use the gid of the parent directory.
*
* Note: newcp->c_attr.va_{mode,type} must already be set before calling
* this routine.
*/
static void
{
secpolicy_vnode_create_gid(cr) != 0)) {
} else {
else
}
/*
* if we're creating a directory, and the parent directory has the
* set-GID bit set, set it on the new directory.
* Otherwise, if the user is neither privileged nor a member of the
* file's new group, clear the file's set-GID bit.
*/
}
/*
* create an acl for the newly created file. should be called right
* after cachefs_creategid.
*/
static void
{
int gotvsec = 0;
int error = 0; /* placeholder */
int i;
return;
/*
* XXX should probably not do VSA_ACL and VSA_ACLCNT, but that
* would hit code paths that isn't hit anywhere else.
*/
if (error != 0)
goto out;
gotvsec = 1;
vsec.vsa_dfaclcnt = 0;
}
/*
* this function should be called pretty much after
* the rest of the file creation stuff is done. so,
* uid, gid, etc. should be `right'. we'll go with
* that, rather than trying to determine whether to
* get stuff from cr or va.
*/
for (i = 0; i < vsec.vsa_aclcnt; i++) {
case DEF_USER_OBJ:
break;
case DEF_GROUP_OBJ:
break;
case DEF_OTHER_OBJ:
break;
case DEF_CLASS_OBJ:
break;
case DEF_USER:
break;
case DEF_GROUP:
break;
}
}
/* XXX is this the POSIX thing to do? */
/*
* we don't need to log this; rather, we clear the
* MD_ACL bit when we reconnect.
*/
if (error != 0)
goto out;
}
out:
if (gotvsec) {
}
}
/*
* this is translated from the UFS code for access checking.
*/
static int
{
int shift = 0;
/*
* Disallow write attempts on read-only
* file systems, unless the file is special.
*/
if (vn_is_readonly(vp)) {
return (EROFS);
}
}
}
/*
* if we need to do ACLs, do it. this works whether anyone
* has explicitly made an ACL or not.
*/
shift += 3;
shift += 3;
}
/* compute missing mode bits */
if (mode == 0)
return (0);
}
/*
* This is transcribed from ufs_acl_access(). If that changes, then
* this should, too.
*
* Check the cnode's ACL's to see if this mode of access is
* allowed; return 0 if allowed, EACCES if not.
*
* We follow the procedure defined in Sec. 3.3.5, ACL Access
* Check Algorithm, of the POSIX 1003.6 Draft Standard.
*/
static int
{
int error = 0;
int mask = ~0;
int ismask = 0;
int gperm = 0;
int ngroup = 0;
int gotvsec = 0;
int i;
/*
* strictly speaking, we shouldn't set VSA_DFACL and DFACLCNT,
* but then i believe we'd be the only thing exercising those
* code paths -- probably a bad thing.
*/
/* XXX KLUDGE! correct insidious 0-class problem */
if (error != 0) {
#ifdef CFSDEBUG
printf("cachefs_acl_access():"
"error %d from getaclfromcache()\n",
error);
#endif /* CFSDEBUG */
goto again;
} else {
goto out;
}
}
} else {
else
}
if (error == 0)
NULL);
if (error != 0) {
#ifdef CFSDEBUG
printf("cachefs_acl_access():"
"error %d from getsecattr(backvp)\n",
error);
#endif /* CFSDEBUG */
goto out;
}
}
gotvsec = 1;
for (i = 0; i < vsec.vsa_aclcnt; i++) {
case USER_OBJ:
/*
* this might look cleaner in the 2nd loop
* below, but we do it here as an
* optimization.
*/
goto out;
}
break;
case CLASS_OBJ:
ismask = 1;
break;
}
}
for (i = 0; i < vsec.vsa_aclcnt; i++) {
case USER:
goto out;
}
break;
case GROUP_OBJ:
++ngroup;
if (! ismask) {
goto out;
}
}
break;
case GROUP:
++ngroup;
}
break;
case OTHER_OBJ:
if (ngroup == 0) {
goto out;
}
break;
default:
break;
}
}
out:
if (gotvsec) {
}
return (error);
}
/*
* see if permissions allow for removal of the given file from
* the given directory.
*/
static int
{
/*
* If the containing directory is sticky, the user must:
* - own the directory, or
* - own the file, or
* - be able to write the file (if it's a plain file), or
* - be sufficiently privileged.
*/
return (secpolicy_vnode_remove(cr));
return (0);
}
/*
* Returns a new name, may even be unique.
* Stolen from nfs code.
* Since now we will use renaming to .cfs* in place of .nfs*
* for CacheFS. Both NFS and CacheFS will rename opened files.
*/
static char cachefs_prefix[] = ".cfs";
static char *
cachefs_newname(void)
{
char *news;
char *s, *p;
if (newnum == 0) {
newnum |= 0x10000;
}
s = news;
p = cachefs_prefix;
while (*p != '\0')
*s++ = *p++;
while (id != 0) {
id >>= 4;
}
*s = '\0';
return (news);
}
/*
* Called to rename the specified file to a temporary file so
* operations to the file after remove work.
* Must call this routine with the dir c_rwlock held as a writer.
*/
static int
/*ARGSUSED*/
{
char *tmpname;
int error;
/* get the new name for the file */
tmpname = cachefs_newname();
/* do the link */
else
if (error) {
return (error);
}
}
/* drop the backvp so NFS does not also do a rename */
return (0);
}
/*
* Marks the cnode as modified.
*/
static void
{
int error;
/* if not on the modify list */
/* put on modified list, also marks the file as modified */
/* if a modified regular file that is not local */
(void) cachefs_getfrontfile(cp);
/* identify file so fsck knows it is modified */
if (error) {
"Cannot change ff mode.\n");
}
}
}
}
}
/*
* Marks the cnode as modified.
* Allocates a rl slot for the cnode if necessary.
* Returns 0 for success, !0 if cannot get an rl slot.
*/
static int
{
int error;
/* get the rl slot if needed */
/* get a metadata slot if we do not have one yet */
}
if (error)
return (error);
}
/* get a free rl entry */
if (error)
return (error);
/* hold the filegrp so the attrcache file is not gc */
if (error) {
return (error);
}
}
return (0);
}
int
{
return (0);
break;
}
static int
{
int error = 0;
/* Assert cachefs compatibility if NFSv4 is in use */
if (cmd == _PC_FILESIZEBITS) {
(*valp) = 0;
while (maxsize != 0) {
maxsize >>= 1;
(*valp)++;
}
(*valp)++;
} else
return (error);
}