static void BStore(struct brequest *ab) { struct vcache *tvc; afs_int32 code; struct vrequest *treq = NULL; #if defined(AFS_SGI_ENV) struct cred *tmpcred; #endif AFS_STATCNT(BStore); if ((code = afs_CreateReq(&treq, ab->cred))) return; tvc = ab->vc; #if defined(AFS_SGI_ENV) /* * Since StoreOnLastReference can end up calling osi_SyncVM which * calls into VM code that assumes that u.u_cred has the * correct credentials, we set our to theirs for this xaction */ tmpcred = OSI_GET_CURRENT_CRED(); OSI_SET_CURRENT_CRED(ab->cred); /* * To avoid recursion since the WriteLock may be released during VM * operations, we hold the VOP_RWLOCK across this transaction as * do the other callers of StoreOnLastReference */ AFS_RWLOCK((vnode_t *) tvc, 1); #endif ObtainWriteLock(&tvc->lock, 209); code = afs_StoreOnLastReference(tvc, treq); ReleaseWriteLock(&tvc->lock); #if defined(AFS_SGI_ENV) OSI_SET_CURRENT_CRED(tmpcred); AFS_RWUNLOCK((vnode_t *) tvc, 1); #endif /* now set final return code, and wakeup anyone waiting */ if ((ab->flags & BUVALID) == 0) { /* To explain code_raw/code_checkcode: * Anyone that's waiting won't have our treq, so they won't be able to * call afs_CheckCode themselves on the return code we provide here. * But if we give back only the afs_CheckCode value, they won't know * what the "raw" value was. So give back both values, so the waiter * can know the "raw" value for interpreting the value internally, as * well as the afs_CheckCode value to give to the OS. */ ab->code_raw = code; ab->code_checkcode = afs_CheckCode(code, treq, 430); ab->flags |= BUVALID; if (ab->flags & BUWAIT) { ab->flags &= ~BUWAIT; afs_osi_Wakeup(ab); } } afs_DestroyReq(treq); }
/* ptr_parm 0 is the pathname, size_parm 0 to the fetch is the chunk number */ static void BPath(struct brequest *ab) { struct dcache *tdc = NULL; struct vcache *tvc = NULL; struct vnode *tvn = NULL; #ifdef AFS_LINUX22_ENV struct dentry *dp = NULL; #endif afs_size_t offset, len; struct vrequest *treq = NULL; afs_int32 code; AFS_STATCNT(BPath); if ((code = afs_CreateReq(&treq, ab->cred))) { return; } AFS_GUNLOCK(); #ifdef AFS_LINUX22_ENV code = gop_lookupname((char *)ab->ptr_parm[0], AFS_UIOSYS, 1, &dp); if (dp) tvn = (struct vnode *)dp->d_inode; #else code = gop_lookupname((char *)ab->ptr_parm[0], AFS_UIOSYS, 1, &tvn); #endif AFS_GLOCK(); osi_FreeLargeSpace((char *)ab->ptr_parm[0]); /* free path name buffer here */ if (code) { afs_DestroyReq(treq); return; } /* now path may not have been in afs, so check that before calling our cache manager */ if (!tvn || !IsAfsVnode(tvn)) { /* release it and give up */ if (tvn) { #ifdef AFS_LINUX22_ENV dput(dp); #else AFS_RELE(tvn); #endif } afs_DestroyReq(treq); return; } tvc = VTOAFS(tvn); /* here we know its an afs vnode, so we can get the data for the chunk */ tdc = afs_GetDCache(tvc, ab->size_parm[0], treq, &offset, &len, 1); if (tdc) { afs_PutDCache(tdc); } #ifdef AFS_LINUX22_ENV dput(dp); #else AFS_RELE(tvn); #endif afs_DestroyReq(treq); }
/* afs_root - stat the root of the file system. AFS global held on entry. */ static int afs_root(struct super_block *afsp) { afs_int32 code = 0; struct vcache *tvp = 0; AFS_STATCNT(afs_root); if (afs_globalVp && (afs_globalVp->f.states & CStatd)) { tvp = afs_globalVp; } else { struct vrequest *treq = NULL; cred_t *credp = crref(); if (afs_globalVp) { afs_PutVCache(afs_globalVp); afs_globalVp = NULL; } if (!(code = afs_CreateReq(&treq, credp)) && !(code = afs_CheckInit())) { tvp = afs_GetVCache(&afs_rootFid, treq, NULL, NULL); if (tvp) { struct inode *ip = AFSTOV(tvp); struct vattr *vattr = NULL; code = afs_CreateAttr(&vattr); if (!code) { afs_getattr(tvp, vattr, credp); afs_fill_inode(ip, vattr); /* setup super_block and mount point inode. */ afs_globalVp = tvp; #if defined(HAVE_LINUX_D_MAKE_ROOT) afsp->s_root = d_make_root(ip); #else afsp->s_root = d_alloc_root(ip); #endif #if !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP) afsp->s_root->d_op = &afs_dentry_operations; #endif afs_DestroyAttr(vattr); } } else code = EIO; } crfree(credp); afs_DestroyReq(treq); } afs_Trace2(afs_iclSetp, CM_TRACE_VFSROOT, ICL_TYPE_POINTER, afs_globalVp, ICL_TYPE_INT32, code); return code; }
static void BPrefetchNoCache(struct brequest *ab) { struct vrequest *treq = NULL; int code; if ((code = afs_CreateReq(&treq, ab->cred))) return; #ifndef UKERNEL /* OS-specific prefetch routine */ afs_PrefetchNoCache(ab->vc, ab->cred, (struct nocache_read_request *) ab->ptr_parm[0]); #endif afs_DestroyReq(treq); }
int afs_readlink(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred) { afs_int32 code; struct vrequest *treq = NULL; char *tp; struct afs_fakestat_state fakestat; OSI_VC_CONVERT(avc); AFS_STATCNT(afs_readlink); afs_Trace1(afs_iclSetp, CM_TRACE_READLINK, ICL_TYPE_POINTER, avc); if ((code = afs_CreateReq(&treq, acred))) return code; afs_InitFakeStat(&fakestat); AFS_DISCON_LOCK(); code = afs_EvalFakeStat(&avc, &fakestat, treq); if (code) goto done; code = afs_VerifyVCache(avc, treq); if (code) goto done; if (vType(avc) != VLNK) { code = EINVAL; goto done; } ObtainWriteLock(&avc->lock, 158); code = afs_HandleLink(avc, treq); /* finally uiomove it to user-land */ if (code == 0) { tp = avc->linkData; if (tp) AFS_UIOMOVE(tp, strlen(tp), UIO_READ, auio, code); else { code = EIO; } } ReleaseWriteLock(&avc->lock); done: afs_PutFakeStat(&fakestat); AFS_DISCON_UNLOCK(); code = afs_CheckCode(code, treq, 32); afs_DestroyReq(treq); return code; }
/* size_parm 0 to the fetch is the chunk number, * ptr_parm 0 is the dcache entry to wakeup, * size_parm 1 is true iff we should release the dcache entry here. */ static void BPrefetch(struct brequest *ab) { struct dcache *tdc; struct vcache *tvc; afs_size_t offset, len, abyte, totallen = 0; struct vrequest *treq = NULL; int code; AFS_STATCNT(BPrefetch); if ((code = afs_CreateReq(&treq, ab->cred))) return; abyte = ab->size_parm[0]; tvc = ab->vc; do { tdc = afs_GetDCache(tvc, abyte, treq, &offset, &len, 1); if (tdc) { afs_PutDCache(tdc); } abyte+=len; totallen += len; } while ((totallen < afs_preCache) && tdc && (len > 0)); /* now, dude may be waiting for us to clear DFFetchReq bit; do so. Can't * use tdc from GetDCache since afs_GetDCache may fail, but someone may * be waiting for our wakeup anyway. */ tdc = (struct dcache *)(ab->ptr_parm[0]); ObtainSharedLock(&tdc->lock, 640); if (tdc->mflags & DFFetchReq) { UpgradeSToWLock(&tdc->lock, 641); tdc->mflags &= ~DFFetchReq; ReleaseWriteLock(&tdc->lock); } else { ReleaseSharedLock(&tdc->lock); } afs_osi_Wakeup(&tdc->validPos); if (ab->size_parm[1]) { afs_PutDCache(tdc); /* put this one back, too */ } afs_DestroyReq(treq); }
static void BPartialStore(struct brequest *ab) { struct vcache *tvc; afs_int32 code; struct vrequest *treq = NULL; int locked, shared_locked = 0; AFS_STATCNT(BStore); if ((code = afs_CreateReq(&treq, ab->cred))) return; tvc = ab->vc; locked = tvc->lock.excl_locked? 1:0; if (!locked) ObtainWriteLock(&tvc->lock, 1209); else if (!(tvc->lock.excl_locked & WRITE_LOCK)) { shared_locked = 1; ConvertSToRLock(&tvc->lock); } code = afs_StoreAllSegments(tvc, treq, AFS_ASYNC); if (!locked) ReleaseWriteLock(&tvc->lock); else if (shared_locked) ConvertSToRLock(&tvc->lock); /* now set final return code, and wakeup anyone waiting */ if ((ab->flags & BUVALID) == 0) { /* set final code, since treq doesn't go across processes */ ab->code_raw = code; ab->code_checkcode = afs_CheckCode(code, treq, 43); ab->flags |= BUVALID; if (ab->flags & BUWAIT) { ab->flags &= ~BUWAIT; afs_osi_Wakeup(ab); } } afs_DestroyReq(treq); }
/* * Given a FID, obtain or construct a dentry, or return an error. * This should be called with the BKL and AFS_GLOCK held. */ static struct dentry *get_dentry_from_fid(cred_t *credp, struct VenusFid *afid) { struct vrequest *treq = NULL; struct vcache *vcp; struct vattr *vattr = NULL; struct inode *ip; struct dentry *dp; afs_int32 code; code = afs_CreateAttr(&vattr); if (code) { return ERR_PTR(-afs_CheckCode(code, NULL, 104)); } code = afs_CreateReq(&treq, credp); if (code) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): afs_CreateReq: %d\n", afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique, code); #endif afs_DestroyAttr(vattr); return ERR_PTR(-afs_CheckCode(code, NULL, 101)); } vcp = afs_GetVCache(afid, treq, NULL, NULL); if (vcp == NULL) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): no vcache\n", afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique); #endif afs_DestroyReq(treq); afs_DestroyAttr(vattr); return NULL; } /* * Now, it might be that we just caused a directory vnode to * spring into existence, in which case its parent FID is unset. * We need to do something about that, but only because we care * in our own get_parent(), below -- the common code never looks * at parentVnode on directories, except for VIOCGETVCXSTATUS. * So, if this fails, we don't really care very much. */ if (vType(vcp) == VDIR && vcp->mvstat != AFS_MVSTAT_ROOT && !vcp->f.parent.vnode) update_dir_parent(treq, vcp); /* * If this is a volume root directory and fakestat is enabled, * we might need to replace the directory by a mount point. */ code = UnEvalFakeStat(treq, &vcp); if (code) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): UnEvalFakeStat: %d\n", afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique, code); #endif afs_PutVCache(vcp); code = afs_CheckCode(code, treq, 103); afs_DestroyReq(treq); afs_DestroyAttr(vattr); return ERR_PTR(-code); } ip = AFSTOV(vcp); afs_getattr(vcp, vattr, credp); afs_fill_inode(ip, vattr); /* d_alloc_anon might block, so we shouldn't hold the glock */ AFS_GUNLOCK(); dp = d_alloc_anon(ip); AFS_GLOCK(); if (!dp) { iput(ip); #ifdef OSI_EXPORT_DEBUG printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): out of memory\n", afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique); #endif afs_DestroyReq(treq); afs_DestroyAttr(vattr); return ERR_PTR(-ENOMEM); } dp->d_op = &afs_dentry_operations; afs_DestroyReq(treq); afs_DestroyAttr(vattr); return dp; }
int afs_MemRead(struct vcache *avc, struct uio *auio, afs_ucred_t *acred, daddr_t albn, struct buf **abpp, int noLock) { afs_size_t totalLength; afs_size_t transferLength; afs_size_t filePos; afs_size_t offset, tlen; afs_size_t len = 0; afs_int32 trimlen; struct dcache *tdc = 0; afs_int32 error, trybusy = 1; afs_int32 code; struct vrequest *treq = NULL; #ifdef AFS_DARWIN80_ENV uio_t tuiop = NULL; #else struct uio tuio; struct uio *tuiop = &tuio; struct iovec *tvec; memset(&tuio, 0, sizeof(tuio)); #endif AFS_STATCNT(afs_MemRead); if (avc->vc_error) return EIO; /* check that we have the latest status info in the vnode cache */ if ((code = afs_CreateReq(&treq, acred))) return code; if (!noLock) { code = afs_VerifyVCache(avc, treq); if (code) { code = afs_CheckCode(code, treq, 8); /* failed to get it */ afs_DestroyReq(treq); return code; } } #ifndef AFS_VM_RDWR_ENV if (AFS_NFSXLATORREQ(acred)) { if (!afs_AccessOK (avc, PRSFS_READ, treq, CHECK_MODE_BITS | CMB_ALLOW_EXEC_AS_READ)) { code = afs_CheckCode(EACCES, treq, 9); afs_DestroyReq(treq); return code; } } #endif #ifndef AFS_DARWIN80_ENV tvec = (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec)); memset(tvec, 0, sizeof(struct iovec)); #endif totalLength = AFS_UIO_RESID(auio); filePos = AFS_UIO_OFFSET(auio); afs_Trace4(afs_iclSetp, CM_TRACE_READ, ICL_TYPE_POINTER, avc, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(filePos), ICL_TYPE_INT32, totalLength, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length)); error = 0; transferLength = 0; if (!noLock) ObtainReadLock(&avc->lock); #if defined(AFS_TEXT_ENV) && !defined(AFS_VM_RDWR_ENV) if (avc->flushDV.high == AFS_MAXDV && avc->flushDV.low == AFS_MAXDV) { hset(avc->flushDV, avc->f.m.DataVersion); } #endif /* * Locks held: * avc->lock(R) */ /* This bit is bogus. We're checking to see if the read goes past the * end of the file. If so, we should be zeroing out all of the buffers * that the client has passed into us (there is a danger that we may leak * kernel memory if we do not). However, this behaviour is disabled by * not setting len before this segment runs, and by setting len to 0 * immediately we enter it. In addition, we also need to check for a read * which partially goes off the end of the file in the while loop below. */ if (filePos >= avc->f.m.Length) { if (len > AFS_ZEROS) len = sizeof(afs_zeros); /* and in 0 buffer */ len = 0; #ifdef AFS_DARWIN80_ENV trimlen = len; tuiop = afsio_darwin_partialcopy(auio, trimlen); #else afsio_copy(auio, &tuio, tvec); trimlen = len; afsio_trim(&tuio, trimlen); #endif AFS_UIOMOVE(afs_zeros, trimlen, UIO_READ, tuiop, code); } while (avc->f.m.Length > 0 && totalLength > 0) { /* read all of the cached info */ if (filePos >= avc->f.m.Length) break; /* all done */ if (noLock) { if (tdc) { ReleaseReadLock(&tdc->lock); afs_PutDCache(tdc); } tdc = afs_FindDCache(avc, filePos); if (tdc) { ObtainReadLock(&tdc->lock); offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk); len = tdc->f.chunkBytes - offset; } } else { int versionOk; /* a tricky question: does the presence of the DFFetching flag * mean that we're fetching the latest version of the file? No. * The server could update the file as soon as the fetch responsible * for the setting of the DFFetching flag completes. * * However, the presence of the DFFetching flag (visible under * a dcache read lock since it is set and cleared only under a * dcache write lock) means that we're fetching as good a version * as was known to this client at the time of the last call to * afs_VerifyVCache, since the latter updates the stat cache's * m.DataVersion field under a vcache write lock, and from the * time that the DFFetching flag goes on in afs_GetDCache (before * the fetch starts), to the time it goes off (after the fetch * completes), afs_GetDCache keeps at least a read lock on the * vcache entry. * * This means that if the DFFetching flag is set, we can use that * data for any reads that must come from the current version of * the file (current == m.DataVersion). * * Another way of looking at this same point is this: if we're * fetching some data and then try do an afs_VerifyVCache, the * VerifyVCache operation will not complete until after the * DFFetching flag is turned off and the dcache entry's f.versionNo * field is updated. * * Note, by the way, that if DFFetching is set, * m.DataVersion > f.versionNo (the latter is not updated until * after the fetch completes). */ if (tdc) { ReleaseReadLock(&tdc->lock); afs_PutDCache(tdc); /* before reusing tdc */ } #ifdef STRUCT_TASK_STRUCT_HAS_CRED try_background: #endif tdc = afs_GetDCache(avc, filePos, treq, &offset, &len, 2); ObtainReadLock(&tdc->lock); /* now, first try to start transfer, if we'll need the data. If * data already coming, we don't need to do this, obviously. Type * 2 requests never return a null dcache entry, btw. */ if (!(tdc->dflags & DFFetching) && !hsame(avc->f.m.DataVersion, tdc->f.versionNo)) { /* have cache entry, it is not coming in now, * and we'll need new data */ tagain: #ifdef STRUCT_TASK_STRUCT_HAS_CRED if (trybusy && (!afs_BBusy() || (afs_protocols & VICEP_ACCESS))) { #else if (trybusy && !afs_BBusy()) { #endif struct brequest *bp; /* daemon is not busy */ ObtainSharedLock(&tdc->mflock, 665); if (!(tdc->mflags & DFFetchReq)) { int dontwait = B_DONTWAIT; /* start the daemon (may already be running, however) */ UpgradeSToWLock(&tdc->mflock, 666); tdc->mflags |= DFFetchReq; #ifdef STRUCT_TASK_STRUCT_HAS_CRED if (afs_protocols & VICEP_ACCESS) dontwait = 0; #endif bp = afs_BQueue(BOP_FETCH, avc, dontwait, 0, acred, (afs_size_t) filePos, (afs_size_t) 0, tdc, (void *)0, (void *)0); if (!bp) { tdc->mflags &= ~DFFetchReq; trybusy = 0; /* Avoid bkg daemon since they're too busy */ ReleaseWriteLock(&tdc->mflock); goto tagain; } ConvertWToSLock(&tdc->mflock); /* don't use bp pointer! */ } code = 0; ConvertSToRLock(&tdc->mflock); while (!code && tdc->mflags & DFFetchReq) { afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT, ICL_TYPE_STRING, __FILE__, ICL_TYPE_INT32, __LINE__, ICL_TYPE_POINTER, tdc, ICL_TYPE_INT32, tdc->dflags); /* don't need waiting flag on this one */ ReleaseReadLock(&tdc->mflock); ReleaseReadLock(&tdc->lock); ReleaseReadLock(&avc->lock); code = afs_osi_SleepSig(&tdc->validPos); ObtainReadLock(&avc->lock); ObtainReadLock(&tdc->lock); ObtainReadLock(&tdc->mflock); } ReleaseReadLock(&tdc->mflock); if (code) { error = code; break; } } } /* now data may have started flowing in (if DFFetching is on). If * data is now streaming in, then wait for some interesting stuff. */ code = 0; while (!code && (tdc->dflags & DFFetching) && tdc->validPos <= filePos) { /* too early: wait for DFFetching flag to vanish, * or data to appear */ afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT, ICL_TYPE_STRING, __FILE__, ICL_TYPE_INT32, __LINE__, ICL_TYPE_POINTER, tdc, ICL_TYPE_INT32, tdc->dflags); ReleaseReadLock(&tdc->lock); ReleaseReadLock(&avc->lock); code = afs_osi_SleepSig(&tdc->validPos); ObtainReadLock(&avc->lock); ObtainReadLock(&tdc->lock); } if (code) { error = code; break; } /* fetching flag gone, data is here, or we never tried * (BBusy for instance) */ len = tdc->validPos - filePos; versionOk = hsame(avc->f.m.DataVersion, tdc->f.versionNo) ? 1 : 0; if (tdc->dflags & DFFetching) { /* still fetching, some new data is here: * compute length and offset */ offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk); } else { /* no longer fetching, verify data version * (avoid new GetDCache call) */ if (versionOk && len > 0) { offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk); } else { /* don't have current data, so get it below */ afs_Trace3(afs_iclSetp, CM_TRACE_VERSIONNO, ICL_TYPE_INT64, ICL_HANDLE_OFFSET(filePos), ICL_TYPE_HYPER, &avc->f.m.DataVersion, ICL_TYPE_HYPER, &tdc->f.versionNo); #if 0 #ifdef STRUCT_TASK_STRUCT_HAS_CRED if (afs_protocols & VICEP_ACCESS) { printf("afs_read: DV mismatch? %d instead of %d for %u.%u.%u\n", tdc->f.versionNo.low, avc->f.m.DataVersion.low, avc->f.fid.Fid.Volume, avc->f.fid.Fid.Vnode, avc->f.fid.Fid.Unique); printf("afs_read: validPos %llu filePos %llu totalLength %lld m.Length %llu noLock %d\n", tdc->validPos, filePos, totalLength, avc->f.m.Length, noLock); printf("afs_read: or len too low? %lld for %u.%u.%u\n", len, avc->f.fid.Fid.Volume, avc->f.fid.Fid.Vnode, avc->f.fid.Fid.Unique); } #endif #endif ReleaseReadLock(&tdc->lock); afs_PutDCache(tdc); tdc = NULL; } } if (!tdc) { #ifdef STRUCT_TASK_STRUCT_HAS_CRED if (afs_protocols & VICEP_ACCESS) { /* avoid foreground fetch */ if (!versionOk) { printf("afs_read: avoid forground %u.%u.%u\n", avc->f.fid.Fid.Volume, avc->f.fid.Fid.Vnode, avc->f.fid.Fid.Unique); goto try_background; } #if 0 printf("afs_read: forground %u.%u.%u\n", avc->f.fid.Fid.Volume, avc->f.fid.Fid.Vnode, avc->f.fid.Fid.Unique); #endif } #endif /* If we get here, it was not possible to start the * background daemon. With flag == 1 afs_GetDCache * does the FetchData rpc synchronously. */ ReleaseReadLock(&avc->lock); tdc = afs_GetDCache(avc, filePos, treq, &offset, &len, 1); ObtainReadLock(&avc->lock); if (tdc) ObtainReadLock(&tdc->lock); } } afs_Trace3(afs_iclSetp, CM_TRACE_VNODEREAD, ICL_TYPE_POINTER, tdc, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(offset), ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(len)); if (!tdc) { error = EIO; break; } /* * Locks held: * avc->lock(R) * tdc->lock(R) */ if (len > totalLength) len = totalLength; /* will read len bytes */ if (len <= 0) { /* shouldn't get here if DFFetching is on */ /* read past the end of a chunk, may not be at next chunk yet, and yet * also not at eof, so may have to supply fake zeros */ len = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset; /* bytes left in chunk addr space */ if (len > totalLength) len = totalLength; /* and still within xfr request */ tlen = avc->f.m.Length - offset; /* and still within file */ if (len > tlen) len = tlen; if (len > AFS_ZEROS) len = sizeof(afs_zeros); /* and in 0 buffer */ #ifdef AFS_DARWIN80_ENV trimlen = len; tuiop = afsio_darwin_partialcopy(auio, trimlen); #else afsio_copy(auio, &tuio, tvec); trimlen = len; afsio_trim(&tuio, trimlen); #endif AFS_UIOMOVE(afs_zeros, trimlen, UIO_READ, tuiop, code); if (code) { error = code; break; } } else { /* get the data from the mem cache */ /* mung uio structure to be right for this transfer */ #ifdef AFS_DARWIN80_ENV trimlen = len; tuiop = afsio_darwin_partialcopy(auio, trimlen); uio_setoffset(tuiop, offset); #else afsio_copy(auio, &tuio, tvec); trimlen = len; afsio_trim(&tuio, trimlen); tuio.afsio_offset = offset; #endif code = afs_MemReadUIO(&tdc->f.inode, tuiop); if (code) { error = code; break; } } /* otherwise we've read some, fixup length, etc and continue with next seg */ len = len - AFS_UIO_RESID(tuiop); /* compute amount really transferred */ trimlen = len; afsio_skip(auio, trimlen); /* update input uio structure */ totalLength -= len; transferLength += len; filePos += len; if (len <= 0) break; /* surprise eof */ #ifdef AFS_DARWIN80_ENV if (tuiop) { uio_free(tuiop); tuiop = 0; } #endif } /* the whole while loop */ /* * Locks held: * avc->lock(R) * tdc->lock(R) if tdc */ /* if we make it here with tdc non-zero, then it is the last chunk we * dealt with, and we have to release it when we're done. We hold on * to it in case we need to do a prefetch. */ if (tdc) { ReleaseReadLock(&tdc->lock); /* * try to queue prefetch, if needed. If DataVersion is zero there * should not be any more: files with DV 0 never have been stored * on the fileserver, symbolic links and directories never require * more than a single chunk. */ if (!noLock && !(hiszero(avc->f.m.DataVersion)) && #ifndef AFS_VM_RDWR_ENV afs_preCache #else 1 #endif ) { afs_PrefetchChunk(avc, tdc, acred, treq); } afs_PutDCache(tdc); } if (!noLock) ReleaseReadLock(&avc->lock); #ifdef AFS_DARWIN80_ENV if (tuiop) uio_free(tuiop); #else osi_FreeSmallSpace(tvec); #endif error = afs_CheckCode(error, treq, 10); afs_DestroyReq(treq); return error; } /* called with the dcache entry triggering the fetch, the vcache entry involved, * and a vrequest for the read call. Marks the dcache entry as having already * triggered a prefetch, starts the prefetch going and sets the DFFetchReq * flag in the prefetched block, so that the next call to read knows to wait * for the daemon to start doing things. * * This function must be called with the vnode at least read-locked, and * no locks on the dcache, because it plays around with dcache entries. */ void afs_PrefetchChunk(struct vcache *avc, struct dcache *adc, afs_ucred_t *acred, struct vrequest *areq) { struct dcache *tdc; afs_size_t offset; afs_size_t j1, j2; /* junk vbls for GetDCache to trash */ offset = adc->f.chunk + 1; /* next chunk we'll need */ offset = AFS_CHUNKTOBASE(offset); /* base of next chunk */ ObtainReadLock(&adc->lock); ObtainSharedLock(&adc->mflock, 662); if (offset < avc->f.m.Length && !(adc->mflags & DFNextStarted) && !afs_BBusy()) { struct brequest *bp; UpgradeSToWLock(&adc->mflock, 663); adc->mflags |= DFNextStarted; /* we've tried to prefetch for this guy */ ReleaseWriteLock(&adc->mflock); ReleaseReadLock(&adc->lock); tdc = afs_GetDCache(avc, offset, areq, &j1, &j2, 2); /* type 2 never returns 0 */ /* * In disconnected mode, type 2 can return 0 because it doesn't * make any sense to allocate a dcache we can never fill */ if (tdc == NULL) return; ObtainSharedLock(&tdc->mflock, 651); if (!(tdc->mflags & DFFetchReq)) { /* ask the daemon to do the work */ UpgradeSToWLock(&tdc->mflock, 652); tdc->mflags |= DFFetchReq; /* guaranteed to be cleared by BKG or GetDCache */ /* last parm (1) tells bkg daemon to do an afs_PutDCache when it is done, * since we don't want to wait for it to finish before doing so ourselves. */ bp = afs_BQueue(BOP_FETCH, avc, B_DONTWAIT, 0, acred, (afs_size_t) offset, (afs_size_t) 1, tdc, (void *)0, (void *)0); if (!bp) { /* Bkg table full; just abort non-important prefetching to avoid deadlocks */ tdc->mflags &= ~DFFetchReq; ReleaseWriteLock(&tdc->mflock); afs_PutDCache(tdc); /* * DCLOCKXXX: This is a little sketchy, since someone else * could have already started a prefetch.. In practice, * this probably doesn't matter; at most it would cause an * extra slot in the BKG table to be used up when someone * prefetches this for the second time. */ ObtainReadLock(&adc->lock); ObtainWriteLock(&adc->mflock, 664); adc->mflags &= ~DFNextStarted; ReleaseWriteLock(&adc->mflock); ReleaseReadLock(&adc->lock); } else { ReleaseWriteLock(&tdc->mflock); } } else { ReleaseSharedLock(&tdc->mflock); afs_PutDCache(tdc); } } else { ReleaseSharedLock(&adc->mflock); ReleaseReadLock(&adc->lock); } } int afs_UFSRead(struct vcache *avc, struct uio *auio, afs_ucred_t *acred, daddr_t albn, struct buf **abpp, int noLock) { afs_size_t totalLength; afs_size_t transferLength; afs_size_t filePos; afs_size_t offset, tlen; afs_size_t len = 0; afs_int32 trimlen; struct dcache *tdc = 0; afs_int32 error; struct osi_file *tfile; afs_int32 code; int trybusy = 1; struct vrequest *treq = NULL; #ifdef AFS_DARWIN80_ENV uio_t tuiop=NULL; #else struct uio tuio; struct uio *tuiop = &tuio; struct iovec *tvec; memset(&tuio, 0, sizeof(tuio)); #endif AFS_STATCNT(afs_UFSRead); if (avc && avc->vc_error) return EIO; AFS_DISCON_LOCK(); /* check that we have the latest status info in the vnode cache */ if ((code = afs_CreateReq(&treq, acred))) return code; if (!noLock) { if (!avc) osi_Panic("null avc in afs_UFSRead"); else { code = afs_VerifyVCache(avc, treq); if (code) { code = afs_CheckCode(code, treq, 11); /* failed to get it */ afs_DestroyReq(treq); AFS_DISCON_UNLOCK(); return code; } } } #ifndef AFS_VM_RDWR_ENV if (AFS_NFSXLATORREQ(acred)) { if (!afs_AccessOK (avc, PRSFS_READ, treq, CHECK_MODE_BITS | CMB_ALLOW_EXEC_AS_READ)) { AFS_DISCON_UNLOCK(); code = afs_CheckCode(EACCES, treq, 12); afs_DestroyReq(treq); return code; } } #endif #ifndef AFS_DARWIN80_ENV tvec = (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec)); memset(tvec, 0, sizeof(struct iovec)); #endif totalLength = AFS_UIO_RESID(auio); filePos = AFS_UIO_OFFSET(auio); afs_Trace4(afs_iclSetp, CM_TRACE_READ, ICL_TYPE_POINTER, avc, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(filePos), ICL_TYPE_INT32, totalLength, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length)); error = 0; transferLength = 0; if (!noLock) ObtainReadLock(&avc->lock); #if defined(AFS_TEXT_ENV) && !defined(AFS_VM_RDWR_ENV) if (avc->flushDV.high == AFS_MAXDV && avc->flushDV.low == AFS_MAXDV) { hset(avc->flushDV, avc->f.m.DataVersion); } #endif /* This bit is bogus. We're checking to see if the read goes past the * end of the file. If so, we should be zeroing out all of the buffers * that the client has passed into us (there is a danger that we may leak * kernel memory if we do not). However, this behaviour is disabled by * not setting len before this segment runs, and by setting len to 0 * immediately we enter it. In addition, we also need to check for a read * which partially goes off the end of the file in the while loop below. */ if (filePos >= avc->f.m.Length) { if (len > AFS_ZEROS) len = sizeof(afs_zeros); /* and in 0 buffer */ len = 0; #ifdef AFS_DARWIN80_ENV trimlen = len; tuiop = afsio_darwin_partialcopy(auio, trimlen); #else afsio_copy(auio, &tuio, tvec); trimlen = len; afsio_trim(&tuio, trimlen); #endif AFS_UIOMOVE(afs_zeros, trimlen, UIO_READ, tuiop, code); } while (avc->f.m.Length > 0 && totalLength > 0) { /* read all of the cached info */ if (filePos >= avc->f.m.Length) break; /* all done */ if (noLock) { if (tdc) { ReleaseReadLock(&tdc->lock); afs_PutDCache(tdc); } tdc = afs_FindDCache(avc, filePos); if (tdc) { ObtainReadLock(&tdc->lock); offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk); len = tdc->validPos - filePos; } } else { int versionOk; /* a tricky question: does the presence of the DFFetching flag * mean that we're fetching the latest version of the file? No. * The server could update the file as soon as the fetch responsible * for the setting of the DFFetching flag completes. * * However, the presence of the DFFetching flag (visible under * a dcache read lock since it is set and cleared only under a * dcache write lock) means that we're fetching as good a version * as was known to this client at the time of the last call to * afs_VerifyVCache, since the latter updates the stat cache's * m.DataVersion field under a vcache write lock, and from the * time that the DFFetching flag goes on in afs_GetDCache (before * the fetch starts), to the time it goes off (after the fetch * completes), afs_GetDCache keeps at least a read lock on the * vcache entry. * * This means that if the DFFetching flag is set, we can use that * data for any reads that must come from the current version of * the file (current == m.DataVersion). * * Another way of looking at this same point is this: if we're * fetching some data and then try do an afs_VerifyVCache, the * VerifyVCache operation will not complete until after the * DFFetching flag is turned off and the dcache entry's f.versionNo * field is updated. * * Note, by the way, that if DFFetching is set, * m.DataVersion > f.versionNo (the latter is not updated until * after the fetch completes). */ if (tdc) { ReleaseReadLock(&tdc->lock); afs_PutDCache(tdc); /* before reusing tdc */ } #ifdef STRUCT_TASK_STRUCT_HAS_CRED try_background: #endif tdc = afs_GetDCache(avc, filePos, treq, &offset, &len, 2); if (!tdc) { error = ENETDOWN; break; } ObtainReadLock(&tdc->lock); /* now, first try to start transfer, if we'll need the data. If * data already coming, we don't need to do this, obviously. Type * 2 requests never return a null dcache entry, btw. */ if (!(tdc->dflags & DFFetching) && !hsame(avc->f.m.DataVersion, tdc->f.versionNo)) { /* have cache entry, it is not coming in now, and we'll need new data */ tagain: #ifdef STRUCT_TASK_STRUCT_HAS_CRED if (trybusy && (!afs_BBusy() || (afs_protocols & VICEP_ACCESS))) { #else if (trybusy && !afs_BBusy()) { #endif struct brequest *bp; /* daemon is not busy */ ObtainSharedLock(&tdc->mflock, 667); if (!(tdc->mflags & DFFetchReq)) { int dontwait = B_DONTWAIT; UpgradeSToWLock(&tdc->mflock, 668); tdc->mflags |= DFFetchReq; #ifdef STRUCT_TASK_STRUCT_HAS_CRED if (afs_protocols & VICEP_ACCESS) dontwait = 0; #endif bp = afs_BQueue(BOP_FETCH, avc, dontwait, 0, acred, (afs_size_t) filePos, (afs_size_t) 0, tdc, (void *)0, (void *)0); if (!bp) { /* Bkg table full; retry deadlocks */ tdc->mflags &= ~DFFetchReq; trybusy = 0; /* Avoid bkg daemon since they're too busy */ ReleaseWriteLock(&tdc->mflock); goto tagain; } ConvertWToSLock(&tdc->mflock); } code = 0; ConvertSToRLock(&tdc->mflock); while (!code && tdc->mflags & DFFetchReq) { afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT, ICL_TYPE_STRING, __FILE__, ICL_TYPE_INT32, __LINE__, ICL_TYPE_POINTER, tdc, ICL_TYPE_INT32, tdc->dflags); /* don't need waiting flag on this one */ ReleaseReadLock(&tdc->mflock); ReleaseReadLock(&tdc->lock); ReleaseReadLock(&avc->lock); code = afs_osi_SleepSig(&tdc->validPos); ObtainReadLock(&avc->lock); ObtainReadLock(&tdc->lock); ObtainReadLock(&tdc->mflock); } ReleaseReadLock(&tdc->mflock); if (code) { error = code; break; } } } /* now data may have started flowing in (if DFFetching is on). If * data is now streaming in, then wait for some interesting stuff. */ code = 0; while (!code && (tdc->dflags & DFFetching) && tdc->validPos <= filePos) { /* too early: wait for DFFetching flag to vanish, * or data to appear */ afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT, ICL_TYPE_STRING, __FILE__, ICL_TYPE_INT32, __LINE__, ICL_TYPE_POINTER, tdc, ICL_TYPE_INT32, tdc->dflags); ReleaseReadLock(&tdc->lock); ReleaseReadLock(&avc->lock); code = afs_osi_SleepSig(&tdc->validPos); ObtainReadLock(&avc->lock); ObtainReadLock(&tdc->lock); } if (code) { error = code; break; } /* fetching flag gone, data is here, or we never tried * (BBusy for instance) */ len = tdc->validPos - filePos; versionOk = hsame(avc->f.m.DataVersion, tdc->f.versionNo) ? 1 : 0; if (tdc->dflags & DFFetching) { /* still fetching, some new data is here: * compute length and offset */ offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk); } else { /* no longer fetching, verify data version (avoid new * GetDCache call) */ if (versionOk && len > 0) { offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk); } else { /* don't have current data, so get it below */ afs_Trace3(afs_iclSetp, CM_TRACE_VERSIONNO, ICL_TYPE_INT64, ICL_HANDLE_OFFSET(filePos), ICL_TYPE_HYPER, &avc->f.m.DataVersion, ICL_TYPE_HYPER, &tdc->f.versionNo); #if 0 #ifdef STRUCT_TASK_STRUCT_HAS_CRED if (afs_protocols & VICEP_ACCESS) { printf("afs_read: DV mismatch? %d instead of %d for %u.%u.%u\n", tdc->f.versionNo.low, avc->f.m.DataVersion.low, avc->f.fid.Fid.Volume, avc->f.fid.Fid.Vnode, avc->f.fid.Fid.Unique); printf("afs_read: validPos %llu filePos %llu totalLength %d m.Length %llu noLock %d\n", tdc->validPos, filePos, totalLength, avc->f.m.Length, noLock); printf("afs_read: or len too low? %lld for %u.%u.%u\n", len, avc->f.fid.Fid.Volume, avc->f.fid.Fid.Vnode, avc->f.fid.Fid.Unique); } #endif #endif ReleaseReadLock(&tdc->lock); afs_PutDCache(tdc); tdc = NULL; } } if (!tdc) { #ifdef STRUCT_TASK_STRUCT_HAS_CRED if (afs_protocols & VICEP_ACCESS) { /* avoid foreground fetch */ if (!versionOk) { printf("afs_read: avoid forground %u.%u.%u\n", avc->f.fid.Fid.Volume, avc->f.fid.Fid.Vnode, avc->f.fid.Fid.Unique); goto try_background; } } #endif /* If we get here, it was not possible to start the * background daemon. With flag == 1 afs_GetDCache * does the FetchData rpc synchronously. */ ReleaseReadLock(&avc->lock); tdc = afs_GetDCache(avc, filePos, treq, &offset, &len, 1); ObtainReadLock(&avc->lock); if (tdc) ObtainReadLock(&tdc->lock); } } if (!tdc) { error = EIO; break; } len = tdc->validPos - filePos; afs_Trace3(afs_iclSetp, CM_TRACE_VNODEREAD, ICL_TYPE_POINTER, tdc, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(offset), ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(len)); if (len > totalLength) len = totalLength; /* will read len bytes */ if (len <= 0) { /* shouldn't get here if DFFetching is on */ afs_Trace4(afs_iclSetp, CM_TRACE_VNODEREAD2, ICL_TYPE_POINTER, tdc, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(tdc->validPos), ICL_TYPE_INT32, tdc->f.chunkBytes, ICL_TYPE_INT32, tdc->dflags); /* read past the end of a chunk, may not be at next chunk yet, and yet * also not at eof, so may have to supply fake zeros */ len = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset; /* bytes left in chunk addr space */ if (len > totalLength) len = totalLength; /* and still within xfr request */ tlen = avc->f.m.Length - offset; /* and still within file */ if (len > tlen) len = tlen; if (len > AFS_ZEROS) len = sizeof(afs_zeros); /* and in 0 buffer */ #ifdef AFS_DARWIN80_ENV trimlen = len; tuiop = afsio_darwin_partialcopy(auio, trimlen); #else afsio_copy(auio, &tuio, tvec); trimlen = len; afsio_trim(&tuio, trimlen); #endif AFS_UIOMOVE(afs_zeros, trimlen, UIO_READ, tuiop, code); if (code) { error = code; break; } } else { /* get the data from the file */ tfile = (struct osi_file *)osi_UFSOpen(&tdc->f.inode); #ifdef AFS_DARWIN80_ENV trimlen = len; tuiop = afsio_darwin_partialcopy(auio, trimlen); uio_setoffset(tuiop, offset); #else /* mung uio structure to be right for this transfer */ afsio_copy(auio, &tuio, tvec); trimlen = len; afsio_trim(&tuio, trimlen); tuio.afsio_offset = offset; #endif #if defined(AFS_AIX41_ENV) AFS_GUNLOCK(); code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, &tuio, NULL, NULL, NULL, afs_osi_credp); AFS_GLOCK(); #elif defined(AFS_AIX32_ENV) code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, &tuio, NULL, NULL); /* Flush all JFS pages now for big performance gain in big file cases * If we do something like this, must check to be sure that AFS file * isn't mmapped... see afs_gn_map() for why. */ /* if (tfile->vnode->v_gnode && tfile->vnode->v_gnode->gn_seg) { many different ways to do similar things: so far, the best performing one is #2, but #1 might match it if we straighten out the confusion regarding which pages to flush. It really does matter. 1. vm_flushp(tfile->vnode->v_gnode->gn_seg, 0, len/PAGESIZE - 1); 2. vm_releasep(tfile->vnode->v_gnode->gn_seg, offset/PAGESIZE, (len + PAGESIZE-1)/PAGESIZE); 3. vms_inactive(tfile->vnode->v_gnode->gn_seg) Doesn't work correctly 4. vms_delete(tfile->vnode->v_gnode->gn_seg) probably also fails tfile->vnode->v_gnode->gn_seg = NULL; 5. deletep 6. ipgrlse 7. ifreeseg Unfortunately, this seems to cause frequent "cache corruption" episodes. vm_releasep(tfile->vnode->v_gnode->gn_seg, offset/PAGESIZE, (len + PAGESIZE-1)/PAGESIZE); } */ #elif defined(AFS_AIX_ENV) code = VNOP_RDWR(tfile->vnode, UIO_READ, FREAD, (off_t) & offset, &tuio, NULL, NULL, -1); #elif defined(AFS_SUN5_ENV) AFS_GUNLOCK(); #ifdef AFS_SUN510_ENV VOP_RWLOCK(tfile->vnode, 0, NULL); code = VOP_READ(tfile->vnode, &tuio, 0, afs_osi_credp, NULL); VOP_RWUNLOCK(tfile->vnode, 0, NULL); #else VOP_RWLOCK(tfile->vnode, 0); code = VOP_READ(tfile->vnode, &tuio, 0, afs_osi_credp); VOP_RWUNLOCK(tfile->vnode, 0); #endif AFS_GLOCK(); #elif defined(AFS_SGI_ENV) AFS_GUNLOCK(); AFS_VOP_RWLOCK(tfile->vnode, VRWLOCK_READ); AFS_VOP_READ(tfile->vnode, &tuio, IO_ISLOCKED, afs_osi_credp, code); AFS_VOP_RWUNLOCK(tfile->vnode, VRWLOCK_READ); AFS_GLOCK(); #elif defined(AFS_HPUX100_ENV) AFS_GUNLOCK(); code = VOP_RDWR(tfile->vnode, &tuio, UIO_READ, 0, afs_osi_credp); AFS_GLOCK(); #elif defined(AFS_LINUX20_ENV) AFS_GUNLOCK(); code = osi_rdwr(tfile, &tuio, UIO_READ); AFS_GLOCK(); #elif defined(AFS_DARWIN80_ENV) AFS_GUNLOCK(); code = VNOP_READ(tfile->vnode, tuiop, 0, afs_osi_ctxtp); AFS_GLOCK(); #elif defined(AFS_DARWIN_ENV) AFS_GUNLOCK(); VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, current_proc()); code = VOP_READ(tfile->vnode, &tuio, 0, afs_osi_credp); VOP_UNLOCK(tfile->vnode, 0, current_proc()); AFS_GLOCK(); #elif defined(AFS_FBSD80_ENV) AFS_GUNLOCK(); VOP_LOCK(tfile->vnode, LK_EXCLUSIVE); code = VOP_READ(tfile->vnode, &tuio, 0, afs_osi_credp); VOP_UNLOCK(tfile->vnode, 0); AFS_GLOCK(); #elif defined(AFS_FBSD_ENV) AFS_GUNLOCK(); VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, curthread); code = VOP_READ(tfile->vnode, &tuio, 0, afs_osi_credp); VOP_UNLOCK(tfile->vnode, 0, curthread); AFS_GLOCK(); #elif defined(AFS_NBSD_ENV) AFS_GUNLOCK(); VOP_LOCK(tfile->vnode, LK_EXCLUSIVE); code = VOP_READ(tfile->vnode, &tuio, 0, afs_osi_credp); VOP_UNLOCK(tfile->vnode, 0); AFS_GLOCK(); #elif defined(AFS_XBSD_ENV) AFS_GUNLOCK(); VOP_LOCK(tfile->vnode, LK_EXCLUSIVE, curproc); code = VOP_READ(tfile->vnode, &tuio, 0, afs_osi_credp); VOP_UNLOCK(tfile->vnode, 0, curproc); AFS_GLOCK(); #else code = VOP_RDWR(tfile->vnode, &tuio, UIO_READ, 0, afs_osi_credp); #endif osi_UFSClose(tfile); if (code) { error = code; break; } } /* otherwise we've read some, fixup length, etc and continue with next seg */ len = len - AFS_UIO_RESID(tuiop); /* compute amount really transferred */ trimlen = len; afsio_skip(auio, trimlen); /* update input uio structure */ totalLength -= len; transferLength += len; filePos += len; if (len <= 0) break; /* surprise eof */ #ifdef AFS_DARWIN80_ENV if (tuiop) { uio_free(tuiop); tuiop = 0; } #endif } /* if we make it here with tdc non-zero, then it is the last chunk we * dealt with, and we have to release it when we're done. We hold on * to it in case we need to do a prefetch, obviously. */ if (tdc) { ReleaseReadLock(&tdc->lock); #if !defined(AFS_VM_RDWR_ENV) /* * try to queue prefetch, if needed. If DataVersion is zero there * should not be any more: files with DV 0 never have been stored * on the fileserver, symbolic links and directories never require * more than a single chunk. */ if (!noLock && !(hiszero(avc->f.m.DataVersion))) { if (!(tdc->mflags & DFNextStarted)) afs_PrefetchChunk(avc, tdc, acred, treq); } #endif afs_PutDCache(tdc); } if (!noLock) ReleaseReadLock(&avc->lock); #ifdef AFS_DARWIN80_ENV if (tuiop) uio_free(tuiop); #else osi_FreeSmallSpace(tvec); #endif AFS_DISCON_UNLOCK(); error = afs_CheckCode(error, treq, 13); afs_DestroyReq(treq); return error; }
/* Note that we don't set CDirty here, this is OK because the unlink * RPC is called synchronously */ int afs_remove(OSI_VC_DECL(adp), char *aname, afs_ucred_t *acred) { struct vrequest *treq = NULL; struct dcache *tdc; struct VenusFid unlinkFid; afs_int32 code; struct vcache *tvc; afs_size_t offset, len; struct afs_fakestat_state fakestate; OSI_VC_CONVERT(adp); AFS_STATCNT(afs_remove); afs_Trace2(afs_iclSetp, CM_TRACE_REMOVE, ICL_TYPE_POINTER, adp, ICL_TYPE_STRING, aname); if ((code = afs_CreateReq(&treq, acred))) { return code; } afs_InitFakeStat(&fakestate); AFS_DISCON_LOCK(); code = afs_EvalFakeStat(&adp, &fakestate, treq); if (code) goto done; /* Check if this is dynroot */ if (afs_IsDynroot(adp)) { code = afs_DynrootVOPRemove(adp, acred, aname); goto done; } if (afs_IsDynrootMount(adp)) { code = EROFS; goto done; } if (strlen(aname) > AFSNAMEMAX) { code = ENAMETOOLONG; goto done; } tagain: code = afs_VerifyVCache(adp, treq); tvc = NULL; if (code) { code = afs_CheckCode(code, treq, 23); goto done; } /** If the volume is read-only, return error without making an RPC to the * fileserver */ if (adp->f.states & CRO) { code = EROFS; goto done; } /* If we're running disconnected without logging, go no further... */ if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { code = ENETDOWN; goto done; } tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1); /* test for error below */ ObtainWriteLock(&adp->lock, 142); if (tdc) ObtainSharedLock(&tdc->lock, 638); /* * Make sure that the data in the cache is current. We may have * received a callback while we were waiting for the write lock. */ if (!(adp->f.states & CStatd) || (tdc && !hsame(adp->f.m.DataVersion, tdc->f.versionNo))) { ReleaseWriteLock(&adp->lock); if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } goto tagain; } unlinkFid.Fid.Vnode = 0; if (!tvc) { tvc = osi_dnlc_lookup(adp, aname, WRITE_LOCK); } /* This should not be necessary since afs_lookup() has already * done the work. */ if (!tvc) if (tdc) { code = afs_dir_Lookup(tdc, aname, &unlinkFid.Fid); if (code == 0) { afs_int32 cached = 0; unlinkFid.Cell = adp->f.fid.Cell; unlinkFid.Fid.Volume = adp->f.fid.Fid.Volume; if (unlinkFid.Fid.Unique == 0) { tvc = afs_LookupVCache(&unlinkFid, treq, &cached, adp, aname); } else { ObtainReadLock(&afs_xvcache); tvc = afs_FindVCache(&unlinkFid, 0, DO_STATS); ReleaseReadLock(&afs_xvcache); } } } if (AFS_IS_DISCON_RW) { if (!adp->f.shadow.vnode && !(adp->f.ddirty_flags & VDisconCreate)) { /* Make shadow copy of parent dir. */ afs_MakeShadowDir(adp, tdc); } /* Can't hold a dcache lock whilst we're getting a vcache one */ if (tdc) ReleaseSharedLock(&tdc->lock); /* XXX - We're holding adp->lock still, and we've got no * guarantee about whether the ordering matches the lock hierarchy */ ObtainWriteLock(&tvc->lock, 713); /* If we were locally created, then we don't need to do very * much beyond ensuring that we don't exist anymore */ if (tvc->f.ddirty_flags & VDisconCreate) { afs_DisconRemoveDirty(tvc); } else { /* Add removed file vcache to dirty list. */ afs_DisconAddDirty(tvc, VDisconRemove, 1); } adp->f.m.LinkCount--; ReleaseWriteLock(&tvc->lock); if (tdc) ObtainSharedLock(&tdc->lock, 714); } if (tvc && osi_Active(tvc)) { /* about to delete whole file, prefetch it first */ ReleaseWriteLock(&adp->lock); if (tdc) ReleaseSharedLock(&tdc->lock); ObtainWriteLock(&tvc->lock, 143); FetchWholeEnchilada(tvc, treq); ReleaseWriteLock(&tvc->lock); ObtainWriteLock(&adp->lock, 144); /* Technically I don't think we need this back, but let's hold it anyway; The "got" reference should actually be sufficient. */ if (tdc) ObtainSharedLock(&tdc->lock, 640); } osi_dnlc_remove(adp, aname, tvc); Tadp1 = adp; #ifndef AFS_DARWIN80_ENV Tadpr = VREFCOUNT(adp); #endif Ttvc = tvc; Tnam = aname; Tnam1 = 0; #ifndef AFS_DARWIN80_ENV if (tvc) Ttvcr = VREFCOUNT(tvc); #endif #ifdef AFS_AIX_ENV if (tvc && VREFCOUNT_GT(tvc, 2) && tvc->opens > 0 && !(tvc->f.states & CUnlinked)) { #else if (tvc && VREFCOUNT_GT(tvc, 1) && tvc->opens > 0 && !(tvc->f.states & CUnlinked)) { #endif char *unlname = afs_newname(); ReleaseWriteLock(&adp->lock); if (tdc) ReleaseSharedLock(&tdc->lock); code = afsrename(adp, aname, adp, unlname, acred, treq); Tnam1 = unlname; if (!code) { void *oldmvid = NULL; if (tvc->mvid.silly_name) oldmvid = tvc->mvid.silly_name; tvc->mvid.silly_name = unlname; if (oldmvid) osi_FreeSmallSpace(oldmvid); crhold(acred); if (tvc->uncred) { crfree(tvc->uncred); } tvc->uncred = acred; tvc->f.states |= CUnlinked; /* if rename succeeded, remove should not */ ObtainWriteLock(&tvc->lock, 715); if (tvc->f.ddirty_flags & VDisconRemove) { tvc->f.ddirty_flags &= ~VDisconRemove; } ReleaseWriteLock(&tvc->lock); } else { osi_FreeSmallSpace(unlname); } if (tdc) afs_PutDCache(tdc); afs_PutVCache(tvc); } else { code = afsremove(adp, tdc, tvc, aname, acred, treq); } done: afs_PutFakeStat(&fakestate); #if !defined(AFS_DARWIN80_ENV) && !defined(UKERNEL) /* we can't track by thread, it's not exported in the KPI; only do this on !macos */ osi_Assert(!WriteLocked(&adp->lock) || (adp->lock.pid_writer != MyPidxx)); #endif AFS_DISCON_UNLOCK(); afs_DestroyReq(treq); return code; } /* afs_remunlink -- This tries to delete the file at the server after it has * been renamed when unlinked locally but now has been finally released. * * CAUTION -- may be called with avc unheld. */ int afs_remunlink(struct vcache *avc, int doit) { afs_ucred_t *cred; char *unlname; struct vcache *adp; struct VenusFid dirFid; struct dcache *tdc; afs_int32 code = 0; if (NBObtainWriteLock(&avc->lock, 423)) return 0; #if defined(AFS_DARWIN80_ENV) if (vnode_get(AFSTOV(avc))) { ReleaseWriteLock(&avc->lock); return 0; } #endif if (avc->mvid.silly_name && (doit || (avc->f.states & CUnlinkedDel))) { struct vrequest *treq = NULL; if ((code = afs_CreateReq(&treq, avc->uncred))) { ReleaseWriteLock(&avc->lock); } else { /* Must bump the refCount because GetVCache may block. * Also clear mvid so no other thread comes here if we block. */ unlname = avc->mvid.silly_name; avc->mvid.silly_name = NULL; cred = avc->uncred; avc->uncred = NULL; #if defined(AFS_DARWIN_ENV) && !defined(AFS_DARWIN80_ENV) VREF(AFSTOV(avc)); #else AFS_FAST_HOLD(avc); #endif /* We'll only try this once. If it fails, just release the vnode. * Clear after doing hold so that NewVCache doesn't find us yet. */ avc->f.states &= ~(CUnlinked | CUnlinkedDel); ReleaseWriteLock(&avc->lock); dirFid.Cell = avc->f.fid.Cell; dirFid.Fid.Volume = avc->f.fid.Fid.Volume; dirFid.Fid.Vnode = avc->f.parent.vnode; dirFid.Fid.Unique = avc->f.parent.unique; adp = afs_GetVCache(&dirFid, treq, NULL, NULL); if (adp) { tdc = afs_FindDCache(adp, (afs_size_t) 0); ObtainWriteLock(&adp->lock, 159); if (tdc) ObtainSharedLock(&tdc->lock, 639); /* afsremove releases the adp & tdc locks, and does vn_rele(avc) */ code = afsremove(adp, tdc, avc, unlname, cred, treq); afs_PutVCache(adp); } else { /* we failed - and won't be back to try again. */ afs_PutVCache(avc); } osi_FreeSmallSpace(unlname); crfree(cred); afs_DestroyReq(treq); } } else { #if defined(AFS_DARWIN80_ENV) vnode_put(AFSTOV(avc)); #endif ReleaseWriteLock(&avc->lock); } return code; }
int afs_mkdir(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, struct vcache **avcp, afs_ucred_t *acred) { struct vrequest *treq = NULL; afs_int32 code; struct afs_conn *tc; struct rx_connection *rxconn; struct VenusFid newFid; struct dcache *tdc; struct dcache *new_dc; afs_size_t offset, len; struct vcache *tvc; struct AFSStoreStatus InStatus; struct AFSFetchStatus *OutFidStatus, *OutDirStatus; struct AFSCallBack CallBack; struct AFSVolSync tsync; afs_int32 now; struct afs_fakestat_state fakestate; XSTATS_DECLS; OSI_VC_CONVERT(adp); AFS_STATCNT(afs_mkdir); afs_Trace2(afs_iclSetp, CM_TRACE_MKDIR, ICL_TYPE_POINTER, adp, ICL_TYPE_STRING, aname); OutFidStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); memset(&InStatus, 0, sizeof(InStatus)); if ((code = afs_CreateReq(&treq, acred))) goto done2; afs_InitFakeStat(&fakestate); if (strlen(aname) > AFSNAMEMAX) { code = ENAMETOOLONG; goto done3; } if (!afs_ENameOK(aname)) { code = EINVAL; goto done3; } AFS_DISCON_LOCK(); code = afs_EvalFakeStat(&adp, &fakestate, treq); if (code) goto done; code = afs_VerifyVCache(adp, treq); if (code) goto done; /** If the volume is read-only, return error without making an RPC to the * fileserver */ if (adp->f.states & CRO) { code = EROFS; goto done; } if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { /*printf("Network is down in afs_mkdir\n");*/ code = ENETDOWN; goto done; } InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP; InStatus.ClientModTime = osi_Time(); InStatus.UnixModeBits = attrs->va_mode & 0xffff; /* only care about protection bits */ InStatus.Group = (afs_int32) afs_cr_gid(acred); tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1); ObtainWriteLock(&adp->lock, 153); if (!AFS_IS_DISCON_RW) { do { tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn); if (tc) { XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR); now = osi_Time(); RX_AFS_GUNLOCK(); code = RXAFS_MakeDir(rxconn, (struct AFSFid *)&adp->f.fid.Fid, aname, &InStatus, (struct AFSFid *)&newFid.Fid, OutFidStatus, OutDirStatus, &CallBack, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; CallBack.ExpirationTime += now; /* DON'T forget to Set the callback value... */ } else code = -1; } while (afs_Analyze (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_MAKEDIR, SHARED_LOCK, NULL)); if (code) { if (code < 0) { afs_StaleVCache(adp); } ReleaseWriteLock(&adp->lock); if (tdc) afs_PutDCache(tdc); goto done; } } else { /* Disconnected. */ /* We have the dir entry now, we can use it while disconnected. */ if (adp->mvid.target_root == NULL) { /* If not mount point, generate a new fid. */ newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; afs_GenFakeFid(&newFid, VDIR, 1); } /* XXX: If mount point???*/ /* Operations with the actual dir's cache entry are further * down, where the dir entry gets created. */ } /* if (!AFS_IS_DISCON_RW) */ /* otherwise, we should see if we can make the change to the dir locally */ if (tdc) ObtainWriteLock(&tdc->lock, 632); if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, OutDirStatus, 1)) { /* we can do it locally */ ObtainWriteLock(&afs_xdcache, 294); code = afs_dir_Create(tdc, aname, &newFid.Fid); ReleaseWriteLock(&afs_xdcache); if (code) { ZapDCE(tdc); /* surprise error -- use invalid value */ DZap(tdc); } } if (tdc) { ReleaseWriteLock(&tdc->lock); afs_PutDCache(tdc); } if (AFS_IS_DISCON_RW) /* We will have to settle with the local link count. */ adp->f.m.LinkCount++; else adp->f.m.LinkCount = OutDirStatus->LinkCount; newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; ReleaseWriteLock(&adp->lock); if (AFS_IS_DISCON_RW) { /* When disconnected, we have to create the full dir here. */ /* Generate a new vcache and fill it. */ tvc = afs_NewVCache(&newFid, NULL); if (tvc) { *avcp = tvc; } else { code = EIO; goto done; } ObtainWriteLock(&tvc->lock, 738); afs_GenDisconStatus(adp, tvc, &newFid, attrs, treq, VDIR); ReleaseWriteLock(&tvc->lock); /* And now make an empty dir, containing . and .. : */ /* Get a new dcache for it first. */ new_dc = afs_GetDCache(tvc, (afs_size_t) 0, treq, &offset, &len, 1); if (!new_dc) { /* printf("afs_mkdir: can't get new dcache for dir.\n"); */ code = EIO; goto done; } ObtainWriteLock(&afs_xdcache, 739); code = afs_dir_MakeDir(new_dc, (afs_int32 *) &newFid.Fid, (afs_int32 *) &adp->f.fid.Fid); ReleaseWriteLock(&afs_xdcache); /* if (code) printf("afs_mkdir: afs_dirMakeDir code = %u\n", code); */ afs_PutDCache(new_dc); ObtainWriteLock(&tvc->lock, 731); /* Update length in the vcache. */ tvc->f.m.Length = new_dc->f.chunkBytes; afs_DisconAddDirty(tvc, VDisconCreate, 1); ReleaseWriteLock(&tvc->lock); } else { /* now we're done with parent dir, create the real dir's cache entry */ tvc = afs_GetVCache(&newFid, treq, NULL, NULL); if (tvc) { code = 0; *avcp = tvc; } else { /* For some reason, we cannot fetch the vcache for our * newly-created dir. */ code = EIO; } } /* if (AFS_DISCON_RW) */ done: AFS_DISCON_UNLOCK(); done3: afs_PutFakeStat(&fakestate); code = afs_CheckCode(code, treq, 26); afs_DestroyReq(treq); done2: osi_FreeSmallSpace(OutFidStatus); osi_FreeSmallSpace(OutDirStatus); return code; }
afs_rmdir(OSI_VC_DECL(adp), char *aname, afs_ucred_t *acred) #endif { struct vrequest *treq = NULL; struct dcache *tdc; struct vcache *tvc = NULL; afs_int32 code; struct afs_conn *tc; afs_size_t offset, len; struct AFSFetchStatus OutDirStatus; struct AFSVolSync tsync; struct afs_fakestat_state fakestate; struct rx_connection *rxconn; XSTATS_DECLS; OSI_VC_CONVERT(adp); AFS_STATCNT(afs_rmdir); afs_Trace2(afs_iclSetp, CM_TRACE_RMDIR, ICL_TYPE_POINTER, adp, ICL_TYPE_STRING, aname); if ((code = afs_CreateReq(&treq, acred))) goto done2; afs_InitFakeStat(&fakestate); if (strlen(aname) > AFSNAMEMAX) { code = ENAMETOOLONG; goto done; } AFS_DISCON_LOCK(); code = afs_EvalFakeStat(&adp, &fakestate, treq); if (code) goto done; code = afs_VerifyVCache(adp, treq); if (code) goto done; /** If the volume is read-only, return error without making an RPC to the * fileserver */ if (adp->f.states & CRO) { code = EROFS; goto done; } if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { /* Disconnected read only mode. */ code = ENETDOWN; goto done; } tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1); /* test for error below */ ObtainWriteLock(&adp->lock, 154); if (tdc) ObtainSharedLock(&tdc->lock, 633); if (tdc && (adp->f.states & CForeign)) { struct VenusFid unlinkFid; unlinkFid.Fid.Vnode = 0; code = afs_dir_Lookup(tdc, aname, &unlinkFid.Fid); if (code == 0) { afs_int32 cached = 0; unlinkFid.Cell = adp->f.fid.Cell; unlinkFid.Fid.Volume = adp->f.fid.Fid.Volume; if (unlinkFid.Fid.Unique == 0) { tvc = afs_LookupVCache(&unlinkFid, treq, &cached, adp, aname); } else { ObtainReadLock(&afs_xvcache); tvc = afs_FindVCache(&unlinkFid, 0, 1 /* do xstats */ ); ReleaseReadLock(&afs_xvcache); } } } if (!AFS_IS_DISCON_RW) { /* Not disconnected, can connect to server. */ do { tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn); if (tc) { XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR); RX_AFS_GUNLOCK(); code = RXAFS_RemoveDir(rxconn, (struct AFSFid *)&adp->f.fid.Fid, aname, &OutDirStatus, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; } else code = -1; } while (afs_Analyze (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_REMOVEDIR, SHARED_LOCK, NULL)); if (code) { if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } if (code < 0) { afs_StaleVCache(adp); } ReleaseWriteLock(&adp->lock); goto done; } /* here if rpc worked; update the in-core link count */ adp->f.m.LinkCount = OutDirStatus.LinkCount; } else { /* Disconnected. */ if (!tdc) { ReleaseWriteLock(&adp->lock); /* printf("afs_rmdir: No local dcache!\n"); */ code = ENETDOWN; goto done; } if (!tvc) { /* Find the vcache. */ struct VenusFid tfid; tfid.Cell = adp->f.fid.Cell; tfid.Fid.Volume = adp->f.fid.Fid.Volume; code = afs_dir_Lookup(tdc, aname, &tfid.Fid); ObtainSharedLock(&afs_xvcache, 764); tvc = afs_FindVCache(&tfid, 0, 1 /* do xstats */ ); ReleaseSharedLock(&afs_xvcache); if (!tvc) { /* printf("afs_rmdir: Can't find dir's vcache!\n"); */ ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); /* drop ref count */ ReleaseWriteLock(&adp->lock); code = ENETDOWN; goto done; } } if (tvc->f.m.LinkCount > 2) { /* This dir contains more than . and .., thus it can't be * deleted. */ ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); afs_PutVCache(tvc); ReleaseWriteLock(&adp->lock); code = ENOTEMPTY; goto done; } /* Make a shadow copy of the parent dir (if not done already). * If we were created locally, then we don't need to have a shadow * directory (as there's no server state to remember) */ if (!adp->f.shadow.vnode && !(adp->f.ddirty_flags & VDisconCreate)) { afs_MakeShadowDir(adp, tdc); } adp->f.m.LinkCount--; } /* if (!AFS_IS_DISCON_RW) */ if (tdc) UpgradeSToWLock(&tdc->lock, 634); if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) { /* we can do it locally */ code = afs_dir_Delete(tdc, aname); if (code) { ZapDCE(tdc); /* surprise error -- invalid value */ DZap(tdc); } } if (tdc) { ReleaseWriteLock(&tdc->lock); afs_PutDCache(tdc); /* drop ref count */ } if (tvc) osi_dnlc_purgedp(tvc); /* get rid of any entries for this directory */ else osi_dnlc_remove(adp, aname, 0); if (tvc) { ObtainWriteLock(&tvc->lock, 155); tvc->f.states &= ~CUnique; /* For the dfs xlator */ if (AFS_IS_DISCON_RW) { if (tvc->f.ddirty_flags & VDisconCreate) { /* If we we were created whilst disconnected, removal doesn't * need to get logged. Just go away gracefully */ afs_DisconRemoveDirty(tvc); } else { afs_DisconAddDirty(tvc, VDisconRemove, 1); } } ReleaseWriteLock(&tvc->lock); afs_PutVCache(tvc); } ReleaseWriteLock(&adp->lock); /* don't worry about link count since dirs can not be hardlinked */ code = 0; done: AFS_DISCON_UNLOCK(); afs_PutFakeStat(&fakestate); code = afs_CheckCode(code, treq, 27); afs_DestroyReq(treq); done2: return code; }
static struct dentry *afs_export_get_parent(struct dentry *child) { struct VenusFid tfid; struct vrequest *treq = NULL; struct cell *tcell; struct vcache *vcp; struct dentry *dp = NULL; cred_t *credp; afs_uint32 cellidx; int code; if (!child->d_inode) { /* can't find the parent of a negative dentry */ #ifdef OSI_EXPORT_DEBUG printk("afs: get_parent(%s): no inode\n", child->d_name.name ? (char *)child->d_name.name : "?"); #endif return ERR_PTR(-EIO); } credp = crref(); AFS_GLOCK(); vcp = VTOAFS(child->d_inode); if (afs_IsDynrootMount(vcp)) { /* the dynmount directory; parent is always the AFS root */ tfid = afs_globalVp->f.fid; } else if (afs_IsDynrootAny(vcp) && VNUM_TO_VNTYPE(vcp->f.fid.Fid.Vnode) == VN_TYPE_MOUNT) { /* a mount point in the dynmount directory */ afs_GetDynrootMountFid(&tfid); } else if (vcp->mvstat == AFS_MVSTAT_ROOT) { /* volume root */ ObtainReadLock(&vcp->lock); if (vcp->mvid.parent && vcp->mvid.parent->Fid.Volume) { tfid = *vcp->mvid.parent; ReleaseReadLock(&vcp->lock); } else { ReleaseReadLock(&vcp->lock); tcell = afs_GetCell(vcp->f.fid.Cell, READ_LOCK); if (!tcell) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_parent(0x%08x/%d/%d.%d): no cell\n", vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); #endif dp = ERR_PTR(-ENOENT); goto done; } cellidx = tcell->cellIndex; afs_PutCell(tcell, READ_LOCK); afs_GetDynrootMountFid(&tfid); tfid.Fid.Vnode = VNUM_FROM_TYPEID(VN_TYPE_MOUNT, cellidx << 2); tfid.Fid.Unique = vcp->f.fid.Fid.Volume; } } else { /* any other vnode */ if (vType(vcp) == VDIR && !vcp->f.parent.vnode && vcp->mvstat != AFS_MVSTAT_MTPT) { code = afs_CreateReq(&treq, credp); if (code) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_parent(0x%08x/%d/%d.%d): afs_CreateReq: %d\n", vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code); #endif dp = ERR_PTR(-ENOENT); goto done; } else { code = update_dir_parent(treq, vcp); if (code) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_parent(0x%08x/%d/%d.%d): update_dir_parent: %d\n", vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code); #endif dp = ERR_PTR(-ENOENT); goto done; } } } tfid.Cell = vcp->f.fid.Cell; tfid.Fid.Volume = vcp->f.fid.Fid.Volume; tfid.Fid.Vnode = vcp->f.parent.vnode; tfid.Fid.Unique = vcp->f.parent.unique; } #ifdef OSI_EXPORT_DEBUG printk("afs: get_parent(0x%08x/%d/%d.%d): => 0x%08x/%d/%d.%d\n", vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, tfid.Cell, tfid.Fid.Volume, tfid.Fid.Vnode, tfid.Fid.Unique); #endif dp = get_dentry_from_fid(credp, &tfid); if (!dp) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_parent(0x%08x/%d/%d.%d): no dentry\n", vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); #endif dp = ERR_PTR(-ENOENT); } done: afs_DestroyReq(treq); AFS_GUNLOCK(); crfree(credp); return dp; }
static int afs_export_get_name(struct dentry *parent, char *name, struct dentry *child) { struct afs_fakestat_state fakestate; struct get_name_data data; struct vrequest *treq = NULL; struct volume *tvp; struct vcache *vcp; struct dcache *tdc; cred_t *credp; afs_size_t dirOffset, dirLen; afs_int32 code = 0; if (!parent->d_inode) { #ifdef OSI_EXPORT_DEBUG /* can't lookup name in a negative dentry */ printk("afs: get_name(%s, %s): no parent inode\n", parent->d_name.name ? (char *)parent->d_name.name : "?", child->d_name.name ? (char *)child->d_name.name : "?"); #endif return -EIO; } if (!child->d_inode) { #ifdef OSI_EXPORT_DEBUG /* can't find the FID of negative dentry */ printk("afs: get_name(%s, %s): no child inode\n", parent->d_name.name ? (char *)parent->d_name.name : "?", child->d_name.name ? (char *)child->d_name.name : "?"); #endif return -ENOENT; } afs_InitFakeStat(&fakestate); credp = crref(); AFS_GLOCK(); vcp = VTOAFS(child->d_inode); /* special case dynamic mount directory */ if (afs_IsDynrootMount(vcp)) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): this is the dynmount dir\n", parent->d_name.name ? (char *)parent->d_name.name : "?", vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); #endif data.fid = vcp->f.fid; if (VTOAFS(parent->d_inode) == afs_globalVp) strcpy(name, AFS_DYNROOT_MOUNTNAME); else code = -ENOENT; goto done; } /* Figure out what FID to look for */ if (vcp->mvstat == AFS_MVSTAT_ROOT) { /* volume root */ tvp = afs_GetVolume(&vcp->f.fid, 0, READ_LOCK); if (!tvp) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): no volume for root\n", parent->d_name.name ? (char *)parent->d_name.name : "?", vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); #endif code = ENOENT; goto done; } data.fid = tvp->mtpoint; afs_PutVolume(tvp, READ_LOCK); } else { data.fid = vcp->f.fid; } vcp = VTOAFS(parent->d_inode); #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): parent is 0x%08x/%d/%d.%d\n", parent->d_name.name ? (char *)parent->d_name.name : "?", data.fid.Cell, data.fid.Fid.Volume, data.fid.Fid.Vnode, data.fid.Fid.Unique, vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); #endif code = afs_CreateReq(&treq, credp); if (code) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): afs_CreateReq: %d\n", parent->d_name.name ? (char *)parent->d_name.name : "?", data.fid.Cell, data.fid.Fid.Volume, data.fid.Fid.Vnode, data.fid.Fid.Unique, code); #endif goto done; } /* a dynamic mount point in the dynamic mount directory */ if (afs_IsDynrootMount(vcp) && afs_IsDynrootAnyFid(&data.fid) && VNUM_TO_VNTYPE(data.fid.Fid.Vnode) == VN_TYPE_MOUNT) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): dynamic mount point\n", parent->d_name.name ? (char *)parent->d_name.name : "?", data.fid.Cell, data.fid.Fid.Volume, data.fid.Fid.Vnode, data.fid.Fid.Unique); #endif vcp = afs_GetVCache(&data.fid, treq, NULL, NULL); if (vcp) { ObtainReadLock(&vcp->lock); if (strlen(vcp->linkData + 1) <= NAME_MAX) strcpy(name, vcp->linkData + 1); else code = ENOENT; ReleaseReadLock(&vcp->lock); afs_PutVCache(vcp); } else { #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): no vcache\n", parent->d_name.name ? (char *)parent->d_name.name : "?", data.fid.Cell, data.fid.Fid.Volume, data.fid.Fid.Vnode, data.fid.Fid.Unique); #endif code = ENOENT; } goto done; } code = afs_EvalFakeStat(&vcp, &fakestate, treq); if (code) goto done; if (vcp->f.fid.Cell != data.fid.Cell || vcp->f.fid.Fid.Volume != data.fid.Fid.Volume) { /* parent is not the expected cell and volume; thus it * cannot possibly contain the fid we are looking for */ #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): wrong parent 0x%08x/%d\n", parent->d_name.name ? (char *)parent->d_name.name : "?", data.fid.Cell, data.fid.Fid.Volume, data.fid.Fid.Vnode, data.fid.Fid.Unique, vcp->f.fid.Cell, vcp->f.fid.Fid.Volume); #endif code = ENOENT; goto done; } redo: if (!(vcp->f.states & CStatd)) { if ((code = afs_VerifyVCache2(vcp, treq))) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): VerifyVCache2(0x%08x/%d/%d.%d): %d\n", parent->d_name.name ? (char *)parent->d_name.name : "?", data.fid.Cell, data.fid.Fid.Volume, data.fid.Fid.Vnode, data.fid.Fid.Unique, vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code); #endif goto done; } } tdc = afs_GetDCache(vcp, (afs_size_t) 0, treq, &dirOffset, &dirLen, 1); if (!tdc) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): GetDCache(0x%08x/%d/%d.%d): %d\n", parent->d_name.name ? (char *)parent->d_name.name : "?", data.fid.Cell, data.fid.Fid.Volume, data.fid.Fid.Vnode, data.fid.Fid.Unique, vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code); #endif code = EIO; goto done; } ObtainReadLock(&vcp->lock); ObtainReadLock(&tdc->lock); /* * Make sure that the data in the cache is current. There are two * cases we need to worry about: * 1. The cache data is being fetched by another process. * 2. The cache data is no longer valid */ while ((vcp->f.states & CStatd) && (tdc->dflags & DFFetching) && hsame(vcp->f.m.DataVersion, tdc->f.versionNo)) { ReleaseReadLock(&tdc->lock); ReleaseReadLock(&vcp->lock); afs_osi_Sleep(&tdc->validPos); ObtainReadLock(&vcp->lock); ObtainReadLock(&tdc->lock); } if (!(vcp->f.states & CStatd) || !hsame(vcp->f.m.DataVersion, tdc->f.versionNo)) { ReleaseReadLock(&tdc->lock); ReleaseReadLock(&vcp->lock); afs_PutDCache(tdc); #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): dir (0x%08x/%d/%d.%d) changed; retrying\n", parent->d_name.name ? (char *)parent->d_name.name : "?", data.fid.Cell, data.fid.Fid.Volume, data.fid.Fid.Vnode, data.fid.Fid.Unique, vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); #endif goto redo; } data.name = name; data.found = 0; code = afs_dir_EnumerateDir(tdc, get_name_hook, &data); if (!code && !data.found) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): not found\n", parent->d_name.name ? (char *)parent->d_name.name : "?", data.fid.Cell, data.fid.Fid.Volume, data.fid.Fid.Vnode, data.fid.Fid.Unique); #endif code = ENOENT; } else if (code) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): Enumeratedir(0x%08x/%d/%d.%d): %d\n", parent->d_name.name ? (char *)parent->d_name.name : "?", data.fid.Cell, data.fid.Fid.Volume, data.fid.Fid.Vnode, data.fid.Fid.Unique, vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code); #endif } ReleaseReadLock(&tdc->lock); ReleaseReadLock(&vcp->lock); afs_PutDCache(tdc); done: if (!code) { printk("afs: get_name(%s, 0x%08x/%d/%d.%d) => %s\n", parent->d_name.name ? (char *)parent->d_name.name : "?", data.fid.Cell, data.fid.Fid.Volume, data.fid.Fid.Vnode, data.fid.Fid.Unique, name); } afs_PutFakeStat(&fakestate); code = afs_CheckCode(code, treq, 102); afs_DestroyReq(treq); AFS_GUNLOCK(); crfree(credp); return -code; }
/* * This is almost exactly like the PFlush() routine in afs_pioctl.c, * but that routine is static. We are about to change a file from * normal caching to bypass it's caching. Therefore, we want to * free up any cache space in use by the file, and throw out any * existing VM pages for the file. We keep track of the number of * times we go back and forth from caching to bypass. */ void afs_TransitionToBypass(struct vcache *avc, afs_ucred_t *acred, int aflags) { afs_int32 code; int setDesire = 0; int setManual = 0; if (!avc) return; if (aflags & TRANSChangeDesiredBit) setDesire = 1; if (aflags & TRANSSetManualBit) setManual = 1; AFS_GLOCK(); ObtainWriteLock(&avc->lock, 925); /* * Someone may have beat us to doing the transition - we had no lock * when we checked the flag earlier. No cause to panic, just return. */ if (avc->cachingStates & FCSBypass) goto done; /* If we never cached this, just change state */ if (setDesire && (!(avc->cachingStates & FCSBypass))) { avc->cachingStates |= FCSBypass; goto done; } /* cg2v, try to store any chunks not written 20071204 */ if (avc->execsOrWriters > 0) { struct vrequest *treq = NULL; code = afs_CreateReq(&treq, acred); if (!code) { code = afs_StoreAllSegments(avc, treq, AFS_SYNC | AFS_LASTSTORE); afs_DestroyReq(treq); } } #if 0 /* also cg2v, don't dequeue the callback */ ObtainWriteLock(&afs_xcbhash, 956); afs_DequeueCallback(avc); ReleaseWriteLock(&afs_xcbhash); #endif avc->f.states &= ~(CStatd | CDirty); /* next reference will re-stat */ /* now find the disk cache entries */ afs_TryToSmush(avc, acred, 1); osi_dnlc_purgedp(avc); if (avc->linkData && !(avc->f.states & CCore)) { afs_osi_Free(avc->linkData, strlen(avc->linkData) + 1); avc->linkData = NULL; } avc->cachingStates |= FCSBypass; /* Set the bypass flag */ if(setDesire) avc->cachingStates |= FCSDesireBypass; if(setManual) avc->cachingStates |= FCSManuallySet; avc->cachingTransitions++; done: ReleaseWriteLock(&avc->lock); AFS_GUNLOCK(); }
/* dispatch a no-cache read request */ afs_int32 afs_ReadNoCache(struct vcache *avc, struct nocache_read_request *bparms, afs_ucred_t *acred) { afs_int32 code; afs_int32 bcnt; struct brequest *breq; struct vrequest *areq = NULL; if (avc->vc_error) { code = EIO; afs_warn("afs_ReadNoCache VCache Error!\n"); goto cleanup; } AFS_GLOCK(); /* the receiver will free areq */ code = afs_CreateReq(&areq, acred); if (code) { afs_warn("afs_ReadNoCache afs_CreateReq error!\n"); } else { code = afs_VerifyVCache(avc, areq); if (code) { afs_warn("afs_ReadNoCache Failed to verify VCache!\n"); } } AFS_GUNLOCK(); if (code) { code = afs_CheckCode(code, areq, 11); /* failed to get it */ goto cleanup; } bparms->areq = areq; /* and queue this one */ bcnt = 1; AFS_GLOCK(); while(bcnt < 20) { breq = afs_BQueue(BOP_FETCH_NOCACHE, avc, B_DONTWAIT, 0, acred, 1, 1, bparms, (void *)0, (void *)0); if(breq != 0) { code = 0; break; } afs_osi_Wait(10 * bcnt, 0, 0); } AFS_GUNLOCK(); if(!breq) { code = EBUSY; goto cleanup; } return code; cleanup: /* If there's a problem before we queue the request, we need to * do everything that would normally happen when the request was * processed, like unlocking the pages and freeing memory. */ unlock_and_release_pages(bparms->auio); AFS_GLOCK(); afs_DestroyReq(areq); AFS_GUNLOCK(); osi_Free(bparms->auio->uio_iov, bparms->auio->uio_iovcnt * sizeof(struct iovec)); osi_Free(bparms->auio, sizeof(struct uio)); osi_Free(bparms, sizeof(struct nocache_read_request)); return code; }
int afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, afs_ucred_t *acred) #endif { afs_int32 code; struct vrequest *treq = NULL; struct unixuser *au; int inited = 0; OSI_VC_CONVERT(avc); AFS_STATCNT(afs_getattr); afs_Trace2(afs_iclSetp, CM_TRACE_GETATTR, ICL_TYPE_POINTER, avc, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length)); if (afs_fakestat_enable && avc->mvstat == AFS_MVSTAT_MTPT) { struct afs_fakestat_state fakestat; struct vrequest *ureq = NULL; code = afs_CreateReq(&ureq, acred); if (code) { return code; } afs_InitFakeStat(&fakestat); code = afs_TryEvalFakeStat(&avc, &fakestat, ureq); if (code) { afs_PutFakeStat(&fakestat); afs_DestroyReq(ureq); return code; } code = afs_CopyOutAttrs(avc, attrs); afs_PutFakeStat(&fakestat); afs_DestroyReq(ureq); return code; } #if defined(AFS_SUN5_ENV) if (flags & ATTR_HINT) { code = afs_CopyOutAttrs(avc, attrs); return code; } #endif #if defined(AFS_DARWIN_ENV) && !defined(AFS_DARWIN80_ENV) if (avc->f.states & CUBCinit) { code = afs_CopyOutAttrs(avc, attrs); return code; } #endif AFS_DISCON_LOCK(); if (afs_shuttingdown != AFS_RUNNING) { AFS_DISCON_UNLOCK(); return EIO; } if (!(avc->f.states & CStatd)) { if (!(code = afs_CreateReq(&treq, acred))) { code = afs_VerifyVCache2(avc, treq); inited = 1; } } else code = 0; #if defined(AFS_SUN5_ENV) if (code == 0) osi_FlushPages(avc, acred); #endif if (code == 0) { osi_FlushText(avc); /* only needed to flush text if text locked last time */ code = afs_CopyOutAttrs(avc, attrs); if (afs_nfsexporter) { if (!inited) { if ((code = afs_CreateReq(&treq, acred))) { return code; } inited = 1; } if (AFS_NFSXLATORREQ(acred)) { if ((vType(avc) != VDIR) && !afs_AccessOK(avc, PRSFS_READ, treq, CHECK_MODE_BITS | CMB_ALLOW_EXEC_AS_READ)) { afs_DestroyReq(treq); return EACCES; } } if ((au = afs_FindUser(treq->uid, -1, READ_LOCK))) { struct afs_exporter *exporter = au->exporter; if (exporter && !(afs_nfsexporter->exp_states & EXP_UNIXMODE)) { unsigned int ubits; /* * If the remote user wishes to enforce default Unix mode semantics, * like in the nfs exporter case, we OR in the user bits * into the group and other bits. We need to do this * because there is no RFS_ACCESS call and thus nfs * clients implement nfs_access by interpreting the * mode bits in the traditional way, which of course * loses with afs. */ ubits = (attrs->va_mode & 0700) >> 6; attrs->va_mode = attrs->va_mode | ubits | (ubits << 3); /* If it's the root of AFS, replace the inode number with the * inode number of the mounted on directory; otherwise this * confuses getwd()... */ #ifdef AFS_LINUX22_ENV if (avc == afs_globalVp) { struct inode *ip = AFSTOV(avc)->i_sb->s_root->d_inode; attrs->va_nodeid = ip->i_ino; /* VTOI()? */ } #else if ( #if defined(AFS_DARWIN_ENV) vnode_isvroot(AFSTOV(avc)) #elif defined(AFS_NBSD50_ENV) AFSTOV(avc)->v_vflag & VV_ROOT #else AFSTOV(avc)->v_flag & VROOT #endif ) { struct vnode *vp = AFSTOV(avc); #ifdef AFS_DARWIN80_ENV /* XXX vp = vnode_mount(vp)->mnt_vnodecovered; */ vp = 0; #else vp = vp->v_vfsp->vfs_vnodecovered; if (vp) { /* Ignore weird failures */ #ifdef AFS_SGI62_ENV attrs->va_nodeid = VnodeToIno(vp); #else struct inode *ip; ip = (struct inode *)VTOI(vp); if (ip) /* Ignore weird failures */ attrs->va_nodeid = ip->i_number; #endif } #endif } #endif /* AFS_LINUX22_ENV */ } afs_PutUser(au, READ_LOCK); } }
/* don't set CDirty in here because RPC is called synchronously */ int afs_symlink(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, char *atargetName, struct vcache **tvcp, afs_ucred_t *acred) { afs_uint32 now = 0; struct vrequest *treq = NULL; afs_int32 code = 0; struct afs_conn *tc; struct VenusFid newFid; struct dcache *tdc; afs_size_t offset, len; afs_int32 alen; struct server *hostp = 0; struct vcache *tvc; struct AFSStoreStatus InStatus; struct AFSFetchStatus *OutFidStatus, *OutDirStatus; struct AFSCallBack CallBack; struct AFSVolSync tsync; struct volume *volp = 0; struct afs_fakestat_state fakestate; struct rx_connection *rxconn; XSTATS_DECLS; OSI_VC_CONVERT(adp); AFS_STATCNT(afs_symlink); afs_Trace2(afs_iclSetp, CM_TRACE_SYMLINK, ICL_TYPE_POINTER, adp, ICL_TYPE_STRING, aname); OutFidStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); memset(&InStatus, 0, sizeof(InStatus)); if ((code = afs_CreateReq(&treq, acred))) goto done2; afs_InitFakeStat(&fakestate); AFS_DISCON_LOCK(); code = afs_EvalFakeStat(&adp, &fakestate, treq); if (code) goto done; if (strlen(aname) > AFSNAMEMAX || strlen(atargetName) > AFSPATHMAX) { code = ENAMETOOLONG; goto done; } if (afs_IsDynroot(adp)) { code = afs_DynrootVOPSymlink(adp, acred, aname, atargetName); goto done; } if (afs_IsDynrootMount(adp)) { code = EROFS; goto done; } code = afs_VerifyVCache(adp, treq); if (code) { code = afs_CheckCode(code, treq, 30); goto done; } /** If the volume is read-only, return error without making an RPC to the * fileserver */ if (adp->f.states & CRO) { code = EROFS; goto done; } if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { code = ENETDOWN; goto done; } InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE; InStatus.ClientModTime = osi_Time(); alen = strlen(atargetName); /* we want it to include the null */ if ( (*atargetName == '#' || *atargetName == '%') && alen > 1 && atargetName[alen-1] == '.') { InStatus.UnixModeBits = 0644; /* mt pt: null from "." at end */ if (alen == 1) alen++; /* Empty string */ } else { InStatus.UnixModeBits = 0755; alen++; /* add in the null */ } tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1); volp = afs_FindVolume(&adp->f.fid, READ_LOCK); /*parent is also in same vol */ ObtainWriteLock(&adp->lock, 156); if (tdc) ObtainWriteLock(&tdc->lock, 636); /* No further locks: if the SymLink succeeds, it does not matter what happens * to our local copy of the directory. If somebody tampers with it in the meantime, * the copy will be invalidated */ if (!AFS_IS_DISCON_RW) { do { tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn); if (tc) { hostp = tc->parent->srvr->server; XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SYMLINK); if (adp->f.states & CForeign) { now = osi_Time(); RX_AFS_GUNLOCK(); code = RXAFS_DFSSymlink(rxconn, (struct AFSFid *)&adp->f.fid.Fid, aname, atargetName, &InStatus, (struct AFSFid *)&newFid.Fid, OutFidStatus, OutDirStatus, &CallBack, &tsync); RX_AFS_GLOCK(); } else { RX_AFS_GUNLOCK(); code = RXAFS_Symlink(rxconn, (struct AFSFid *)&adp->f.fid.Fid, aname, atargetName, &InStatus, (struct AFSFid *)&newFid.Fid, OutFidStatus, OutDirStatus, &tsync); RX_AFS_GLOCK(); } XSTATS_END_TIME; } else code = -1; } while (afs_Analyze (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_SYMLINK, SHARED_LOCK, NULL)); } else { newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; afs_GenFakeFid(&newFid, VREG, 0); } ObtainWriteLock(&afs_xvcache, 40); if (code) { if (code < 0) { afs_StaleVCache(adp); } ReleaseWriteLock(&adp->lock); ReleaseWriteLock(&afs_xvcache); if (tdc) { ReleaseWriteLock(&tdc->lock); afs_PutDCache(tdc); } goto done; } /* otherwise, we should see if we can make the change to the dir locally */ if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, OutDirStatus, 1)) { /* we can do it locally */ ObtainWriteLock(&afs_xdcache, 293); /* If the following fails because the name has been created in the meantime, the * directory is out-of-date - the file server knows best! */ code = afs_dir_Create(tdc, aname, &newFid.Fid); ReleaseWriteLock(&afs_xdcache); if (code && !AFS_IS_DISCON_RW) { ZapDCE(tdc); /* surprise error -- use invalid value */ DZap(tdc); } } if (tdc) { ReleaseWriteLock(&tdc->lock); afs_PutDCache(tdc); } newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; ReleaseWriteLock(&adp->lock); /* now we're done with parent dir, create the link's entry. Note that * no one can get a pointer to the new cache entry until we release * the xvcache lock. */ tvc = afs_NewVCache(&newFid, hostp); if (!tvc) { code = -2; ReleaseWriteLock(&afs_xvcache); goto done; } ObtainWriteLock(&tvc->lock, 157); ObtainWriteLock(&afs_xcbhash, 500); tvc->f.states |= CStatd; /* have valid info */ tvc->f.states &= ~CBulkFetching; if (adp->f.states & CForeign) { tvc->f.states |= CForeign; /* We don't have to worry about losing the callback since we're doing it * under the afs_xvcache lock actually, afs_NewVCache may drop the * afs_xvcache lock, if it calls afs_FlushVCache */ tvc->cbExpires = CallBack.ExpirationTime + now; afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp); } else { tvc->cbExpires = 0x7fffffff; /* never expires, they can't change */ /* since it never expires, we don't have to queue the callback */ } ReleaseWriteLock(&afs_xcbhash); if (AFS_IS_DISCON_RW) { attrs->va_mode = InStatus.UnixModeBits; afs_GenDisconStatus(adp, tvc, &newFid, attrs, treq, VLNK); code = afs_DisconCreateSymlink(tvc, atargetName, treq); if (code) { /* XXX - When this goes wrong, we need to tidy up the changes we made to * the parent, and get rid of the vcache we just created */ ReleaseWriteLock(&tvc->lock); ReleaseWriteLock(&afs_xvcache); afs_PutVCache(tvc); goto done; } afs_DisconAddDirty(tvc, VDisconCreate, 0); } else { afs_ProcessFS(tvc, OutFidStatus, treq); } if (!tvc->linkData) { tvc->linkData = afs_osi_Alloc(alen); osi_Assert(tvc->linkData != NULL); strncpy(tvc->linkData, atargetName, alen - 1); tvc->linkData[alen - 1] = 0; } ReleaseWriteLock(&tvc->lock); ReleaseWriteLock(&afs_xvcache); if (tvcp) *tvcp = tvc; else afs_PutVCache(tvc); code = 0; done: afs_PutFakeStat(&fakestate); if (volp) afs_PutVolume(volp, READ_LOCK); AFS_DISCON_UNLOCK(); code = afs_CheckCode(code, treq, 31); afs_DestroyReq(treq); done2: osi_FreeSmallSpace(OutFidStatus); osi_FreeSmallSpace(OutDirStatus); return code; }
int afs_CheckRootVolume(void) { char rootVolName[32]; struct volume *tvp = NULL; int usingDynroot = afs_GetDynrootEnable(); int localcell; AFS_STATCNT(afs_CheckRootVolume); if (*afs_rootVolumeName == 0) { strcpy(rootVolName, "root.afs"); } else { strcpy(rootVolName, afs_rootVolumeName); } if (usingDynroot) { afs_GetDynrootFid(&afs_rootFid); tvp = afs_GetVolume(&afs_rootFid, NULL, READ_LOCK); } else { struct cell *lc = afs_GetPrimaryCell(READ_LOCK); if (!lc) return ENOENT; localcell = lc->cellNum; afs_PutCell(lc, READ_LOCK); tvp = afs_GetVolumeByName(rootVolName, localcell, 1, NULL, READ_LOCK); if (!tvp) { char buf[128]; int len = strlen(rootVolName); if ((len < 9) || strcmp(&rootVolName[len - 9], ".readonly")) { strcpy(buf, rootVolName); afs_strcat(buf, ".readonly"); tvp = afs_GetVolumeByName(buf, localcell, 1, NULL, READ_LOCK); } } if (tvp) { int volid = (tvp->roVol ? tvp->roVol : tvp->volume); afs_rootFid.Cell = localcell; if (afs_rootFid.Fid.Volume && afs_rootFid.Fid.Volume != volid && afs_globalVp) { /* If we had a root fid before and it changed location we reset * the afs_globalVp so that it will be reevaluated. * Just decrement the reference count. This only occurs during * initial cell setup and can panic the machine if we set the * count to zero and fs checkv is executed when the current * directory is /afs. */ #ifdef AFS_LINUX20_ENV { struct vrequest *treq = NULL; struct vattr vattr; cred_t *credp; struct dentry *dp; struct vcache *vcp; afs_rootFid.Fid.Volume = volid; afs_rootFid.Fid.Vnode = 1; afs_rootFid.Fid.Unique = 1; credp = crref(); if (afs_CreateReq(&treq, credp)) goto out; vcp = afs_GetVCache(&afs_rootFid, treq, NULL, NULL); if (!vcp) goto out; afs_getattr(vcp, &vattr, credp); afs_fill_inode(AFSTOV(vcp), &vattr); dp = d_find_alias(AFSTOV(afs_globalVp)); #if defined(AFS_LINUX24_ENV) #if defined(HAVE_DCACHE_LOCK) spin_lock(&dcache_lock); #else spin_lock(&AFSTOV(vcp)->i_lock); #endif #if defined(AFS_LINUX26_ENV) spin_lock(&dp->d_lock); #endif #endif #if defined(D_ALIAS_IS_HLIST) hlist_del_init(&dp->d_alias); hlist_add_head(&dp->d_alias, &(AFSTOV(vcp)->i_dentry)); #else list_del_init(&dp->d_alias); list_add(&dp->d_alias, &(AFSTOV(vcp)->i_dentry)); #endif dp->d_inode = AFSTOV(vcp); #if defined(AFS_LINUX24_ENV) #if defined(AFS_LINUX26_ENV) spin_unlock(&dp->d_lock); #endif #if defined(HAVE_DCACHE_LOCK) spin_unlock(&dcache_lock); #else spin_unlock(&AFSTOV(vcp)->i_lock); #endif #endif dput(dp); AFS_FAST_RELE(afs_globalVp); afs_globalVp = vcp; out: crfree(credp); afs_DestroyReq(treq); } #else #ifdef AFS_DARWIN80_ENV afs_PutVCache(afs_globalVp); #else AFS_FAST_RELE(afs_globalVp); #endif afs_globalVp = 0; #endif } afs_rootFid.Fid.Volume = volid; afs_rootFid.Fid.Vnode = 1; afs_rootFid.Fid.Unique = 1; } } if (tvp) { afs_initState = 300; /* won */ afs_osi_Wakeup(&afs_initState); afs_PutVolume(tvp, READ_LOCK); } if (afs_rootFid.Fid.Volume) return 0; else return ENOENT; }
/* question: does afs_create need to set CDirty in the adp or the avc? * I think we can get away without it, but I'm not sure. Note that * afs_setattr is called in here for truncation. */ #ifdef AFS_SGI64_ENV int afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, int flags, int amode, struct vcache **avcp, afs_ucred_t *acred) #else /* AFS_SGI64_ENV */ int afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, enum vcexcl aexcl, int amode, struct vcache **avcp, afs_ucred_t *acred) #endif /* AFS_SGI64_ENV */ { afs_int32 origCBs, origZaps, finalZaps; struct vrequest *treq = NULL; afs_int32 code; struct afs_conn *tc; struct VenusFid newFid; struct AFSStoreStatus InStatus; struct AFSFetchStatus *OutFidStatus, *OutDirStatus; struct AFSVolSync tsync; struct AFSCallBack CallBack; afs_int32 now; struct dcache *tdc; afs_size_t offset, len; struct server *hostp = 0; struct vcache *tvc; struct volume *volp = 0; struct afs_fakestat_state fakestate; struct rx_connection *rxconn; XSTATS_DECLS; OSI_VC_CONVERT(adp); AFS_STATCNT(afs_create); OutFidStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); memset(&InStatus, 0, sizeof(InStatus)); if ((code = afs_CreateReq(&treq, acred))) goto done2; afs_Trace3(afs_iclSetp, CM_TRACE_CREATE, ICL_TYPE_POINTER, adp, ICL_TYPE_STRING, aname, ICL_TYPE_INT32, amode); afs_InitFakeStat(&fakestate); #ifdef AFS_SGI65_ENV /* If avcp is passed not null, it's the old reference to this file. * We can use this to avoid create races. For now, just decrement * the reference count on it. */ if (*avcp) { AFS_RELE(AFSTOV(*avcp)); *avcp = NULL; } #endif if (strlen(aname) > AFSNAMEMAX) { code = ENAMETOOLONG; goto done3; } if (!afs_ENameOK(aname)) { code = EINVAL; goto done3; } switch (attrs->va_type) { case VBLK: case VCHR: #if !defined(AFS_SUN5_ENV) case VSOCK: #endif case VFIFO: /* We don't support special devices or FIFOs */ code = EINVAL; goto done3; default: ; } AFS_DISCON_LOCK(); code = afs_EvalFakeStat(&adp, &fakestate, treq); if (code) goto done; tagain: code = afs_VerifyVCache(adp, treq); if (code) goto done; /** If the volume is read-only, return error without making an RPC to the * fileserver */ if (adp->f.states & CRO) { code = EROFS; goto done; } if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { code = ENETDOWN; goto done; } tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1); ObtainWriteLock(&adp->lock, 135); if (tdc) ObtainSharedLock(&tdc->lock, 630); /* * Make sure that the data in the cache is current. We may have * received a callback while we were waiting for the write lock. */ if (!(adp->f.states & CStatd) || (tdc && !hsame(adp->f.m.DataVersion, tdc->f.versionNo))) { ReleaseWriteLock(&adp->lock); if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } goto tagain; } if (tdc) { /* see if file already exists. If it does, we only set * the size attributes (to handle O_TRUNC) */ code = afs_dir_Lookup(tdc, aname, &newFid.Fid); /* use dnlc first xxx */ if (code == 0) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); ReleaseWriteLock(&adp->lock); #ifdef AFS_SGI64_ENV if (flags & VEXCL) { #else if (aexcl != NONEXCL) { #endif code = EEXIST; /* file exists in excl mode open */ goto done; } /* found the file, so use it */ newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; tvc = NULL; if (newFid.Fid.Unique == 0) { tvc = afs_LookupVCache(&newFid, treq, NULL, adp, aname); } if (!tvc) /* lookup failed or wasn't called */ tvc = afs_GetVCache(&newFid, treq, NULL, NULL); if (tvc) { /* if the thing exists, we need the right access to open it. * we must check that here, since no other checks are * made by the open system call */ len = attrs->va_size; /* only do the truncate */ /* * We used to check always for READ access before; the * problem is that we will fail if the existing file * has mode -w-w-w, which is wrong. */ if ((amode & VREAD) && !afs_AccessOK(tvc, PRSFS_READ, treq, CHECK_MODE_BITS)) { afs_PutVCache(tvc); code = EACCES; goto done; } #if defined(AFS_DARWIN80_ENV) if ((amode & VWRITE) || VATTR_IS_ACTIVE(attrs, va_data_size)) #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) if ((amode & VWRITE) || (attrs->va_mask & AT_SIZE)) #else if ((amode & VWRITE) || len != 0xffffffff) #endif { /* needed for write access check */ tvc->f.parent.vnode = adp->f.fid.Fid.Vnode; tvc->f.parent.unique = adp->f.fid.Fid.Unique; /* need write mode for these guys */ if (!afs_AccessOK (tvc, PRSFS_WRITE, treq, CHECK_MODE_BITS)) { afs_PutVCache(tvc); code = EACCES; goto done; } } #if defined(AFS_DARWIN80_ENV) if (VATTR_IS_ACTIVE(attrs, va_data_size)) #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) if (attrs->va_mask & AT_SIZE) #else if (len != 0xffffffff) #endif { if (vType(tvc) != VREG) { afs_PutVCache(tvc); code = EISDIR; goto done; } /* do a truncate */ #if defined(AFS_DARWIN80_ENV) VATTR_INIT(attrs); VATTR_SET_SUPPORTED(attrs, va_data_size); VATTR_SET_ACTIVE(attrs, va_data_size); #elif defined(UKERNEL) attrs->va_mask = ATTR_SIZE; #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) attrs->va_mask = AT_SIZE; #else VATTR_NULL(attrs); #endif attrs->va_size = len; ObtainWriteLock(&tvc->lock, 136); tvc->f.states |= CCreating; ReleaseWriteLock(&tvc->lock); #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) #if defined(AFS_SGI64_ENV) code = afs_setattr(VNODE_TO_FIRST_BHV((vnode_t *) tvc), attrs, 0, acred); #else code = afs_setattr(tvc, attrs, 0, acred); #endif /* AFS_SGI64_ENV */ #else /* SUN5 || SGI */ code = afs_setattr(tvc, attrs, acred); #endif /* SUN5 || SGI */ ObtainWriteLock(&tvc->lock, 137); tvc->f.states &= ~CCreating; ReleaseWriteLock(&tvc->lock); if (code) { afs_PutVCache(tvc); goto done; } } *avcp = tvc; } else code = ENOENT; /* shouldn't get here */ /* make sure vrefCount bumped only if code == 0 */ goto done; } } /* if we create the file, we don't do any access checks, since * that's how O_CREAT is supposed to work */ if (adp->f.states & CForeign) { origCBs = afs_allCBs; origZaps = afs_allZaps; } else { origCBs = afs_evenCBs; /* if changes, we don't really have a callback */ origZaps = afs_evenZaps; /* number of even numbered vnodes discarded */ } InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP; InStatus.ClientModTime = osi_Time(); InStatus.Group = (afs_int32) afs_cr_gid(acred); if (AFS_NFSXLATORREQ(acred)) { /* * XXX The following is mainly used to fix a bug in the HP-UX * nfs client where they create files with mode of 0 without * doing any setattr later on to fix it. * XXX */ #if defined(AFS_AIX_ENV) if (attrs->va_mode != -1) { #else #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) if (attrs->va_mask & AT_MODE) { #else if (attrs->va_mode != ((unsigned short)-1)) { #endif #endif if (!attrs->va_mode) attrs->va_mode = 0x1b6; /* XXX default mode: rw-rw-rw XXX */ } } if (!AFS_IS_DISCONNECTED) { /* If not disconnected, connect to the server.*/ InStatus.UnixModeBits = attrs->va_mode & 0xffff; /* only care about protection bits */ do { tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn); if (tc) { hostp = tc->srvr->server; /* remember for callback processing */ now = osi_Time(); XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_CREATEFILE); RX_AFS_GUNLOCK(); code = RXAFS_CreateFile(rxconn, (struct AFSFid *)&adp->f.fid.Fid, aname, &InStatus, (struct AFSFid *) &newFid.Fid, OutFidStatus, OutDirStatus, &CallBack, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; CallBack.ExpirationTime += now; } else code = -1; } while (afs_Analyze (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_CREATEFILE, SHARED_LOCK, NULL)); if ((code == EEXIST || code == UAEEXIST) && #ifdef AFS_SGI64_ENV !(flags & VEXCL) #else /* AFS_SGI64_ENV */ aexcl == NONEXCL #endif ) { /* if we get an EEXIST in nonexcl mode, just do a lookup */ if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } ReleaseWriteLock(&adp->lock); #if defined(AFS_SGI64_ENV) code = afs_lookup(VNODE_TO_FIRST_BHV((vnode_t *) adp), aname, avcp, NULL, 0, NULL, acred); #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) code = afs_lookup(adp, aname, avcp, NULL, 0, NULL, acred); #elif defined(UKERNEL) code = afs_lookup(adp, aname, avcp, acred, 0); #elif !defined(AFS_DARWIN_ENV) code = afs_lookup(adp, aname, avcp, acred); #endif goto done; } if (code) { if (code < 0) { ObtainWriteLock(&afs_xcbhash, 488); afs_DequeueCallback(adp); adp->f.states &= ~CStatd; ReleaseWriteLock(&afs_xcbhash); osi_dnlc_purgedp(adp); } ReleaseWriteLock(&adp->lock); if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } goto done; } } else { /* Generate a fake FID for disconnected mode. */ newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; afs_GenFakeFid(&newFid, VREG, 1); } /* if (!AFS_IS_DISCON_RW) */ /* otherwise, we should see if we can make the change to the dir locally */ if (tdc) UpgradeSToWLock(&tdc->lock, 631); if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, OutDirStatus, 1)) { /* we can do it locally */ ObtainWriteLock(&afs_xdcache, 291); code = afs_dir_Create(tdc, aname, &newFid.Fid); ReleaseWriteLock(&afs_xdcache); if (code) { ZapDCE(tdc); DZap(tdc); } } if (tdc) { ReleaseWriteLock(&tdc->lock); afs_PutDCache(tdc); } if (AFS_IS_DISCON_RW) adp->f.m.LinkCount++; newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; ReleaseWriteLock(&adp->lock); volp = afs_FindVolume(&newFid, READ_LOCK); /* New tricky optimistic callback handling algorithm for file creation works * as follows. We create the file essentially with no locks set at all. File * server may thus handle operations from others cache managers as well as from * this very own cache manager that reference the file in question before * we managed to create the cache entry. However, if anyone else changes * any of the status information for a file, we'll see afs_evenCBs increase * (files always have even fids). If someone on this workstation manages * to do something to the file, they'll end up having to create a cache * entry for the new file. Either we'll find it once we've got the afs_xvcache * lock set, or it was also *deleted* the vnode before we got there, in which case * we will find evenZaps has changed, too. Thus, we only assume we have the right * status information if no callbacks or vnode removals have occurred to even * numbered files from the time the call started until the time that we got the xvcache * lock set. Of course, this also assumes that any call that modifies a file first * gets a write lock on the file's vnode, but if that weren't true, the whole cache manager * would fail, since no call would be able to update the local vnode status after modifying * a file on a file server. */ ObtainWriteLock(&afs_xvcache, 138); if (adp->f.states & CForeign) finalZaps = afs_allZaps; /* do this before calling newvcache */ else finalZaps = afs_evenZaps; /* do this before calling newvcache */ /* don't need to call RemoveVCB, since only path leaving a callback is the * one where we pass through afs_NewVCache. Can't have queued a VCB unless * we created and freed an entry between file creation time and here, and the * freeing of the vnode will change evenZaps. Don't need to update the VLRU * queue, since the find will only succeed in the event of a create race, and * then the vcache will be at the front of the VLRU queue anyway... */ if (!(tvc = afs_FindVCache(&newFid, 0, DO_STATS))) { tvc = afs_NewVCache(&newFid, hostp); if (tvc) { int finalCBs; ObtainWriteLock(&tvc->lock, 139); ObtainWriteLock(&afs_xcbhash, 489); finalCBs = afs_evenCBs; /* add the callback in */ if (adp->f.states & CForeign) { tvc->f.states |= CForeign; finalCBs = afs_allCBs; } if (origCBs == finalCBs && origZaps == finalZaps) { tvc->f.states |= CStatd; /* we've fake entire thing, so don't stat */ tvc->f.states &= ~CBulkFetching; if (!AFS_IS_DISCON_RW) { tvc->cbExpires = CallBack.ExpirationTime; afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp); } } else { afs_DequeueCallback(tvc); tvc->f.states &= ~(CStatd | CUnique); tvc->callback = 0; if (tvc->f.fid.Fid.Vnode & 1 || (vType(tvc) == VDIR)) osi_dnlc_purgedp(tvc); } ReleaseWriteLock(&afs_xcbhash); if (AFS_IS_DISCON_RW) { afs_DisconAddDirty(tvc, VDisconCreate, 0); afs_GenDisconStatus(adp, tvc, &newFid, attrs, treq, VREG); } else { afs_ProcessFS(tvc, OutFidStatus, treq); } tvc->f.parent.vnode = adp->f.fid.Fid.Vnode; tvc->f.parent.unique = adp->f.fid.Fid.Unique; #if !defined(UKERNEL) if (volp && (volp->states & VPartVisible)) tvc->f.states |= CPartVisible; #endif ReleaseWriteLock(&tvc->lock); *avcp = tvc; code = 0; } else code = ENOENT; } else { /* otherwise cache entry already exists, someone else must * have created it. Comments used to say: "don't need write * lock to *clear* these flags" but we should do it anyway. * Code used to clear stat bit and callback, but I don't see * the point -- we didn't have a create race, somebody else just * snuck into NewVCache before we got here, probably a racing * lookup. */ *avcp = tvc; code = 0; } ReleaseWriteLock(&afs_xvcache); done: AFS_DISCON_UNLOCK(); done3: if (volp) afs_PutVolume(volp, READ_LOCK); if (code == 0) { if (afs_mariner) afs_AddMarinerName(aname, *avcp); /* return the new status in vattr */ afs_CopyOutAttrs(*avcp, attrs); if (afs_mariner) afs_MarinerLog("store$Creating", *avcp); } afs_PutFakeStat(&fakestate); code = afs_CheckCode(code, treq, 20); afs_DestroyReq(treq); done2: osi_FreeSmallSpace(OutFidStatus); osi_FreeSmallSpace(OutDirStatus); return code; } /* * Check to see if we can track the change locally: requires that * we have sufficiently recent info in data cache. If so, we * know the new DataVersion number, and place it correctly in both the * data and stat cache entries. This routine returns 1 if we should * do the operation locally, and 0 otherwise. * * This routine must be called with the stat cache entry write-locked, * and dcache entry write-locked. */ int afs_LocalHero(struct vcache *avc, struct dcache *adc, AFSFetchStatus * astat, int aincr) { afs_int32 ok; afs_hyper_t avers; AFS_STATCNT(afs_LocalHero); hset64(avers, astat->dataVersionHigh, astat->DataVersion); /* avers *is* the version number now, no matter what */ if (adc) { /* does what's in the dcache *now* match what's in the vcache *now*, * and do we have a valid callback? if not, our local copy is not "ok" */ ok = (hsame(avc->f.m.DataVersion, adc->f.versionNo) && avc->callback && (avc->f.states & CStatd) && avc->cbExpires >= osi_Time()); } else { ok = 0; } if (ok) { /* check that the DV on the server is what we expect it to be */ afs_hyper_t newDV; hset(newDV, adc->f.versionNo); hadd32(newDV, aincr); if (!hsame(avers, newDV)) { ok = 0; } } #if defined(AFS_SGI_ENV) osi_Assert(avc->v.v_type == VDIR); #endif /* The bulk status code used the length as a sequence number. */ /* Don't update the vcache entry unless the stats are current. */ if (avc->f.states & CStatd) { hset(avc->f.m.DataVersion, avers); #ifdef AFS_64BIT_CLIENT FillInt64(avc->f.m.Length, astat->Length_hi, astat->Length); #else /* AFS_64BIT_CLIENT */ avc->f.m.Length = astat->Length; #endif /* AFS_64BIT_CLIENT */ avc->f.m.Date = astat->ClientModTime; } if (ok) { /* we've been tracking things correctly */ adc->dflags |= DFEntryMod; adc->f.versionNo = avers; return 1; } else { if (adc) { ZapDCE(adc); DZap(adc); } if (avc->f.states & CStatd) { osi_dnlc_purgedp(avc); } return 0; } }
/** * @param aname Volume name. * @param acell Cell id. * @param agood * @param areq Request type. * @param locktype Type of lock to be used. * @return Volume or NULL if failure. */ static struct volume * afs_NewVolumeByName(char *aname, afs_int32 acell, int agood, struct vrequest *areq, afs_int32 locktype) { afs_int32 code, type = 0; struct volume *tv, *tv1; struct vldbentry *tve; struct nvldbentry *ntve; struct uvldbentry *utve; struct cell *tcell; char *tbuffer, *ve; struct afs_conn *tconn; struct vrequest *treq = NULL; struct rx_connection *rxconn; if (strlen(aname) > VL_MAXNAMELEN) /* Invalid volume name */ return NULL; tcell = afs_GetCell(acell, READ_LOCK); if (!tcell) { return NULL; } code = afs_CreateReq(&treq, afs_osi_credp); /* *must* be unauth for vldb */ if (code) { return NULL; } /* allow null request if we don't care about ENODEV/ETIMEDOUT distinction */ if (!areq) areq = treq; afs_Trace2(afs_iclSetp, CM_TRACE_GETVOL, ICL_TYPE_STRING, aname, ICL_TYPE_POINTER, aname); tbuffer = osi_AllocLargeSpace(AFS_LRALLOCSIZ); tve = (struct vldbentry *)(tbuffer + 1024); ntve = (struct nvldbentry *)tve; utve = (struct uvldbentry *)tve; do { tconn = afs_ConnByMHosts(tcell->cellHosts, tcell->vlport, tcell->cellNum, treq, SHARED_LOCK, 0, &rxconn); if (tconn) { if (tconn->srvr->server->flags & SNO_LHOSTS) { type = 0; RX_AFS_GUNLOCK(); code = VL_GetEntryByNameO(rxconn, aname, tve); RX_AFS_GLOCK(); } else if (tconn->srvr->server->flags & SYES_LHOSTS) { type = 1; RX_AFS_GUNLOCK(); code = VL_GetEntryByNameN(rxconn, aname, ntve); RX_AFS_GLOCK(); } else { type = 2; RX_AFS_GUNLOCK(); code = VL_GetEntryByNameU(rxconn, aname, utve); RX_AFS_GLOCK(); if (!(tconn->srvr->server->flags & SVLSRV_UUID)) { if (code == RXGEN_OPCODE) { type = 1; RX_AFS_GUNLOCK(); code = VL_GetEntryByNameN(rxconn, aname, ntve); RX_AFS_GLOCK(); if (code == RXGEN_OPCODE) { type = 0; tconn->srvr->server->flags |= SNO_LHOSTS; RX_AFS_GUNLOCK(); code = VL_GetEntryByNameO(rxconn, aname, tve); RX_AFS_GLOCK(); } else if (!code) tconn->srvr->server->flags |= SYES_LHOSTS; } else if (!code) tconn->srvr->server->flags |= SVLSRV_UUID; } lastnvcode = code; } } else code = -1; } while (afs_Analyze(tconn, rxconn, code, NULL, treq, -1, /* no op code for this */ SHARED_LOCK, tcell)); if (code) { /* If the client has yet to contact this cell and contact failed due * to network errors, mark the VLDB servers as back up. * That the client tried and failed can be determined from the * fact that there was a downtime incident, but CHasVolRef is not set. */ /* RT 48959 - unclear if this should really go */ #if 0 if (areq->networkError && !(tcell->states & CHasVolRef)) { int i; struct server *sp; struct srvAddr *sap; for (i = 0; i < AFS_MAXCELLHOSTS; i++) { if ((sp = tcell->cellHosts[i]) == NULL) break; for (sap = sp->addr; sap; sap = sap->next_sa) afs_MarkServerUpOrDown(sap, 0); } } #endif afs_CopyError(treq, areq); osi_FreeLargeSpace(tbuffer); afs_PutCell(tcell, READ_LOCK); afs_DestroyReq(treq); return NULL; } /* * Check to see if this cell has not yet referenced a volume. If * it hasn't, it's just about to change its status, and we need to mark * this fact down. Note that it is remotely possible that afs_SetupVolume * could fail and we would still not have a volume reference. */ if (!(tcell->states & CHasVolRef)) { tcell->states |= CHasVolRef; afs_stats_cmperf.numCellsContacted++; } /*First time a volume in this cell has been referenced */ if (type == 2) ve = (char *)utve; else if (type == 1) ve = (char *)ntve; else ve = (char *)tve; tv = afs_SetupVolume(0, aname, ve, tcell, agood, type, treq); if ((agood == 3) && tv && tv->backVol) { /* * This means that very soon we'll ask for the BK volume so * we'll prefetch it (well we did already.) */ tv1 = afs_SetupVolume(tv->backVol, (char *)0, ve, tcell, 0, type, treq); if (tv1) { tv1->refCount--; } } if ((agood >= 2) && tv && tv->roVol) { /* * This means that very soon we'll ask for the RO volume so * we'll prefetch it (well we did already.) */ tv1 = afs_SetupVolume(tv->roVol, NULL, ve, tcell, 0, type, treq); if (tv1) { tv1->refCount--; } } osi_FreeLargeSpace(tbuffer); afs_PutCell(tcell, READ_LOCK); afs_DestroyReq(treq); return tv; } /*afs_NewVolumeByName */