static int UnEvalFakeStat(struct vrequest *areq, struct vcache **vcpp) { struct VenusFid tfid; struct volume *tvp; struct vcache *tvc; int code; if (!afs_fakestat_enable) return 0; if (*vcpp == afs_globalVp || vType(*vcpp) != VDIR || (*vcpp)->mvstat != 2) return 0; /* Figure out what FID to look for */ tvp = afs_GetVolume(&(*vcpp)->f.fid, 0, READ_LOCK); if (!tvp) { #ifdef OSI_EXPORT_DEBUG printk("afs: UnEvalFakeStat(0x%08x/%d/%d.%d): no volume\n", (*vcpp)->f.fid.Cell, (*vcpp)->f.fid.Fid.Volume, (*vcpp)->f.fid.Fid.Vnode, (*vcpp)->f.fid.Fid.Unique); #endif return ENOENT; } tfid = tvp->mtpoint; afs_PutVolume(tvp, READ_LOCK); tvc = afs_GetVCache(&tfid, areq, NULL, NULL); if (!tvc) { #ifdef OSI_EXPORT_DEBUG printk("afs: UnEvalFakeStat(0x%08x/%d/%d.%d): GetVCache(0x%08x/%d/%d.%d) failed\n", (*vcpp)->f.fid.Cell, (*vcpp)->f.fid.Fid.Volume, (*vcpp)->f.fid.Fid.Vnode, (*vcpp)->f.fid.Fid.Unique, tfid.Cell, tfid.Fid.Volume, tfid.Fid.Vnode, tfid.Fid.Unique); #endif return ENOENT; } if (afs_fakestat_enable == 2) { ObtainWriteLock(&tvc->lock, 806); code = afs_HandleLink(tvc, areq); if (code) { ReleaseWriteLock(&tvc->lock); afs_PutVCache(tvc); return code; } if (!strchr(tvc->linkData, ':')) { ReleaseWriteLock(&tvc->lock); afs_PutVCache(tvc); return 0; } ReleaseWriteLock(&tvc->lock); } afs_PutVCache(*vcpp); *vcpp = tvc; return 0; }
/* * Invalidate the /afs vnode for dynroot; called when the underlying * directory has changed and needs to be re-read. */ void afs_DynrootInvalidate(void) { afs_int32 retry; struct vcache *tvc; struct VenusFid tfid; if (!afs_dynrootEnable) return; ObtainWriteLock(&afs_dynrootDirLock, 687); afs_dynrootVersion++; afs_dynrootVersionHigh = osi_Time(); ReleaseWriteLock(&afs_dynrootDirLock); afs_GetDynrootFid(&tfid); do { retry = 0; ObtainReadLock(&afs_xvcache); tvc = afs_FindVCache(&tfid, &retry, 0); ReleaseReadLock(&afs_xvcache); } while (retry); if (tvc) { tvc->f.states &= ~(CStatd | CUnique); osi_dnlc_purgedp(tvc); afs_PutVCache(tvc); } }
/*! * Generate a fake fid for a disconnected shadow dir. * Similar to afs_GenFakeFid, only that it uses the dhash * to search for a uniquifier because a shadow dir lives only * in the dcache. * * \param afid * * \note Don't forget to fill in afid with Cell and Volume. */ void afs_GenShadowFid(struct VenusFid *afid) { afs_uint32 i, index, max_unique = 1; struct vcache *tvc = NULL; /* Try generating a fid that isn't used in the vhash. */ do { /* Shadow Fids are always directories */ afid->Fid.Vnode = afs_DisconVnode + 1; i = DVHash(afid); ObtainWriteLock(&afs_xdcache, 737); for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) { i = afs_dvnextTbl[index]; if (afs_indexUnique[index] > max_unique) max_unique = afs_indexUnique[index]; } ReleaseWriteLock(&afs_xdcache); afid->Fid.Unique = max_unique + 1; afs_DisconVnode += 2; if (!afs_DisconVnode) afs_DisconVnode = 2; /* Is this a used vnode? */ ObtainSharedLock(&afs_xvcache, 762); tvc = afs_FindVCache(afid, 0, 1); ReleaseSharedLock(&afs_xvcache); if (tvc) afs_PutVCache(tvc); } while (tvc); }
/* 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; }
/*! * Changes a file's parent fid references. */ static int fix_children_fids_hook(void *hdata, char *aname, afs_int32 vnode, afs_int32 unique) { struct VenusFid tfid; struct VenusFid *afid = (struct VenusFid *) hdata; struct vcache *tvc; struct dcache *tdc = NULL; if ((aname[0] == '.') && !aname[1]) return 0; if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) return 0; tfid.Cell = afid->Cell; tfid.Fid.Volume = afid->Fid.Volume; tfid.Fid.Vnode = vnode; tfid.Fid.Unique = unique; if (!(vnode % 2)) { /* vnode's parity indicates that it's a file. */ /* Get the vcache. */ ObtainSharedLock(&afs_xvcache, 759); tvc = afs_FindVCache(&tfid, 0, 1); ReleaseSharedLock(&afs_xvcache); /* Change the fields. */ if (tvc) { tvc->f.parent.vnode = afid->Fid.Vnode; tvc->f.parent.unique = afid->Fid.Unique; afs_PutVCache(tvc); } } else { /* It's a dir. Fix this dir's .. entry to contain the new fid. */ /* Seek the dir's dcache. */ tdc = afs_FindDCacheByFid(&tfid); if (tdc) { /* Change the .. entry fid. */ afs_dir_ChangeFid(tdc, "..", NULL, &afid->Fid.Vnode); afs_PutDCache(tdc); } } /* if (!(vnode % 2))*/ return 0; }
/* 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 vrequest treq; struct vcache *tvp = 0; AFS_STATCNT(afs_root); if (afs_globalVp && (afs_globalVp->f.states & CStatd)) { tvp = afs_globalVp; } else { cred_t *credp = crref(); if (afs_globalVp) { afs_PutVCache(afs_globalVp); afs_globalVp = NULL; } if (!(code = afs_InitReq(&treq, credp)) && !(code = afs_CheckInit())) { tvp = afs_GetVCache(&afs_rootFid, &treq, NULL, NULL); if (tvp) { struct inode *ip = AFSTOV(tvp); struct vattr vattr; afs_getattr(tvp, &vattr, credp); afs_fill_inode(ip, &vattr); /* setup super_block and mount point inode. */ afs_globalVp = tvp; #if defined(AFS_LINUX24_ENV) afsp->s_root = d_alloc_root(ip); #else afsp->s_root = d_alloc_root(ip, NULL); #endif afsp->s_root->d_op = &afs_dentry_operations; } else code = ENOENT; } crfree(credp); } afs_Trace2(afs_iclSetp, CM_TRACE_VFSROOT, ICL_TYPE_POINTER, afs_globalVp, ICL_TYPE_INT32, code); return code; }
afs_unmount(struct mount *mp, int flags, struct thread *p) #endif { int error = 0; AFS_GLOCK(); if (afs_globalVp && ((flags & MNT_FORCE) || !VREFCOUNT_GT(afs_globalVp, 1))) { /* Put back afs_root's ref */ struct vcache *gvp = afs_globalVp; afs_globalVp = NULL; afs_PutVCache(gvp); } if (afs_globalVp) error = EBUSY; AFS_GUNLOCK(); /* * Release any remaining vnodes on this mount point. * The `1' means that we hold one extra reference on * the root vnode (this is just a guess right now). * This has to be done outside the global lock. */ if (!error) { #if defined(AFS_FBSD80_ENV) error = vflush(mp, 1, (flags & MNT_FORCE) ? FORCECLOSE : 0, curthread); #elif defined(AFS_FBSD53_ENV) error = vflush(mp, 1, (flags & MNT_FORCE) ? FORCECLOSE : 0, p); #else error = vflush(mp, 1, (flags & MNT_FORCE) ? FORCECLOSE : 0); #endif } if (error) goto out; AFS_GLOCK(); AFS_STATCNT(afs_unmount); afs_globalVFS = 0; afs_shutdown(); AFS_GUNLOCK(); out: return error; }
int afs_GetParentVCache(struct vcache *avc, int deleted, struct VenusFid *afid, char *aname, struct vcache **adp) { int code; *adp = NULL; if (afs_GetParentDirFid(avc, afid)) { /* printf("afs_GetParentVCache: Couldn't find parent dir's FID.\n"); */ return ENOENT; } code = afs_GetVnodeName(avc, afid, aname, deleted); if (code) { /* printf("afs_GetParentVCache: Couldn't find file name\n"); */ goto end; } ObtainSharedLock(&afs_xvcache, 766); *adp = afs_FindVCache(afid, 0, 1); ReleaseSharedLock(&afs_xvcache); if (!*adp) { /* printf("afs_GetParentVCache: Couldn't find parent dir's vcache\n"); */ code = ENOENT; goto end; } if ((*adp)->f.ddirty_flags & VDisconCreate) { /* printf("afs_GetParentVCache: deferring until parent exists\n"); */ code = EAGAIN; goto end; } end: if (code && *adp) { afs_PutVCache(*adp); *adp = NULL; } return code; }
/*! * Lookup dirty deleted vnodes in this dir. */ static int chk_del_children_hook(void *hdata, char *aname, afs_int32 vnode, afs_int32 unique) { struct VenusFid tfid; struct DirtyChildrenCount *v = (struct DirtyChildrenCount *) hdata; struct vcache *tvc; if ((aname[0] == '.') && !aname[1]) /* Skip processing this dir again. * It would result in an endless loop. */ return 0; if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) /* Don't process parent dir. */ return 0; /* Get this file's vcache. */ tfid.Cell = v->vc->f.fid.Cell; tfid.Fid.Volume = v->vc->f.fid.Fid.Volume; tfid.Fid.Vnode = vnode; tfid.Fid.Unique = unique; ObtainSharedLock(&afs_xvcache, 757); tvc = afs_FindVCache(&tfid, 0, 1); ReleaseSharedLock(&afs_xvcache); /* Count unfinished dirty children. */ if (tvc) { ObtainReadLock(&tvc->lock); if (tvc->f.ddirty_flags) v->count++; ReleaseReadLock(&tvc->lock); afs_PutVCache(tvc); } return 0; }
int afs_root(OSI_VFS_DECL(afsp), struct vnode **avpp) { afs_int32 code = 0; struct vrequest treq; struct vcache *tvp = 0; OSI_VFS_CONVERT(afsp); AFS_STATCNT(afs_root); if (afs_globalVp && (afs_globalVp->f.states & CStatd)) { tvp = afs_globalVp; } else { if (afs_globalVp) { afs_PutVCache(afs_globalVp); afs_globalVp = NULL; } if (!(code = afs_InitReq(&treq, get_user_struct()->u_cred)) && !(code = afs_CheckInit())) { tvp = afs_GetVCache(&afs_rootFid, &treq, NULL, NULL); /* we really want this to stay around */ if (tvp) { afs_globalVp = tvp; } else code = ENOENT; } } if (tvp) { VN_HOLD(AFSTOV(tvp)); AFSTOV(tvp)->v_flag |= VROOT; /* No-op on Ultrix 2.2 */ afs_globalVFS = afsp; *avpp = AFSTOV(tvp); } afs_Trace3(afs_iclSetp, CM_TRACE_GOPEN, ICL_TYPE_POINTER, *avpp, ICL_TYPE_INT32, 0, ICL_TYPE_INT32, code); return code; }
/*! * This function throws away the whole disconnected state, allowing * the cache manager to reconnect to a server if we get into a state * where reconiliation is impossible. * * \param acred * */ void afs_DisconDiscardAll(afs_ucred_t *acred) { struct vcache *tvc; ObtainWriteLock(&afs_disconDirtyLock, 717); while (!QEmpty(&afs_disconDirty)) { tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq); QRemove(&tvc->dirtyq); ReleaseWriteLock(&afs_disconDirtyLock); ObtainWriteLock(&tvc->lock, 718); afs_ResetVCache(tvc, acred, 0); tvc->f.truncPos = AFS_NOTRUNC; ReleaseWriteLock(&tvc->lock); ObtainWriteLock(&afs_disconDirtyLock, 719); afs_PutVCache(tvc); } afs_DisconDiscardAllShadows(1, acred); ReleaseWriteLock(&afs_disconDirtyLock); }
int afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, char *aname2, struct AFS_UCRED *acred, struct vrequest *areq) { register struct afs_conn *tc; register afs_int32 code = 0; afs_int32 returnCode; int oneDir, doLocally; afs_size_t offset, len; struct VenusFid unlinkFid, fileFid; struct vcache *tvc; struct dcache *tdc1, *tdc2; struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus; struct AFSVolSync tsync; XSTATS_DECLS; AFS_STATCNT(afs_rename); afs_Trace4(afs_iclSetp, CM_TRACE_RENAME, ICL_TYPE_POINTER, aodp, ICL_TYPE_STRING, aname1, ICL_TYPE_POINTER, andp, ICL_TYPE_STRING, aname2); if (strlen(aname1) > AFSNAMEMAX || strlen(aname2) > AFSNAMEMAX) { code = ENAMETOOLONG; goto done; } /* verify the latest versions of the stat cache entries */ tagain: code = afs_VerifyVCache(aodp, areq); if (code) goto done; code = afs_VerifyVCache(andp, areq); if (code) goto done; /* lock in appropriate order, after some checks */ if (aodp->f.fid.Cell != andp->f.fid.Cell || aodp->f.fid.Fid.Volume != andp->f.fid.Fid.Volume) { code = EXDEV; goto done; } oneDir = 0; code = 0; if (andp->f.fid.Fid.Vnode == aodp->f.fid.Fid.Vnode) { if (!strcmp(aname1, aname2)) { /* Same directory and same name; this is a noop and just return success * to save cycles and follow posix standards */ code = 0; goto done; } if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { code = ENETDOWN; goto done; } ObtainWriteLock(&andp->lock, 147); tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, areq, &offset, &len, 0); if (!tdc1) { code = ENOENT; } else { ObtainWriteLock(&tdc1->lock, 643); } tdc2 = tdc1; oneDir = 1; /* only one dude locked */ } else if ((andp->f.states & CRO) || (aodp->f.states & CRO)) { code = EROFS; goto done; } else if (andp->f.fid.Fid.Vnode < aodp->f.fid.Fid.Vnode) { ObtainWriteLock(&andp->lock, 148); /* lock smaller one first */ ObtainWriteLock(&aodp->lock, 149); tdc2 = afs_FindDCache(andp, (afs_size_t) 0); if (tdc2) ObtainWriteLock(&tdc2->lock, 644); tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, areq, &offset, &len, 0); if (tdc1) ObtainWriteLock(&tdc1->lock, 645); else code = ENOENT; } else { ObtainWriteLock(&aodp->lock, 150); /* lock smaller one first */ ObtainWriteLock(&andp->lock, 557); tdc1 = afs_GetDCache(aodp, (afs_size_t) 0, areq, &offset, &len, 0); if (tdc1) ObtainWriteLock(&tdc1->lock, 646); else code = ENOENT; tdc2 = afs_FindDCache(andp, (afs_size_t) 0); if (tdc2) ObtainWriteLock(&tdc2->lock, 647); } osi_dnlc_remove(aodp, aname1, 0); osi_dnlc_remove(andp, aname2, 0); /* * 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 (tdc1) { if (!(aodp->f.states & CStatd) || !hsame(aodp->f.m.DataVersion, tdc1->f.versionNo)) { ReleaseWriteLock(&aodp->lock); if (!oneDir) { if (tdc2) { ReleaseWriteLock(&tdc2->lock); afs_PutDCache(tdc2); } ReleaseWriteLock(&andp->lock); } ReleaseWriteLock(&tdc1->lock); afs_PutDCache(tdc1); goto tagain; } } if (code == 0) code = afs_dir_Lookup(tdc1, aname1, &fileFid.Fid); if (code) { if (tdc1) { ReleaseWriteLock(&tdc1->lock); afs_PutDCache(tdc1); } ReleaseWriteLock(&aodp->lock); if (!oneDir) { if (tdc2) { ReleaseWriteLock(&tdc2->lock); afs_PutDCache(tdc2); } ReleaseWriteLock(&andp->lock); } goto done; } if (!AFS_IS_DISCON_RW) { /* Connected. */ do { tc = afs_Conn(&aodp->f.fid, areq, SHARED_LOCK); if (tc) { XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME); RX_AFS_GUNLOCK(); code = RXAFS_Rename(tc->id, (struct AFSFid *)&aodp->f.fid.Fid, aname1, (struct AFSFid *)&andp->f.fid.Fid, aname2, &OutOldDirStatus, &OutNewDirStatus, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; } else code = -1; } while (afs_Analyze (tc, code, &andp->f.fid, areq, AFS_STATS_FS_RPCIDX_RENAME, SHARED_LOCK, NULL)); } else { #if defined(AFS_DISCON_ENV) /* Disconnected. */ /* Seek moved file vcache. */ fileFid.Cell = aodp->f.fid.Cell; fileFid.Fid.Volume = aodp->f.fid.Fid.Volume; ObtainSharedLock(&afs_xvcache, 754); tvc = afs_FindVCache(&fileFid, 0 , 1); ReleaseSharedLock(&afs_xvcache); if (tvc) { /* XXX - We're locking this vcache whilst holding dcaches. Ooops */ ObtainWriteLock(&tvc->lock, 750); if (!(tvc->f.ddirty_flags & (VDisconRename|VDisconCreate))) { /* If the vnode was created locally, then we don't care * about recording the rename - we'll do it automatically * on replay. If we've already renamed, we've already stored * the required information about where we came from. */ if (!aodp->f.shadow.vnode) { /* Make shadow copy of parent dir only. */ afs_MakeShadowDir(aodp, tdc1); } /* Save old parent dir fid so it will be searchable * in the shadow dir. */ tvc->f.oldParent.vnode = aodp->f.fid.Fid.Vnode; tvc->f.oldParent.unique = aodp->f.fid.Fid.Unique; afs_DisconAddDirty(tvc, VDisconRename | (oneDir ? VDisconRenameSameDir:0), 1); } ReleaseWriteLock(&tvc->lock); afs_PutVCache(tvc); } else { code = ENOENT; } /* if (tvc) */ #endif } /* if !(AFS_IS_DISCON_RW)*/ returnCode = code; /* remember for later */ /* Now we try to do things locally. This is really loathsome code. */ unlinkFid.Fid.Vnode = 0; if (code == 0) { /* In any event, we don't really care if the data (tdc2) is not * in the cache; if it isn't, we won't do the update locally. */ /* see if version numbers increased properly */ doLocally = 1; if (!AFS_IS_DISCON_RW) { if (oneDir) { /* number increases by 1 for whole rename operation */ if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1)) { doLocally = 0; } } else { /* two separate dirs, each increasing by 1 */ if (!afs_LocalHero(aodp, tdc1, &OutOldDirStatus, 1)) doLocally = 0; if (!afs_LocalHero(andp, tdc2, &OutNewDirStatus, 1)) doLocally = 0; if (!doLocally) { if (tdc1) { ZapDCE(tdc1); DZap(tdc1); } if (tdc2) { ZapDCE(tdc2); DZap(tdc2); } } } } /* if (!AFS_IS_DISCON_RW) */ /* now really do the work */ if (doLocally) { /* first lookup the fid of the dude we're moving */ code = afs_dir_Lookup(tdc1, aname1, &fileFid.Fid); if (code == 0) { /* delete the source */ code = afs_dir_Delete(tdc1, aname1); } /* first see if target is there */ if (code == 0 && afs_dir_Lookup(tdc2, aname2, &unlinkFid.Fid) == 0) { /* target already exists, and will be unlinked by server */ code = afs_dir_Delete(tdc2, aname2); } if (code == 0) { ObtainWriteLock(&afs_xdcache, 292); code = afs_dir_Create(tdc2, aname2, &fileFid.Fid); ReleaseWriteLock(&afs_xdcache); } if (code != 0) { ZapDCE(tdc1); DZap(tdc1); if (!oneDir) { ZapDCE(tdc2); DZap(tdc2); } } } /* update dir link counts */ if (AFS_IS_DISCON_RW) { if (!oneDir) { aodp->f.m.LinkCount--; andp->f.m.LinkCount++; } /* If we're in the same directory, link count doesn't change */ } else { aodp->f.m.LinkCount = OutOldDirStatus.LinkCount; if (!oneDir) andp->f.m.LinkCount = OutNewDirStatus.LinkCount; } } else { /* operation failed (code != 0) */ if (code < 0) { /* if failed, server might have done something anyway, and * assume that we know about it */ ObtainWriteLock(&afs_xcbhash, 498); afs_DequeueCallback(aodp); afs_DequeueCallback(andp); andp->f.states &= ~CStatd; aodp->f.states &= ~CStatd; ReleaseWriteLock(&afs_xcbhash); osi_dnlc_purgedp(andp); osi_dnlc_purgedp(aodp); } } /* release locks */ if (tdc1) { ReleaseWriteLock(&tdc1->lock); afs_PutDCache(tdc1); } if ((!oneDir) && tdc2) { ReleaseWriteLock(&tdc2->lock); afs_PutDCache(tdc2); } ReleaseWriteLock(&aodp->lock); if (!oneDir) { ReleaseWriteLock(&andp->lock); } if (returnCode) { code = returnCode; goto done; } /* now, some more details. if unlinkFid.Fid.Vnode then we should decrement * the link count on this file. Note that if fileFid is a dir, then we don't * have to invalidate its ".." entry, since its DataVersion # should have * changed. However, interface is not good enough to tell us the * *file*'s new DataVersion, so we're stuck. Our hack: delete mark * the data as having an "unknown" version (effectively discarding the ".." * entry */ if (unlinkFid.Fid.Vnode) { unlinkFid.Fid.Volume = aodp->f.fid.Fid.Volume; unlinkFid.Cell = aodp->f.fid.Cell; tvc = NULL; if (!unlinkFid.Fid.Unique) { tvc = afs_LookupVCache(&unlinkFid, areq, NULL, aodp, aname1); } if (!tvc) /* lookup failed or wasn't called */ tvc = afs_GetVCache(&unlinkFid, areq, NULL, NULL); if (tvc) { #ifdef AFS_BOZONLOCK_ENV afs_BozonLock(&tvc->pvnLock, tvc); /* Since afs_TryToSmush will do a pvn_vptrunc */ #endif ObtainWriteLock(&tvc->lock, 151); tvc->f.m.LinkCount--; tvc->f.states &= ~CUnique; /* For the dfs xlator */ if (tvc->f.m.LinkCount == 0 && !osi_Active(tvc)) { /* if this was last guy (probably) discard from cache. * We have to be careful to not get rid of the stat * information, since otherwise operations will start * failing even if the file was still open (or * otherwise active), and the server no longer has the * info. If the file still has valid links, we'll get * a break-callback msg from the server, so it doesn't * matter that we don't discard the status info */ if (!AFS_NFSXLATORREQ(acred)) afs_TryToSmush(tvc, acred, 0); } ReleaseWriteLock(&tvc->lock); #ifdef AFS_BOZONLOCK_ENV afs_BozonUnlock(&tvc->pvnLock, tvc); #endif afs_PutVCache(tvc); } } /* now handle ".." invalidation */ if (!oneDir) { fileFid.Fid.Volume = aodp->f.fid.Fid.Volume; fileFid.Cell = aodp->f.fid.Cell; if (!fileFid.Fid.Unique) tvc = afs_LookupVCache(&fileFid, areq, NULL, andp, aname2); else tvc = afs_GetVCache(&fileFid, areq, NULL, (struct vcache *)0); if (tvc && (vType(tvc) == VDIR)) { ObtainWriteLock(&tvc->lock, 152); tdc1 = afs_FindDCache(tvc, (afs_size_t) 0); if (tdc1) { if (AFS_IS_DISCON_RW) { #if defined(AFS_DISCON_ENV) /* If disconnected, we need to fix (not discard) the "..".*/ afs_dir_ChangeFid(tdc1, "..", &aodp->f.fid.Fid.Vnode, &andp->f.fid.Fid.Vnode); #endif } else { ObtainWriteLock(&tdc1->lock, 648); ZapDCE(tdc1); /* mark as unknown */ DZap(tdc1); ReleaseWriteLock(&tdc1->lock); afs_PutDCache(tdc1); /* put it back */ } } osi_dnlc_remove(tvc, "..", 0); ReleaseWriteLock(&tvc->lock); afs_PutVCache(tvc); } else if (AFS_IS_DISCON_RW && tvc && (vType(tvc) == VREG)) { /* XXX - Should tvc not get locked here? */ tvc->f.parent.vnode = andp->f.fid.Fid.Vnode; tvc->f.parent.unique = andp->f.fid.Fid.Unique; } else if (tvc) { /* True we shouldn't come here since tvc SHOULD be a dir, but we * 'syntactically' need to unless we change the 'if' above... */ afs_PutVCache(tvc); } } code = returnCode; done: return code; }
int afsremove(register struct vcache *adp, register struct dcache *tdc, register struct vcache *tvc, char *aname, afs_ucred_t *acred, struct vrequest *treqp) { register afs_int32 code = 0; register struct afs_conn *tc; struct AFSFetchStatus OutDirStatus; struct AFSVolSync tsync; XSTATS_DECLS; if (!AFS_IS_DISCONNECTED) { do { tc = afs_Conn(&adp->f.fid, treqp, SHARED_LOCK); if (tc) { XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE); RX_AFS_GUNLOCK(); code = RXAFS_RemoveFile(tc->id, (struct AFSFid *)&adp->f.fid.Fid, aname, &OutDirStatus, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; } else code = -1; } while (afs_Analyze (tc, code, &adp->f.fid, treqp, AFS_STATS_FS_RPCIDX_REMOVEFILE, SHARED_LOCK, NULL)); } osi_dnlc_remove(adp, aname, tvc); if (code) { if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } if (tvc) afs_PutVCache(tvc); if (code < 0) { ObtainWriteLock(&afs_xcbhash, 497); afs_DequeueCallback(adp); adp->f.states &= ~CStatd; ReleaseWriteLock(&afs_xcbhash); osi_dnlc_purgedp(adp); } ReleaseWriteLock(&adp->lock); code = afs_CheckCode(code, treqp, 21); return code; } if (tdc) UpgradeSToWLock(&tdc->lock, 637); 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 */ } ReleaseWriteLock(&adp->lock); /* now, get vnode for unlinked dude, and see if we should force it * from cache. adp is now the deleted files vnode. Note that we * call FindVCache instead of GetVCache since if the file's really * gone, we won't be able to fetch the status info anyway. */ if (tvc) { afs_MarinerLog("store$Removing", tvc); #ifdef AFS_BOZONLOCK_ENV afs_BozonLock(&tvc->pvnLock, tvc); /* Since afs_TryToSmush will do a pvn_vptrunc */ #endif ObtainWriteLock(&tvc->lock, 141); /* note that callback will be broken on the deleted file if there are * still >0 links left to it, so we'll get the stat right */ tvc->f.m.LinkCount--; tvc->f.states &= ~CUnique; /* For the dfs xlator */ if (tvc->f.m.LinkCount == 0 && !osi_Active(tvc)) { if (!AFS_NFSXLATORREQ(acred)) afs_TryToSmush(tvc, acred, 0); } ReleaseWriteLock(&tvc->lock); #ifdef AFS_BOZONLOCK_ENV afs_BozonUnlock(&tvc->pvnLock, tvc); #endif afs_PutVCache(tvc); } return (0); }
/* 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; register struct dcache *tdc; struct VenusFid unlinkFid; register afs_int32 code; register 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_InitReq(&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 = ENOENT; 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) { struct VenusFid *oldmvid = NULL; if (tvc->mvid) oldmvid = tvc->mvid; tvc->mvid = (struct VenusFid *)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); #ifndef AFS_DARWIN80_ENV /* 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(); 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(register struct vcache *avc, register int doit) { afs_ucred_t *cred; char *unlname; struct vcache *adp; struct vrequest treq; struct VenusFid dirFid; register 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 && (doit || (avc->f.states & CUnlinkedDel))) { if ((code = afs_InitReq(&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 = (char *)avc->mvid; avc->mvid = 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); } } else { #if defined(AFS_DARWIN80_ENV) vnode_put(AFSTOV(avc)); #endif ReleaseWriteLock(&avc->lock); } return code; }
afs_root(struct mount *mp, struct vnode **vpp) #endif { int error; struct vrequest treq; register struct vcache *tvp = 0; #ifdef AFS_FBSD50_ENV #ifndef AFS_FBSD53_ENV struct thread *td = curthread; #endif struct ucred *cr = td->td_ucred; #else struct proc *p = curproc; struct ucred *cr = p->p_cred->pc_ucred; #endif AFS_GLOCK(); AFS_STATCNT(afs_root); crhold(cr); if (afs_globalVp && (afs_globalVp->f.states & CStatd)) { tvp = afs_globalVp; error = 0; } else { tryagain: #ifndef AFS_FBSD80_ENV if (afs_globalVp) { afs_PutVCache(afs_globalVp); /* vrele() needed here or not? */ afs_globalVp = NULL; } #endif if (!(error = afs_InitReq(&treq, cr)) && !(error = afs_CheckInit())) { tvp = afs_GetVCache(&afs_rootFid, &treq, NULL, NULL); /* we really want this to stay around */ if (tvp) afs_globalVp = tvp; else error = ENOENT; } } if (tvp) { struct vnode *vp = AFSTOV(tvp); #ifdef AFS_FBSD50_ENV ASSERT_VI_UNLOCKED(vp, "afs_root"); #endif AFS_GUNLOCK(); /* * I'm uncomfortable about this. Shouldn't this happen at a * higher level, and shouldn't we busy the top-level directory * to prevent recycling? */ #ifdef AFS_FBSD50_ENV error = vget(vp, LK_EXCLUSIVE | LK_RETRY, td); vp->v_vflag |= VV_ROOT; #else error = vget(vp, LK_EXCLUSIVE | LK_RETRY, p); vp->v_flag |= VROOT; #endif AFS_GLOCK(); if (error != 0) goto tryagain; afs_globalVFS = mp; *vpp = vp; } afs_Trace2(afs_iclSetp, CM_TRACE_VFSROOT, ICL_TYPE_POINTER, tvp ? AFSTOV(tvp) : NULL, ICL_TYPE_INT32, error); AFS_GUNLOCK(); crfree(cr); return error; }
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; 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 == 2) { /* 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_InitReq(&treq, credp); if (code) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_name(%s, 0x%08x/%d/%d.%d): afs_InitReq: %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); AFS_GUNLOCK(); crfree(credp); code = afs_CheckCode(code, &treq, 102); return -code; }
/*! * All files that have been dirty before disconnection are going to * be replayed back to the server. * * \param areq Request from the user. * \param acred User credentials. * * \return If all files synchronized succesfully, return 0, otherwise * return error code * * \note For now, it's the request from the PDiscon pioctl. * */ int afs_ResyncDisconFiles(struct vrequest *areq, afs_ucred_t *acred) { struct afs_conn *tc; struct rx_connection *rxconn; struct vcache *tvc; struct AFSFetchStatus fstat; struct AFSCallBack callback; struct AFSVolSync tsync; int code = 0; afs_int32 start = 0; XSTATS_DECLS; /*AFS_STATCNT(afs_ResyncDisconFiles);*/ ObtainWriteLock(&afs_disconDirtyLock, 707); while (!QEmpty(&afs_disconDirty)) { tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq); /* Can't lock tvc whilst holding the discon dirty lock */ ReleaseWriteLock(&afs_disconDirtyLock); /* Get local write lock. */ ObtainWriteLock(&tvc->lock, 705); if (tvc->f.ddirty_flags & VDisconRemove) { /* Delete the file on the server and just move on * to the next file. After all, it has been deleted * we can't replay any other operation it. */ code = afs_ProcessOpRemove(tvc, areq); goto next_file; } else if (tvc->f.ddirty_flags & VDisconCreate) { /* For newly created files, we don't need a server lock. */ code = afs_ProcessOpCreate(tvc, areq, acred); if (code) goto next_file; tvc->f.ddirty_flags &= ~VDisconCreate; tvc->f.ddirty_flags |= VDisconCreated; } #if 0 /* Get server write lock. */ do { tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn); if (tc) { XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK); RX_AFS_GUNLOCK(); code = RXAFS_SetLock(rxconn, (struct AFSFid *)&tvc->f.fid.Fid, LockWrite, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; } else code = -1; } while (afs_Analyze(tc, rxconn, code, &tvc->f.fid, areq, AFS_STATS_FS_RPCIDX_SETLOCK, SHARED_LOCK, NULL)); if (code) goto next_file; #endif if (tvc->f.ddirty_flags & VDisconRename) { /* If we're renaming the file, do so now */ code = afs_ProcessOpRename(tvc, areq); if (code) goto unlock_srv_file; } /* Issue a FetchStatus to get info about DV and callbacks. */ do { tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn); if (tc) { tvc->callback = tc->srvr->server; start = osi_Time(); XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS); RX_AFS_GUNLOCK(); code = RXAFS_FetchStatus(rxconn, (struct AFSFid *)&tvc->f.fid.Fid, &fstat, &callback, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; } else code = -1; } while (afs_Analyze(tc, rxconn, code, &tvc->f.fid, areq, AFS_STATS_FS_RPCIDX_FETCHSTATUS, SHARED_LOCK, NULL)); if (code) { goto unlock_srv_file; } if ((dv_match(tvc, fstat) && (tvc->f.m.Date == fstat.ServerModTime)) || (afs_ConflictPolicy == CLIENT_WINS) || (tvc->f.ddirty_flags & VDisconCreated)) { /* * Send changes to the server if there's data version match, or * client wins policy has been selected or file has been created * but doesn't have it's the contents on to the server yet. */ /* * XXX: Checking server attr changes by timestamp might not the * most elegant solution, but it's the most viable one that we could find. */ afs_UpdateStatus(tvc, &tvc->f.fid, areq, &fstat, &callback, start); code = afs_SendChanges(tvc, areq); } else if (afs_ConflictPolicy == SERVER_WINS) { /* DV mismatch, apply collision resolution policy. */ /* Discard this files chunks and remove from current dir. */ afs_ResetVCache(tvc, acred, 0); tvc->f.truncPos = AFS_NOTRUNC; } else { /* printf("afs_ResyncDisconFiles: no resolution policy selected.\n"); */ } /* if DV match or client wins policy */ unlock_srv_file: /* Release server write lock. */ #if 0 do { tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn); if (tc) { XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK); RX_AFS_GUNLOCK(); ucode = RXAFS_ReleaseLock(rxconn, (struct AFSFid *) &tvc->f.fid.Fid, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; } else ucode = -1; } while (afs_Analyze(tc, rxconn, ucode, &tvc->f.fid, areq, AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK, NULL)); #endif next_file: ObtainWriteLock(&afs_disconDirtyLock, 710); if (code == 0) { /* Replayed successfully - pull the vcache from the * disconnected list */ tvc->f.ddirty_flags = 0; QRemove(&tvc->dirtyq); afs_PutVCache(tvc); } else { if (code == EAGAIN) { /* Operation was deferred. Pull it from the current place in * the list, and stick it at the end again */ QRemove(&tvc->dirtyq); QAdd(&afs_disconDirty, &tvc->dirtyq); } else { /* Failed - keep state as is, and let the user know we died */ ReleaseWriteLock(&tvc->lock); break; } } /* Release local write lock. */ ReleaseWriteLock(&tvc->lock); } /* while (tvc) */ if (code) { ReleaseWriteLock(&afs_disconDirtyLock); return code; } /* Dispose of all of the shadow directories */ afs_DisconDiscardAllShadows(0, acred); ReleaseWriteLock(&afs_disconDirtyLock); return code; }
int afs_AccessOK(struct vcache *avc, afs_int32 arights, struct vrequest *areq, afs_int32 check_mode_bits) { register struct vcache *tvc; struct VenusFid dirFid; register afs_int32 mask; afs_int32 dirBits; register afs_int32 fileBits; AFS_STATCNT(afs_AccessOK); if ((vType(avc) == VDIR) || (avc->f.states & CForeign)) { /* rights are just those from acl */ if (afs_InReadDir(avc)) { /* if we are already in readdir, then they may have read and * lookup, and nothing else, and nevermind the real ACL. * Otherwise we might end up with problems trying to call * FetchStatus on the vnode readdir is working on, and that * would be a real mess. */ dirBits = PRSFS_LOOKUP | PRSFS_READ; return (arights == (dirBits & arights)); } return (arights == afs_GetAccessBits(avc, arights, areq)); } else { /* some rights come from dir and some from file. Specifically, you * have "a" rights to a file if you are its owner, which comes * back as "a" rights to the file. You have other rights just * from dir, but all are restricted by the file mode bit. Now, * if you have I and A rights to a file, we throw in R and W * rights for free. These rights will then be restricted by * the access mask. */ dirBits = 0; if (avc->f.parent.vnode) { 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; /* Avoid this GetVCache call */ tvc = afs_GetVCache(&dirFid, areq, NULL, NULL); if (tvc) { dirBits = afs_GetAccessBits(tvc, arights, areq); afs_PutVCache(tvc); } } else dirBits = 0xffffffff; /* assume OK; this is a race condition */ if (arights & PRSFS_ADMINISTER) fileBits = afs_GetAccessBits(avc, arights, areq); else fileBits = 0; /* don't make call if results don't matter */ /* compute basic rights in fileBits, taking A from file bits */ fileBits = (fileBits & PRSFS_ADMINISTER) | (dirBits & ~PRSFS_ADMINISTER); /* for files, throw in R and W if have I and A (owner). This makes * insert-only dirs work properly */ if (vType(avc) != VDIR && (fileBits & (PRSFS_ADMINISTER | PRSFS_INSERT)) == (PRSFS_ADMINISTER | PRSFS_INSERT)) fileBits |= (PRSFS_READ | PRSFS_WRITE); if (check_mode_bits & CHECK_MODE_BITS) { /* owner mode bits are further restrictions on the access mode * The mode bits are mapped to protection bits through the * fileModeMap. If CMB_ALLOW_EXEC_AS_READ is set, it's from the * NFS translator and we don't know if it's a read or execute * on the NFS client, but both need to read the data. */ mask = (avc->f.m.Mode & 0700) >> 6; /* file restrictions to use */ fileBits &= ~fileModeMap[mask]; if (check_mode_bits & CMB_ALLOW_EXEC_AS_READ) { if (avc->f.m.Mode & 0100) fileBits |= PRSFS_READ; } } return ((fileBits & arights) == arights); /* true if all rights bits are on */ }
/* 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; }
/*! * Handles all the reconnection details: * - Get all the details about the vnode: name, fid, and parent dir fid. * - Send data to server. * - Handle errors. * - Reorder vhash and dcaches in their hashes, using the newly acquired fid. */ int afs_ProcessOpCreate(struct vcache *avc, struct vrequest *areq, afs_ucred_t *acred) { char *tname = NULL, *ttargetName = NULL; struct AFSStoreStatus InStatus; struct AFSFetchStatus OutFidStatus, OutDirStatus; struct VenusFid pdir_fid, newFid; struct AFSCallBack CallBack; struct AFSVolSync tsync; struct vcache *tdp = NULL, *tvc = NULL; struct dcache *tdc = NULL; struct afs_conn *tc; struct rx_connection *rxconn; afs_int32 hash, new_hash, index; afs_size_t tlen; int code, op = 0; XSTATS_DECLS; tname = afs_osi_Alloc(AFSNAMEMAX); if (!tname) return ENOMEM; code = afs_GetParentVCache(avc, 0, &pdir_fid, tname, &tdp); if (code) goto end; /* This data may also be in linkData, but then we have to deal with * the joy of terminating NULLs and . and file modes. So just get * it from the dcache where it won't have been fiddled with. */ if (vType(avc) == VLNK) { afs_size_t offset; struct dcache *tdc; struct osi_file *tfile; tdc = afs_GetDCache(avc, 0, areq, &offset, &tlen, 0); if (!tdc) { code = ENOENT; goto end; } if (tlen > 1024) { afs_PutDCache(tdc); code = EFAULT; goto end; } tlen++; /* space for NULL */ ttargetName = afs_osi_Alloc(tlen); if (!ttargetName) { afs_PutDCache(tdc); return ENOMEM; } ObtainReadLock(&tdc->lock); tfile = afs_CFileOpen(&tdc->f.inode); code = afs_CFileRead(tfile, 0, ttargetName, tlen); ttargetName[tlen-1] = '\0'; afs_CFileClose(tfile); ReleaseReadLock(&tdc->lock); afs_PutDCache(tdc); } /* Set status. */ InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP; InStatus.ClientModTime = avc->f.m.Date; InStatus.Owner = avc->f.m.Owner; InStatus.Group = (afs_int32) afs_cr_gid(acred); /* Only care about protection bits. */ InStatus.UnixModeBits = avc->f.m.Mode & 0xffff; do { tc = afs_Conn(&tdp->f.fid, areq, SHARED_LOCK, &rxconn); if (tc) { switch (vType(avc)) { case VREG: /* Make file on server. */ op = AFS_STATS_FS_RPCIDX_CREATEFILE; XSTATS_START_TIME(op); RX_AFS_GUNLOCK(); code = RXAFS_CreateFile(tc->id, (struct AFSFid *)&tdp->f.fid.Fid, tname, &InStatus, (struct AFSFid *) &newFid.Fid, &OutFidStatus, &OutDirStatus, &CallBack, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; break; case VDIR: /* Make dir on server. */ op = AFS_STATS_FS_RPCIDX_MAKEDIR; XSTATS_START_TIME(op); RX_AFS_GUNLOCK(); code = RXAFS_MakeDir(rxconn, (struct AFSFid *) &tdp->f.fid.Fid, tname, &InStatus, (struct AFSFid *) &newFid.Fid, &OutFidStatus, &OutDirStatus, &CallBack, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; break; case VLNK: /* Make symlink on server. */ op = AFS_STATS_FS_RPCIDX_SYMLINK; XSTATS_START_TIME(op); RX_AFS_GUNLOCK(); code = RXAFS_Symlink(rxconn, (struct AFSFid *) &tdp->f.fid.Fid, tname, ttargetName, &InStatus, (struct AFSFid *) &newFid.Fid, &OutFidStatus, &OutDirStatus, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; break; default: op = AFS_STATS_FS_RPCIDX_CREATEFILE; code = 1; break; } } else code = -1; } while (afs_Analyze(tc, rxconn, code, &tdp->f.fid, areq, op, SHARED_LOCK, NULL)); /* TODO: Handle errors. */ if (code) { /* printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); */ goto end; } /* The rpc doesn't set the cell number. */ newFid.Cell = avc->f.fid.Cell; /* * Change the fid in the dir entry. */ /* Seek the dir's dcache. */ tdc = afs_FindDCacheByFid(&tdp->f.fid); if (tdc) { /* And now change the fid in the parent dir entry. */ afs_dir_ChangeFid(tdc, tname, &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode); afs_PutDCache(tdc); } if (vType(avc) == VDIR) { /* Change fid in the dir for the "." entry. ".." has alredy been * handled by afs_FixChildrenFids when processing the parent dir. */ tdc = afs_FindDCacheByFid(&avc->f.fid); if (tdc) { afs_dir_ChangeFid(tdc, ".", &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode); if (avc->f.m.LinkCount >= 2) /* For non empty dirs, fix children's parentVnode and * parentUnique reference. */ afs_FixChildrenFids(&avc->f.fid, &newFid); afs_PutDCache(tdc); } } /* Recompute hash chain positions for vnode and dcaches. * Then change to the new FID. */ /* The vcache goes first. */ ObtainWriteLock(&afs_xvcache, 735); /* Old fid hash. */ hash = VCHash(&avc->f.fid); /* New fid hash. */ new_hash = VCHash(&newFid); /* Remove hash from old position. */ /* XXX: not checking array element contents. It shouldn't be empty. * If it oopses, then something else might be wrong. */ if (afs_vhashT[hash] == avc) { /* First in hash chain (might be the only one). */ afs_vhashT[hash] = avc->hnext; } else { /* More elements in hash chain. */ for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) { if (tvc->hnext == avc) { tvc->hnext = avc->hnext; break; } } } /* if (!afs_vhashT[i]->hnext) */ QRemove(&avc->vhashq); /* Insert hash in new position. */ avc->hnext = afs_vhashT[new_hash]; afs_vhashT[new_hash] = avc; QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq); ReleaseWriteLock(&afs_xvcache); /* Do the same thing for all dcaches. */ hash = DVHash(&avc->f.fid); ObtainWriteLock(&afs_xdcache, 743); for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) { hash = afs_dvnextTbl[index]; tdc = afs_GetValidDSlot(index); ReleaseReadLock(&tdc->tlock); if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) { if (!FidCmp(&tdc->f.fid, &avc->f.fid)) { /* Safer but slower. */ afs_HashOutDCache(tdc, 0); /* Put dcache in new positions in the dchash and dvhash. */ new_hash = DCHash(&newFid, tdc->f.chunk); afs_dcnextTbl[tdc->index] = afs_dchashTbl[new_hash]; afs_dchashTbl[new_hash] = tdc->index; new_hash = DVHash(&newFid); afs_dvnextTbl[tdc->index] = afs_dvhashTbl[new_hash]; afs_dvhashTbl[new_hash] = tdc->index; afs_indexUnique[tdc->index] = newFid.Fid.Unique; memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid)); } /* if fid match */ } /* if uniquifier match */ if (tdc) afs_PutDCache(tdc); } /* for all dcaches in this hash bucket */ ReleaseWriteLock(&afs_xdcache); /* Now we can set the new fid. */ memcpy(&avc->f.fid, &newFid, sizeof(struct VenusFid)); end: if (tdp) afs_PutVCache(tdp); afs_osi_Free(tname, AFSNAMEMAX); if (ttargetName) afs_osi_Free(ttargetName, tlen); return code; }
afs_root(struct mount *mp, struct vnode **vpp) #endif { int error; struct vrequest treq; struct vcache *tvp = 0; struct vcache *gvp; #if !defined(AFS_FBSD53_ENV) || defined(AFS_FBSD80_ENV) struct thread *td = curthread; #endif struct ucred *cr = osi_curcred(); AFS_GLOCK(); AFS_STATCNT(afs_root); crhold(cr); tryagain: if (afs_globalVp && (afs_globalVp->f.states & CStatd)) { tvp = afs_globalVp; error = 0; } else { if (!(error = afs_InitReq(&treq, cr)) && !(error = afs_CheckInit())) { tvp = afs_GetVCache(&afs_rootFid, &treq, NULL, NULL); /* we really want this to stay around */ if (tvp) { gvp = afs_globalVp; afs_globalVp = tvp; if (gvp) { afs_PutVCache(gvp); if (tvp != afs_globalVp) { /* someone raced us and won */ afs_PutVCache(tvp); goto tryagain; } } } else error = ENOENT; } } if (tvp) { struct vnode *vp = AFSTOV(tvp); ASSERT_VI_UNLOCKED(vp, "afs_root"); AFS_GUNLOCK(); error = vget(vp, LK_EXCLUSIVE | LK_RETRY, td); AFS_GLOCK(); /* we dropped the glock, so re-check everything it had serialized */ if (!afs_globalVp || !(afs_globalVp->f.states & CStatd) || tvp != afs_globalVp) { vput(vp); afs_PutVCache(tvp); goto tryagain; } if (error != 0) goto tryagain; /* * I'm uncomfortable about this. Shouldn't this happen at a * higher level, and shouldn't we busy the top-level directory * to prevent recycling? */ vp->v_vflag |= VV_ROOT; afs_globalVFS = mp; *vpp = vp; } afs_Trace2(afs_iclSetp, CM_TRACE_VFSROOT, ICL_TYPE_POINTER, tvp ? AFSTOV(tvp) : NULL, ICL_TYPE_INT32, error); AFS_GUNLOCK(); crfree(cr); return error; }
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; }
/*! * Try to get a vnode's name by comparing all parent dir's entries * to the given fid. It can also return the dir's dcache. * * \param avc The file's vcache. * \param afid The parent dir's fid. * \param aname A preallocated string for the name. * \param deleted Has this file been deleted? If yes, use the shadow * dir for looking up the name. */ int afs_GetVnodeName(struct vcache *avc, struct VenusFid *afid, char *aname, int deleted) { int code = 0; struct dcache *tdc; struct vcache *parent_vc; struct NameAndFid tnf; struct VenusFid parent_fid; struct VenusFid shadow_fid; /* List dir contents and get it's tdc. */ if (deleted) { /* For deleted files, get the shadow dir's tdc: */ /* Get the parent dir's vcache that contains the shadow fid. */ parent_fid.Cell = avc->f.fid.Cell; parent_fid.Fid.Volume = avc->f.fid.Fid.Volume; if (avc->f.ddirty_flags & VDisconRename) { /* For renames the old dir fid is needed. */ parent_fid.Fid.Vnode = avc->f.oldParent.vnode; parent_fid.Fid.Unique = avc->f.oldParent.unique; } else { parent_fid.Fid.Vnode = afid->Fid.Vnode; parent_fid.Fid.Unique = afid->Fid.Unique; } /* Get the parent dir's vcache that contains the shadow fid. */ ObtainSharedLock(&afs_xvcache, 755); parent_vc = afs_FindVCache(&parent_fid, 0, 1); ReleaseSharedLock(&afs_xvcache); if (!parent_vc) { return ENOENT; } shadow_fid.Cell = parent_vc->f.fid.Cell; shadow_fid.Fid.Volume = parent_vc->f.fid.Fid.Volume; shadow_fid.Fid.Vnode = parent_vc->f.shadow.vnode; shadow_fid.Fid.Unique = parent_vc->f.shadow.unique; afs_PutVCache(parent_vc); /* Get shadow dir's dcache. */ tdc = afs_FindDCacheByFid(&shadow_fid); } else { /* For normal files, look into the current dir's entry. */ tdc = afs_FindDCacheByFid(afid); } /* if (deleted) */ if (tdc) { tnf.fid = &avc->f.fid; tnf.name_len = -1; tnf.name = aname; afs_dir_EnumerateDir(tdc, &get_vnode_name_hook, &tnf); afs_PutDCache(tdc); if (tnf.name_len == -1) code = ENOENT; } else { /* printf("Directory dcache not found!\n"); */ code = ENOENT; } 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; }
/* 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; } }
/* * 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; struct vcache *vcp; struct vattr vattr; struct inode *ip; struct dentry *dp; afs_int32 code; code = afs_InitReq(&treq, credp); if (code) { #ifdef OSI_EXPORT_DEBUG printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): afs_InitReq: %d\n", afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique, code); #endif return ERR_PTR(-afs_CheckCode(code, &treq, 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 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 != 2 && !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); return ERR_PTR(-afs_CheckCode(code, &treq, 101)); } 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 return ERR_PTR(-ENOMEM); } dp->d_op = &afs_dentry_operations; return dp; }