From: Dragos Tatulea Date: Wed, 24 Sep 2008 23:46:17 +0000 (+0000) Subject: DEVEL15-disconnected-rw-20080922 X-Git-Tag: openafs-devel-1_5_53~23 X-Git-Url: https://git.michaelhowe.org/gitweb/?a=commitdiff_plain;h=12f47e39d07ef459cd8e24a1a9c5121391442b53;p=packages%2Fo%2Fopenafs.git DEVEL15-disconnected-rw-20080922 LICENSE IPL10 FIXES 114605 114606 114607 add read-write disconnected support (cherry picked from commit 433afd4779b077a1d0f4b842335e38625e124dcc) --- diff --git a/src/afs/LINUX/osi_vnodeops.c b/src/afs/LINUX/osi_vnodeops.c index 5b1afc4ce..13bb371c6 100644 --- a/src/afs/LINUX/osi_vnodeops.c +++ b/src/afs/LINUX/osi_vnodeops.c @@ -593,6 +593,8 @@ afs_linux_flush(struct file *fp) return 0; } + AFS_DISCON_LOCK(); + credp = crref(); vcp = VTOAFS(FILE_INODE(fp)); @@ -603,13 +605,32 @@ afs_linux_flush(struct file *fp) ObtainSharedLock(&vcp->lock, 535); if ((vcp->execsOrWriters > 0) && (file_count(fp) == 1)) { UpgradeSToWLock(&vcp->lock, 536); - code = afs_StoreAllSegments(vcp, &treq, AFS_SYNC | AFS_LASTSTORE); + if (!AFS_IS_DISCONNECTED) { + code = afs_StoreAllSegments(vcp, + &treq, + AFS_SYNC | AFS_LASTSTORE); + } else { +#if defined(AFS_DISCON_ENV) + if (!vcp->ddirty_flags || + (vcp->ddirty_flags == VDisconShadowed)) { + + ObtainWriteLock(&afs_DDirtyVCListLock, 710); + AFS_DISCON_ADD_DIRTY(vcp); + ReleaseWriteLock(&afs_DDirtyVCListLock); + } + + /* Set disconnected write flag. */ + vcp->ddirty_flags |= VDisconWriteOsiFlush; +#endif + } + ConvertWToSLock(&vcp->lock); } code = afs_CheckCode(code, &treq, 54); ReleaseSharedLock(&vcp->lock); out: + AFS_DISCON_UNLOCK(); AFS_GUNLOCK(); crfree(credp); diff --git a/src/afs/VNOPS/afs_vnop_access.c b/src/afs/VNOPS/afs_vnop_access.c index 74f40503a..25fae9e96 100644 --- a/src/afs/VNOPS/afs_vnop_access.c +++ b/src/afs/VNOPS/afs_vnop_access.c @@ -246,10 +246,10 @@ afs_access(OSI_VC_DECL(avc), register afs_int32 amode, } /* If we're looking for write access, and we're disconnected without logging, forget it */ - if ((amode & VWRITE) && (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING)) { + if ((amode & VWRITE) && (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW)) { afs_PutFakeStat(&fakestate); AFS_DISCON_UNLOCK(); - /*printf("Network is down in afs_vnop_access\n");*/ + printf("Network is down in afs_vnop_access\n"); return ENETDOWN; } diff --git a/src/afs/VNOPS/afs_vnop_attrs.c b/src/afs/VNOPS/afs_vnop_attrs.c index 26f4dfb9f..7ee49a619 100644 --- a/src/afs/VNOPS/afs_vnop_attrs.c +++ b/src/afs/VNOPS/afs_vnop_attrs.c @@ -40,6 +40,7 @@ extern struct vcache *afs_globalVp; extern struct vfs *afs_globalVFS; #endif + /* copy out attributes from cache entry */ int afs_CopyOutAttrs(register struct vcache *avc, register struct vattr *attrs) @@ -509,7 +510,7 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs, } } - if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) { + if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { code = ENETDOWN; goto done; } @@ -542,6 +543,7 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs, afs_size_t tsize = attrs->va_size; ObtainWriteLock(&avc->lock, 128); avc->states |= CDirty; + code = afs_TruncateAllSegments(avc, tsize, &treq, acred); /* if date not explicitly set by this call, set it ourselves, since we * changed the data */ @@ -549,12 +551,18 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs, astat.Mask |= AFS_SETMODTIME; astat.ClientModTime = osi_Time(); } + if (code == 0) { if (((avc->execsOrWriters <= 0) && (avc->states & CCreating) == 0) || (avc->execsOrWriters == 1 && AFS_NFSXLATORREQ(acred))) { - code = afs_StoreAllSegments(avc, &treq, AFS_ASYNC); - if (!code) - avc->states &= ~CDirty; + + /* Store files now if not disconnected. */ + /* XXX: AFS_IS_DISCON_RW handled. */ + if (!AFS_IS_DISCONNECTED) { + code = afs_StoreAllSegments(avc, &treq, AFS_ASYNC); + if (!code) + avc->states &= ~CDirty; + } } } else avc->states &= ~CDirty; @@ -579,10 +587,17 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs, osi_dnlc_purgedp(avc); /* error? erase any changes we made to vcache entry */ } + +#if defined(AFS_DISCON_ENV) } else { - /* Must be logging - but not implemented yet ... */ - code = ENETDOWN; - } + + ObtainSharedLock(&avc->lock, 712); + /* Write changes locally. */ + code = afs_WriteVCacheDiscon(avc, &astat, attrs); + ReleaseSharedLock(&avc->lock); +#endif + } /* if (!AFS_IS_DISCONNECTED) */ + #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) if (AFS_NFSXLATORREQ(acred)) { avc->execsOrWriters--; diff --git a/src/afs/VNOPS/afs_vnop_create.c b/src/afs/VNOPS/afs_vnop_create.c index 95eb55064..c89cb8887 100644 --- a/src/afs/VNOPS/afs_vnop_create.c +++ b/src/afs/VNOPS/afs_vnop_create.c @@ -122,7 +122,7 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, goto done; } - if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) { + if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { code = ENETDOWN; goto done; } @@ -260,11 +260,6 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, } } - if (AFS_IS_DISCONNECTED) { - /* XXX - If we get here, logging must be enabled (as we bypassed the - * earlier check. So - do that logging thang, then return */ - } - /* if we create the file, we don't do any access checks, since * that's how O_CREAT is supposed to work */ if (adp->states & CForeign) { @@ -296,74 +291,88 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, attrs->va_mode = 0x1b6; /* XXX default mode: rw-rw-rw XXX */ } } - InStatus.UnixModeBits = attrs->va_mode & 0xffff; /* only care about protection bits */ - do { - tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK); - 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(tc->id, (struct AFSFid *)&adp->fid.Fid, + + 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->fid, &treq, SHARED_LOCK); + 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(tc->id, (struct AFSFid *)&adp->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, code, &adp->fid, &treq, AFS_STATS_FS_RPCIDX_CREATEFILE, - SHARED_LOCK, NULL)); + RX_AFS_GLOCK(); + XSTATS_END_TIME; + CallBack.ExpirationTime += now; + } else + code = -1; + } while (afs_Analyze + (tc, code, &adp->fid, &treq, AFS_STATS_FS_RPCIDX_CREATEFILE, + SHARED_LOCK, NULL)); - if ((code == EEXIST || code == UAEEXIST) && + if ((code == EEXIST || code == UAEEXIST) && #ifdef AFS_SGI64_ENV - !(flags & VEXCL) + !(flags & VEXCL) #else /* AFS_SGI64_ENV */ - aexcl == NONEXCL + 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 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_OSF_ENV) || defined(AFS_DARWIN_ENV)) #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) #if defined(AFS_SGI64_ENV) - code = afs_lookup(VNODE_TO_FIRST_BHV((vnode_t *) adp), aname, avcp, - NULL, 0, NULL, acred); + code = afs_lookup(VNODE_TO_FIRST_BHV((vnode_t *) adp), aname, avcp, + NULL, 0, NULL, acred); #else - code = afs_lookup(adp, aname, avcp, NULL, 0, NULL, acred); + code = afs_lookup(adp, aname, avcp, NULL, 0, NULL, acred); #endif /* AFS_SGI64_ENV */ #else /* SUN5 || SGI */ - code = afs_lookup(adp, aname, avcp, acred); + code = afs_lookup(adp, aname, avcp, acred); #endif /* SUN5 || SGI */ #endif /* !(AFS_OSF_ENV || AFS_DARWIN_ENV) */ goto done; - } - if (code) { - if (code < 0) { - ObtainWriteLock(&afs_xcbhash, 488); - afs_DequeueCallback(adp); - adp->states &= ~CStatd; - ReleaseWriteLock(&afs_xcbhash); - osi_dnlc_purgedp(adp); - } - ReleaseWriteLock(&adp->lock); - if (tdc) { - ReleaseSharedLock(&tdc->lock); - afs_PutDCache(tdc); - } + } + + if (code) { + if (code < 0) { + ObtainWriteLock(&afs_xcbhash, 488); + afs_DequeueCallback(adp); + adp->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->fid.Cell; + newFid.Fid.Volume = adp->fid.Fid.Volume; + afs_GenFakeFid(&newFid, VREG); + } /* 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_LocalHero(adp, tdc, &OutDirStatus, 1)) { + 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); @@ -436,12 +445,36 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, osi_dnlc_purgedp(tvc); } ReleaseWriteLock(&afs_xcbhash); - afs_ProcessFS(tvc, &OutFidStatus, &treq); + if (!AFS_IS_DISCON_RW) + afs_ProcessFS(tvc, &OutFidStatus, &treq); tvc->parentVnode = adp->fid.Fid.Vnode; tvc->parentUnique = adp->fid.Fid.Unique; ReleaseWriteLock(&tvc->lock); *avcp = tvc; code = 0; + + if (AFS_IS_DISCON_RW) { +#if defined(AFS_DISCON_ENV) + /* After tvc has been created, we can do various ops on it. */ + /* Add to dirty list. */ + if (!tvc->ddirty_flags || + (tvc->ddirty_flags == VDisconShadowed)) { + /* Put it in the list only if it's fresh. */ + ObtainWriteLock(&afs_DDirtyVCListLock, 729); + AFS_DISCON_ADD_DIRTY(tvc); + ReleaseWriteLock(&afs_DDirtyVCListLock); + } + + /* Set create flag. */ + ObtainWriteLock(&tvc->lock, 730); + afs_GenDisconStatus(adp, tvc, &newFid, attrs, &treq, VREG); + tvc->ddirty_flags |= VDisconCreate; + ReleaseWriteLock(&tvc->lock); + +#endif /* #ifdef AFS_DISCON_ENV */ + } /* if (AFS_IS_DISCON_RW) */ + + } else code = ENOENT; } else { diff --git a/src/afs/VNOPS/afs_vnop_dirops.c b/src/afs/VNOPS/afs_vnop_dirops.c index a0a0b0371..0f7e3a4e1 100644 --- a/src/afs/VNOPS/afs_vnop_dirops.c +++ b/src/afs/VNOPS/afs_vnop_dirops.c @@ -44,6 +44,7 @@ afs_mkdir(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, register struct conn *tc; struct VenusFid newFid; register struct dcache *tdc; + struct dcache *new_dc; afs_size_t offset, len; register struct vcache *tvc; struct AFSStoreStatus InStatus; @@ -90,55 +91,81 @@ afs_mkdir(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, goto done; } - if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) { + if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) /*printf("Network is down in afs_mkdir\n");*/ code = ENETDOWN; - } - 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) acred->cr_gid; tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1); ObtainWriteLock(&adp->lock, 153); - do { - tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK); - if (tc) { - XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR); - now = osi_Time(); - RX_AFS_GUNLOCK(); - code = - RXAFS_MakeDir(tc->id, (struct AFSFid *)&adp->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, code, &adp->fid, &treq, AFS_STATS_FS_RPCIDX_MAKEDIR, - SHARED_LOCK, NULL)); - - if (code) { - if (code < 0) { - ObtainWriteLock(&afs_xcbhash, 490); - afs_DequeueCallback(adp); - adp->states &= ~CStatd; - ReleaseWriteLock(&afs_xcbhash); - osi_dnlc_purgedp(adp); + + if (!AFS_IS_DISCON_RW) { + + do { + tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK); + if (tc) { + XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR); + now = osi_Time(); + RX_AFS_GUNLOCK(); + code = + RXAFS_MakeDir(tc->id, + (struct AFSFid *)&adp->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, code, &adp->fid, &treq, AFS_STATS_FS_RPCIDX_MAKEDIR, + SHARED_LOCK, NULL)); + + if (code) { + if (code < 0) { + ObtainWriteLock(&afs_xcbhash, 490); + afs_DequeueCallback(adp); + adp->states &= ~CStatd; + ReleaseWriteLock(&afs_xcbhash); + osi_dnlc_purgedp(adp); + } + ReleaseWriteLock(&adp->lock); + if (tdc) + afs_PutDCache(tdc); + goto done; + } + + } else { +#if defined(AFS_DISCON_ENV) + /* Disconnected. */ + + /* We have the dir entry now, we can use it while disconnected. */ + if (adp->mvid == NULL) { + /* If not mount point, generate a new fid. */ + newFid.Cell = adp->fid.Cell; + newFid.Fid.Volume = adp->fid.Fid.Volume; + afs_GenFakeFid(&newFid, VDIR); } - ReleaseWriteLock(&adp->lock); - if (tdc) - afs_PutDCache(tdc); - goto done; - } + /* XXX: If mount point???*/ + + /* Operations with the actual dir's cache entry are further + * down, where the dir entry gets created. + */ +#endif + } /* 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_LocalHero(adp, tdc, &OutDirStatus, 1)) { + 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); @@ -152,17 +179,77 @@ afs_mkdir(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, ReleaseWriteLock(&tdc->lock); afs_PutDCache(tdc); } - adp->m.LinkCount = OutDirStatus.LinkCount; + + if (AFS_IS_DISCON_RW) + /* We will have to settle with the local link count. */ + adp->m.LinkCount++; + else + adp->m.LinkCount = OutDirStatus.LinkCount; newFid.Cell = adp->fid.Cell; newFid.Fid.Volume = adp->fid.Fid.Volume; ReleaseWriteLock(&adp->lock); - /* 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 - code = ENOENT; + if (AFS_IS_DISCON_RW) { +#if defined(AFS_DISCON_ENV) + /* When disconnected, we have to create the full dir here. */ + + /* Generate a new vcache and fill it. */ + tvc = afs_NewVCache(&newFid, NULL); + if (tvc) { + code = 0; + *avcp = tvc; + } else { + code = ENOENT; + 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 = ENOENT; + goto done; + } + + ObtainWriteLock(&afs_xdcache, 739); + code = afs_dir_MakeDir(new_dc, + (afs_int32 *) &newFid.Fid, + (afs_int32) &adp->fid.Fid); + ReleaseWriteLock(&afs_xdcache); + if (code) + printf("afs_mkdir: afs_dirMakeDir code = %u\n", code); + + /* Add to dirty list. */ + if (!tvc->ddirty_flags || + (tvc->ddirty_flags == VDisconShadowed)) { + + /* Put it in the list only if it's fresh. */ + ObtainWriteLock(&afs_DDirtyVCListLock, 730); + AFS_DISCON_ADD_DIRTY(tvc); + ReleaseWriteLock(&afs_DDirtyVCListLock); + } + + ObtainWriteLock(&tvc->lock, 731); + /* Update length in the vcache. */ + tvc->m.Length = new_dc->f.chunkBytes; + /* Set create flag. */ + tvc->ddirty_flags |= VDisconCreate; + ReleaseWriteLock(&tvc->lock); +#endif /* #ifdef AFS_DISCON_ENV */ + } 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 + code = ENOENT; + } /* if (AFS_DISCON_RW) */ + done: AFS_DISCON_UNLOCK(); done3: @@ -226,11 +313,12 @@ afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) goto done; } - if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) { + 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) @@ -256,42 +344,117 @@ afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) } } - do { - tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK); - if (tc) { - XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR); - RX_AFS_GUNLOCK(); - code = - RXAFS_RemoveDir(tc->id, (struct AFSFid *)&adp->fid.Fid, aname, - &OutDirStatus, &tsync); - RX_AFS_GLOCK(); - XSTATS_END_TIME; - } else - code = -1; - } while (afs_Analyze - (tc, code, &adp->fid, &treq, AFS_STATS_FS_RPCIDX_REMOVEDIR, - SHARED_LOCK, NULL)); - - if (code) { - if (tdc) { + if (!AFS_IS_DISCON_RW) { + /* Not disconnected, can connect to server. */ + do { + tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK); + if (tc) { + XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR); + RX_AFS_GUNLOCK(); + code = + RXAFS_RemoveDir(tc->id, + (struct AFSFid *)&adp->fid.Fid, + aname, + &OutDirStatus, + &tsync); + RX_AFS_GLOCK(); + XSTATS_END_TIME; + } else + code = -1; + } while (afs_Analyze + (tc, code, &adp->fid, &treq, AFS_STATS_FS_RPCIDX_REMOVEDIR, + SHARED_LOCK, NULL)); + + if (code) { + if (tdc) { + ReleaseSharedLock(&tdc->lock); + afs_PutDCache(tdc); + } + + if (code < 0) { + ObtainWriteLock(&afs_xcbhash, 491); + afs_DequeueCallback(adp); + adp->states &= ~CStatd; + ReleaseWriteLock(&afs_xcbhash); + osi_dnlc_purgedp(adp); + } + ReleaseWriteLock(&adp->lock); + goto done; + } + + /* here if rpc worked; update the in-core link count */ + adp->m.LinkCount = OutDirStatus.LinkCount; + + } else { +#if defined(AFS_DISCON_ENV) + /* Disconnected. */ + + if (!tvc) { + /* Find the vcache. */ + struct VenusFid tfid; + + tfid.Cell = adp->fid.Cell; + tfid.Fid.Volume = adp->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); + goto done; + } + } + + if (tvc->m.LinkCount > 2) { + /* This dir contains more than . and .., thus it can't be + * deleted. + */ ReleaseSharedLock(&tdc->lock); - afs_PutDCache(tdc); + code = ENOTEMPTY; + goto done; } - if (code < 0) { - ObtainWriteLock(&afs_xcbhash, 491); - afs_DequeueCallback(adp); - adp->states &= ~CStatd; - ReleaseWriteLock(&afs_xcbhash); - osi_dnlc_purgedp(adp); + + /* Make a shadow copy of the parent dir (if not done already). + * There's no need to make a shadow copy of the deleted directory + * because a dir must be empty in order to be rmdir'ed. + * If the deleted dir has no shadow, it means that it was empty. + */ + if (!(adp->ddirty_flags & VDisconShadowed)) { + /* If tdc available, then it is locked. + * afs_MakeShadowDir unlocks it. + */ + if (tdc) + ReleaseSharedLock(&tdc->lock); + afs_MakeShadowDir(adp); + if (tdc) + ObtainSharedLock(&tdc->lock, 732); } - ReleaseWriteLock(&adp->lock); - goto done; - } - /* here if rpc worked; update the in-core link count */ - adp->m.LinkCount = OutDirStatus.LinkCount; + + if (!tvc->ddirty_flags || + (tvc->ddirty_flags == VDisconShadowed)) { + /* Put it in the list only if it's fresh or has only been shadowed. */ + ObtainWriteLock(&afs_DDirtyVCListLock, 728); + AFS_DISCON_ADD_DIRTY(tvc); + ReleaseWriteLock(&afs_DDirtyVCListLock); + } + + /* Now add the vcache to the dirty list. */ + ObtainWriteLock(&tvc->lock, 727); + tvc->ddirty_flags |= VDisconRemove; + ReleaseWriteLock(&tvc->lock); + + adp->m.LinkCount--; +#endif /* #ifdef AFS_DISCON_ENV */ + } /* if (!AFS_IS_DISCON_RW) */ + if (tdc) UpgradeSToWLock(&tdc->lock, 634); - if (afs_LocalHero(adp, tdc, &OutDirStatus, 1)) { + if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) { /* we can do it locally */ code = afs_dir_Delete(tdc, aname); if (code) { @@ -314,7 +477,9 @@ afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) ObtainWriteLock(&tvc->lock, 155); tvc->states &= ~CUnique; /* For the dfs xlator */ ReleaseWriteLock(&tvc->lock); - afs_PutVCache(tvc); + /* If disconnected, keep this vcache around for resync. */ + if (!AFS_IS_DISCON_RW) + afs_PutVCache(tvc); } ReleaseWriteLock(&adp->lock); /* don't worry about link count since dirs can not be hardlinked */ diff --git a/src/afs/VNOPS/afs_vnop_lookup.c b/src/afs/VNOPS/afs_vnop_lookup.c index 2d85f8364..825494099 100644 --- a/src/afs/VNOPS/afs_vnop_lookup.c +++ b/src/afs/VNOPS/afs_vnop_lookup.c @@ -1744,7 +1744,7 @@ afs_lookup(OSI_VC_DECL(adp), char *aname, struct vcache **avcp, struct AFS_UCRED } code = ENOENT; } else { - /*printf("Network down in afs_lookup\n");*/ + printf("Network down in afs_lookup\n"); code = ENETDOWN; } } diff --git a/src/afs/VNOPS/afs_vnop_open.c b/src/afs/VNOPS/afs_vnop_open.c index 037cc8eb9..82c2c64d5 100644 --- a/src/afs/VNOPS/afs_vnop_open.c +++ b/src/afs/VNOPS/afs_vnop_open.c @@ -74,7 +74,7 @@ afs_open(struct vcache **avcp, afs_int32 aflags, struct AFS_UCRED *acred) #ifdef AFS_DISCON_ENV if (AFS_IS_DISCONNECTED && (afs_DCacheMissingChunks(tvc) != 0)) { ReleaseReadLock(&tvc->lock); - /*printf("Network is down in afs_open: missing chunks\n");*/ + printf("Network is down in afs_open: missing chunks\n"); code = ENETDOWN; goto done; } @@ -96,6 +96,7 @@ afs_open(struct vcache **avcp, afs_int32 aflags, struct AFS_UCRED *acred) (tvc, ((tvc->states & CForeign) ? PRSFS_READ : PRSFS_LOOKUP), &treq, CHECK_MODE_BITS)) { code = EACCES; + printf("afs_Open: no access for dir\n"); goto done; } } diff --git a/src/afs/VNOPS/afs_vnop_read.c b/src/afs/VNOPS/afs_vnop_read.c index 0e82f5b94..313c186cc 100644 --- a/src/afs/VNOPS/afs_vnop_read.c +++ b/src/afs/VNOPS/afs_vnop_read.c @@ -636,7 +636,7 @@ afs_UFSRead(register struct vcache *avc, struct uio *auio, tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 2); #ifdef AFS_DISCON_ENV if (!tdc) { - /*printf("Network down in afs_read");*/ + printf("Network down in afs_read"); error = ENETDOWN; break; } diff --git a/src/afs/VNOPS/afs_vnop_remove.c b/src/afs/VNOPS/afs_vnop_remove.c index 6f8238a2b..3d546a428 100644 --- a/src/afs/VNOPS/afs_vnop_remove.c +++ b/src/afs/VNOPS/afs_vnop_remove.c @@ -109,7 +109,6 @@ afsremove(register struct vcache *adp, register struct dcache *tdc, struct AFSFetchStatus OutDirStatus; struct AFSVolSync tsync; XSTATS_DECLS; - if (!AFS_IS_DISCONNECTED) { do { tc = afs_Conn(&adp->fid, treqp, SHARED_LOCK); @@ -135,6 +134,7 @@ afsremove(register struct vcache *adp, register struct dcache *tdc, ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } + if (tvc) afs_PutVCache(tvc); @@ -151,7 +151,7 @@ afsremove(register struct vcache *adp, register struct dcache *tdc, } if (tdc) UpgradeSToWLock(&tdc->lock, 637); - if (afs_LocalHero(adp, tdc, &OutDirStatus, 1)) { + if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, &OutDirStatus, 1)) { /* we can do it locally */ code = afs_dir_Delete(tdc, aname); if (code) { @@ -187,7 +187,11 @@ afsremove(register struct vcache *adp, register struct dcache *tdc, #ifdef AFS_BOZONLOCK_ENV afs_BozonUnlock(&tvc->pvnLock, tvc); #endif - afs_PutVCache(tvc); + /* Don't decrease refcount for this vcache if disconnected, we will + * need it during replay. + */ + if (!AFS_IS_DISCON_RW) + afs_PutVCache(tvc); } return (0); } @@ -308,7 +312,7 @@ afs_remove(OSI_VC_ARG(adp), aname, acred) } /* If we're running disconnected without logging, go no further... */ - if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) { + if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { #ifdef AFS_OSF_ENV afs_PutVCache(tvc); #endif @@ -319,6 +323,11 @@ afs_remove(OSI_VC_ARG(adp), aname, acred) tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1); /* test for error below */ ObtainWriteLock(&adp->lock, 142); +#if defined(AFS_DISCON_ENV) + if (AFS_IS_DISCON_RW && !(adp->ddirty_flags & VDisconShadowed)) + /* Make shadow copy of parent dir. */ + afs_MakeShadowDir(adp); +#endif if (tdc) ObtainSharedLock(&tdc->lock, 638); @@ -341,7 +350,8 @@ afs_remove(OSI_VC_ARG(adp), aname, acred) tvc = osi_dnlc_lookup(adp, aname, WRITE_LOCK); } /* This should not be necessary since afs_lookup() has already - * done the work */ + * done the work. + */ if (!tvc) if (tdc) { code = afs_dir_Lookup(tdc, aname, &unlinkFid.Fid); @@ -362,6 +372,28 @@ afs_remove(OSI_VC_ARG(adp), aname, acred) } } +#if defined(AFS_DISCON_ENV) + if (AFS_IS_DISCON_RW) { + /* Add removed file vcache to dirty list. */ + + if (!tvc->ddirty_flags || + (tvc->ddirty_flags == VDisconShadowed)) { + /* Add to list only if fresh. */ + ObtainWriteLock(&afs_DDirtyVCListLock, 725); + AFS_DISCON_ADD_DIRTY(tvc); + ReleaseWriteLock(&afs_DDirtyVCListLock); + } + + ObtainWriteLock(&tvc->lock, 726); + tvc->ddirty_flags |= VDisconRemove; + ReleaseWriteLock(&tvc->lock); + + //ObtainWriteLock(&adp->lock, 751); + adp->m.LinkCount--; + //ReleaseWriteLock(&adp->lock); + } +#endif + if (tvc && osi_Active(tvc)) { /* about to delete whole file, prefetch it first */ ReleaseWriteLock(&adp->lock); @@ -426,7 +458,11 @@ afs_remove(OSI_VC_ARG(adp), aname, acred) } if (tdc) afs_PutDCache(tdc); - afs_PutVCache(tvc); + /* Don't decrease refcount for this vcache if disconnected, we will + * need it during replay. + */ + if (!AFS_IS_DISCON_RW) + afs_PutVCache(tvc); } else { code = afsremove(adp, tdc, tvc, aname, acred, &treq); } diff --git a/src/afs/VNOPS/afs_vnop_rename.c b/src/afs/VNOPS/afs_vnop_rename.c index 58f2a69d4..647d744d0 100644 --- a/src/afs/VNOPS/afs_vnop_rename.c +++ b/src/afs/VNOPS/afs_vnop_rename.c @@ -37,7 +37,7 @@ afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, char *aname2, struct AFS_UCRED *acred, struct vrequest *areq) { register struct conn *tc; - register afs_int32 code; + register afs_int32 code = 0; afs_int32 returnCode; int oneDir, doLocally; afs_size_t offset, len; @@ -83,7 +83,7 @@ afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, goto done; } - if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) { + if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { code = ENETDOWN; goto done; } @@ -167,24 +167,90 @@ afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, goto done; } - do { - tc = afs_Conn(&aodp->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->fid.Fid, aname1, - (struct AFSFid *)&andp->fid.Fid, aname2, - &OutOldDirStatus, &OutNewDirStatus, &tsync); - RX_AFS_GLOCK(); - XSTATS_END_TIME; - } else - code = -1; - - } while (afs_Analyze + if (!AFS_IS_DISCON_RW) { + /* Connected. */ + do { + tc = afs_Conn(&aodp->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->fid.Fid, + aname1, + (struct AFSFid *)&andp->fid.Fid, + aname2, + &OutOldDirStatus, + &OutNewDirStatus, + &tsync); + RX_AFS_GLOCK(); + XSTATS_END_TIME; + } else + code = -1; + + } while (afs_Analyze (tc, code, &andp->fid, areq, AFS_STATS_FS_RPCIDX_RENAME, SHARED_LOCK, NULL)); + } else { +#if defined(AFS_DISCON_ENV) + /* Disconnected. */ + + /* Seek moved file vcache. */ + fileFid.Cell = aodp->fid.Cell; + fileFid.Fid.Volume = aodp->fid.Fid.Volume; + ObtainSharedLock(&afs_xvcache, 754); + tvc = afs_FindVCache(&fileFid, 0 , 1); + ReleaseSharedLock(&afs_xvcache); + + if (tvc) { + if (!(tvc->ddirty_flags & VDisconRename)) { + /* We only care about vnodes that haven't been + * renamed. Those that have been renamed at least once + * already have an asociated shadow dir and the flags set + * for the first old location, which is what interests us + * when reconnecting. + */ + + if (!(aodp->ddirty_flags & VDisconShadowed) && + !(tvc->ddirty_flags & VDisconCreate)) { + /* + * Make shadow copy of parent dir only. + * Files that are created locally and renamed, + * don't need a shadow dir, so skip this step. + */ + if (tdc1) + ReleaseWriteLock(&tdc1->lock); + afs_MakeShadowDir(aodp); + if (tdc1) + ObtainWriteLock(&tdc1->lock, 753); + } + + if (!tvc->ddirty_flags || + (tvc->ddirty_flags == VDisconShadowed)) { + /* Add in dirty list.*/ + ObtainWriteLock(&afs_DDirtyVCListLock, 751); + AFS_DISCON_ADD_DIRTY(tvc); + ReleaseWriteLock(&afs_DDirtyVCListLock); + } + + ObtainWriteLock(&tvc->lock, 750); + /* Save old parent dir fid so it will be searchable + * in the shadow dir. + */ + tvc->oldVnode = aodp->fid.Fid.Vnode; + tvc->oldUnique = aodp->fid.Fid.Unique; + /* Set discon state flag. */ + tvc->ddirty_flags |= VDisconRename; + if (oneDir) + tvc->ddirty_flags |= VDisconRenameSameDir; + ReleaseWriteLock(&tvc->lock); + } /* if not previously renamed */ + } 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. */ @@ -194,28 +260,31 @@ afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, * in the cache; if it isn't, we won't do the update locally. */ /* see if version numbers increased properly */ doLocally = 1; - 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) { + 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 */ @@ -247,9 +316,11 @@ afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, } /* update dir link counts */ - aodp->m.LinkCount = OutOldDirStatus.LinkCount; + aodp->m.LinkCount = AFS_IS_DISCON_RW ? + (aodp->m.LinkCount - 1):OutOldDirStatus.LinkCount; if (!oneDir) - andp->m.LinkCount = OutNewDirStatus.LinkCount; + andp->m.LinkCount = AFS_IS_DISCON_RW ? + (andp->m.LinkCount + 1):OutNewDirStatus.LinkCount; } else { /* operation failed (code != 0) */ if (code < 0) { @@ -280,7 +351,6 @@ afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, ReleaseWriteLock(&aodp->lock); if (!oneDir) ReleaseWriteLock(&andp->lock); - if (returnCode) { code = returnCode; goto done; @@ -294,6 +364,7 @@ afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, * the data as having an "unknown" version (effectively discarding the ".." * entry */ if (unlinkFid.Fid.Vnode) { + unlinkFid.Fid.Volume = aodp->fid.Fid.Volume; unlinkFid.Cell = aodp->fid.Cell; tvc = NULL; @@ -342,15 +413,28 @@ afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, ObtainWriteLock(&tvc->lock, 152); tdc1 = afs_FindDCache(tvc, (afs_size_t) 0); if (tdc1) { - ObtainWriteLock(&tdc1->lock, 648); - ZapDCE(tdc1); /* mark as unknown */ - DZap(tdc1); - ReleaseWriteLock(&tdc1->lock); - afs_PutDCache(tdc1); /* put it back */ + if (AFS_IS_DISCON_RW) { +#if defined(AFS_DISCON_ENV) + /* If disconnected, we need to fix (not discard) the "..".*/ + afs_dir_ChangeFid(tdc1, + "..", + &aodp->fid.Fid.Vnode, + &andp->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)) { + tvc->parentVnode = andp->fid.Fid.Vnode; + tvc->parentUnique = andp->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... diff --git a/src/afs/VNOPS/afs_vnop_write.c b/src/afs/VNOPS/afs_vnop_write.c index 7ba255b5a..7da994628 100644 --- a/src/afs/VNOPS/afs_vnop_write.c +++ b/src/afs/VNOPS/afs_vnop_write.c @@ -64,24 +64,46 @@ afs_StoreOnLastReference(register struct vcache *avc, crfree((struct AFS_UCRED *)avc->linkData); /* "crheld" in afs_FakeClose */ avc->linkData = NULL; } - /* Now, send the file back. Used to require 0 writers left, but now do - * it on every close for write, since two closes in a row are harmless - * since first will clean all chunks, and second will be noop. Note that - * this will also save confusion when someone keeps a file open - * inadvertently, since with old system, writes to the server would never - * happen again. - */ - code = afs_StoreAllSegments(avc, treq, AFS_LASTSTORE /*!sync-to-disk */ ); - /* - * We have to do these after the above store in done: in some systems like - * aix they'll need to flush all the vm dirty pages to the disk via the - * strategy routine. During that all procedure (done under no avc locks) - * opens, refcounts would be zero, since it didn't reach the afs_{rd,wr} - * routines which means the vcache is a perfect candidate for flushing! - */ + + if (!AFS_IS_DISCONNECTED) { + /* Connected. */ + + /* Now, send the file back. Used to require 0 writers left, but now do + * it on every close for write, since two closes in a row are harmless + * since first will clean all chunks, and second will be noop. Note that + * this will also save confusion when someone keeps a file open + * inadvertently, since with old system, writes to the server would never + * happen again. + */ + code = afs_StoreAllSegments(avc, treq, AFS_LASTSTORE /*!sync-to-disk */ ); + /* + * We have to do these after the above store in done: in some systems + * like aix they'll need to flush all the vm dirty pages to the disk via + * the strategy routine. During that all procedure (done under no avc + * locks) opens, refcounts would be zero, since it didn't reach the + * afs_{rd,wr} routines which means the vcache is a perfect candidate + * for flushing! + */ + +#ifdef AFS_DISCON_ENV + } else if (AFS_IS_DISCON_RW) { + /* Disconnected. */ + + if (!avc->ddirty_flags || + (avc->ddirty_flags == VDisconShadowed)) { + /* Add to disconnected dirty list. */ + AFS_DISCON_ADD_DIRTY(avc); + } + + /* Set disconnected write flag. */ + avc->ddirty_flags |= VDisconWriteClose; +#endif + } /* if not disconnected */ + #if defined(AFS_SGI_ENV) osi_Assert(avc->opens > 0 && avc->execsOrWriters > 0); #endif + avc->opens--; avc->execsOrWriters--; return code; @@ -382,7 +404,7 @@ afs_UFSWrite(register struct vcache *avc, struct uio *auio, int aio, if (avc->vc_error) return avc->vc_error; - if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) + if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) return ENETDOWN; startDate = osi_Time(); @@ -743,6 +765,7 @@ afs_DoPartialWrite(register struct vcache *avc, struct vrequest *areq) /* otherwise, call afs_StoreDCache (later try to do this async, if possible) */ afs_Trace2(afs_iclSetp, CM_TRACE_PARTIALWRITE, ICL_TYPE_POINTER, avc, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->m.Length)); + #if defined(AFS_SUN5_ENV) code = afs_StoreAllSegments(avc, areq, AFS_ASYNC | AFS_VMSYNC_INVAL); #else @@ -872,6 +895,7 @@ afs_close(OSI_VC_ARG(avc), aflags, acred) afs_PutFakeStat(&fakestat); return code; } + AFS_DISCON_LOCK(); #ifdef AFS_SUN5_ENV if (avc->flockCount) { HandleFlock(avc, LOCK_UN, &treq, 0, 1 /*onlymine */ ); @@ -880,6 +904,7 @@ afs_close(OSI_VC_ARG(avc), aflags, acred) #if defined(AFS_SGI_ENV) if (!lastclose) { afs_PutFakeStat(&fakestat); + AFS_DISCON_UNLOCK(); return 0; } /* unlock any locks for pid - could be wrong for child .. */ @@ -902,6 +927,7 @@ afs_close(OSI_VC_ARG(avc), aflags, acred) if (count > 1) { /* The vfs layer may call this repeatedly with higher "count"; only on the last close (i.e. count = 1) we should actually proceed with the close. */ afs_PutFakeStat(&fakestat); + AFS_DISCON_UNLOCK(); return 0; } #else /* AFS_SGI_ENV */ @@ -914,7 +940,7 @@ afs_close(OSI_VC_ARG(avc), aflags, acred) } #endif /* AFS_SGI_ENV */ if (aflags & (FWRITE | FTRUNC)) { - if (afs_BBusy() || (AFS_NFSXLATORREQ(acred))) { + if (afs_BBusy() || (AFS_NFSXLATORREQ(acred)) || AFS_IS_DISCONNECTED) { /* do it yourself if daemons are all busy */ ObtainWriteLock(&avc->lock, 124); code = afs_StoreOnLastReference(avc, &treq); @@ -955,6 +981,7 @@ afs_close(OSI_VC_ARG(avc), aflags, acred) #ifdef AFS_AIX32_ENV osi_ReleaseVM(avc, acred); #endif + printf("avc->vc_error=%d\n", avc->vc_error); code = avc->vc_error; avc->vc_error = 0; } @@ -1004,6 +1031,7 @@ afs_close(OSI_VC_ARG(avc), aflags, acred) afs_remunlink(avc, 1); /* ignore any return code */ } #endif + AFS_DISCON_UNLOCK(); afs_PutFakeStat(&fakestat); code = afs_CheckCode(code, &treq, 5); return code; @@ -1042,7 +1070,7 @@ afs_fsync(OSI_VC_DECL(avc), struct AFS_UCRED *acred) afs_Trace1(afs_iclSetp, CM_TRACE_FSYNC, ICL_TYPE_POINTER, avc); if ((code = afs_InitReq(&treq, acred))) return code; - + AFS_DISCON_LOCK(); #if defined(AFS_SGI_ENV) AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE); if (flag & FSYNC_INVAL) @@ -1052,11 +1080,37 @@ afs_fsync(OSI_VC_DECL(avc), struct AFS_UCRED *acred) ObtainSharedLock(&avc->lock, 18); code = 0; if (avc->execsOrWriters > 0) { - /* put the file back */ - UpgradeSToWLock(&avc->lock, 41); - code = afs_StoreAllSegments(avc, &treq, AFS_SYNC); - ConvertWToSLock(&avc->lock); - } + + if (!AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { + /* Your average flush. */ + + /* put the file back */ + UpgradeSToWLock(&avc->lock, 41); + code = afs_StoreAllSegments(avc, &treq, AFS_SYNC); + ConvertWToSLock(&avc->lock); + +#if defined(AFS_DISCON_ENV) + } else { + /* Disconnected flush. */ + ObtainWriteLock(&afs_DDirtyVCListLock, 708); + + if (!avc->ddirty_flags || + (avc->ddirty_flags == VDisconShadowed)) { + + /* Add to disconnected dirty list. */ + AFS_DISCON_ADD_DIRTY(avc); + } + + UpgradeSToWLock(&avc->lock, 711); + /* Set disconnected write flag. */ + avc->ddirty_flags |= VDisconWriteFlush; + ConvertWToSLock(&avc->lock); + + ReleaseWriteLock(&afs_DDirtyVCListLock); +#endif + } /* if not disconnected */ + } /* if (avc->execsOrWriters > 0) */ + #if defined(AFS_SGI_ENV) AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE); if (code == VNOVNODE) { @@ -1066,7 +1120,7 @@ afs_fsync(OSI_VC_DECL(avc), struct AFS_UCRED *acred) code = ENOENT; } #endif - + AFS_DISCON_UNLOCK(); code = afs_CheckCode(code, &treq, 33); ReleaseSharedLock(&avc->lock); return code; diff --git a/src/afs/afs.h b/src/afs/afs.h index b3f41c246..0c3b1c0a7 100644 --- a/src/afs/afs.h +++ b/src/afs/afs.h @@ -570,6 +570,27 @@ struct SimpleLocks { #define VRevokeWait 0x1 #define VPageCleaning 0x2 /* Solaris - Cache Trunc Daemon sez keep out */ +#if defined(AFS_DISCON_ENV) + +/* Dirty disconnected vcache flags. */ +#define VDisconSetTime 0x00000001 /* set time. */ +#define VDisconSetMode 0x00000002 /* set mode. */ +/* XXX: to be continued ? */ +#define VDisconTrunc 0x00000020 /* truncate file. */ +#define VDisconSetAttrMask 0x0000003F /* Masks for setattr ops. */ +#define VDisconWriteClose 0x00000400 /* Write op on file close. */ +#define VDisconWriteFlush 0x00000800 /* Write op on normal fsync/flush. */ +#define VDisconWriteOsiFlush 0x00001000 /* Write op on osi flush. */ + +#define VDisconShadowed 0x00002000 /* Shadowed dir. */ +#define VDisconRemove 0x00004000 /* Remove vnop. */ +#define VDisconCreate 0x00008000 /* Create vnop. */ +#define VDisconRename 0x00010000 /* Rename vnop. */ +#define VDisconRenameSameDir 0x00020000 /* Rename in same dir. */ + +/*... to be continued ... */ +#endif + #define CPSIZE 2 #if defined(AFS_XBSD_ENV) || defined(AFS_DARWIN_ENV) #define vrefCount v->v_usecount @@ -634,6 +655,20 @@ struct vcache { #endif struct vcache *hnext; /* Hash next */ struct afs_q vhashq; /* Hashed per-volume list */ + +#if defined(AFS_DISCON_ENV) + /*! Next element in afs_DDirtyVCList. Lock it with afs_DDirtyVCListLock. */ + struct vcache *ddirty_next; + /*! Disconnected flags for this vcache element. */ + uint32_t ddirty_flags; + /*! Shadow vnode + unique keep the shadow dir location. */ + afs_uint32 shVnode; + afs_uint32 shUnique; + /*! The old parent FID for renamed vnodes. */ + afs_uint32 oldVnode; + afs_uint32 oldUnique; +#endif + struct VenusFid fid; struct mstat { afs_size_t Length; diff --git a/src/afs/afs_callback.c b/src/afs/afs_callback.c index 9292c8270..089c2d0a2 100644 --- a/src/afs/afs_callback.c +++ b/src/afs/afs_callback.c @@ -66,6 +66,10 @@ static struct ltable { { "afsdb_client_lock", (char *)&afsdb_client_lock}, { "afsdb_req_lock", (char *)&afsdb_req_lock}, #endif +#ifdef AFS_DISCON_ENV + { "afs_discon_lock", (char *)&afs_discon_lock}, + { "afs_DDirtyVCListLock", (char *)&afs_DDirtyVCListLock}, +#endif }; unsigned long lastCallBack_vnode; unsigned int lastCallBack_dv; diff --git a/src/afs/afs_conn.c b/src/afs/afs_conn.c index 24e30c5d5..9421d6902 100644 --- a/src/afs/afs_conn.c +++ b/src/afs/afs_conn.c @@ -159,7 +159,7 @@ afs_ConnBySA(struct srvAddr *sap, unsigned short aport, afs_int32 acell, return NULL; } - if (AFS_IS_DISCONNECTED) { + if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) { afs_warnuser("afs_ConnBySA: disconnected\n"); ReleaseSharedLock(&afs_xconn); return NULL; @@ -276,7 +276,7 @@ afs_ConnByHost(struct server *aserver, unsigned short aport, afs_int32 acell, AFS_STATCNT(afs_ConnByHost); - if (AFS_IS_DISCONNECTED) { + if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) { afs_warnuser("afs_ConnByHost: disconnected\n"); return NULL; } diff --git a/src/afs/afs_dcache.c b/src/afs/afs_dcache.c index 67fccd36b..ac373f076 100644 --- a/src/afs/afs_dcache.c +++ b/src/afs/afs_dcache.c @@ -34,7 +34,6 @@ static void afs_DCMoveBucket(struct dcache *, afs_int32, afs_int32); static void afs_DCSizeInit(void); static afs_int32 afs_DCWhichBucket(afs_int32, afs_int32); - /* * --------------------- Exported definitions --------------------- */ @@ -462,7 +461,8 @@ afs_AdjustSize(register struct dcache *adc, register afs_int32 newSize) adc->validPos = 0; newSize = ((newSize + afs_fsfragsize) ^ afs_fsfragsize) >> 10; /* round up */ afs_DCAdjustSize(adc, oldSize, newSize); - if (newSize > oldSize) { + if ((newSize > oldSize) && !AFS_IS_DISCONNECTED) { + /* We're growing the file, wakeup the daemon */ afs_MaybeWakeupTruncateDaemon(); } @@ -774,7 +774,7 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint) ICL_TYPE_INT32, tdc->index, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(tchunkoffset)); AFS_STATCNT(afs_gget); - afs_HashOutDCache(tdc); + afs_HashOutDCache(tdc, 1); if (tdc->f.chunkBytes != 0) { discard = 1; if (aneedSpace) @@ -825,17 +825,19 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint) * again by afs_FindDCache or afs_GetDCache. * * Parameters: adc -- pointer to dcache entry to remove from hash tables. + * zap -- zap the given dcache ? * * Locks: Must have the afs_xdcache lock write-locked to call this function. */ int -afs_HashOutDCache(struct dcache *adc) +afs_HashOutDCache(struct dcache *adc, int zap) { int i, us; AFS_STATCNT(afs_glink); - /* we know this guy's in the LRUQ. We'll move dude into DCQ below */ - DZap(adc); + if (zap) + /* we know this guy's in the LRUQ. We'll move dude into DCQ below */ + DZap(adc); /* if this guy is in the hash table, pull him out */ if (adc->f.fid.Fid.Volume != 0) { /* remove entry from first hash chains */ @@ -878,20 +880,21 @@ afs_HashOutDCache(struct dcache *adc) } } - /* prevent entry from being found on a reboot (it is already out of - * the hash table, but after a crash, we just look at fid fields of - * stable (old) entries). - */ - adc->f.fid.Fid.Volume = 0; /* invalid */ + if (zap) { + /* prevent entry from being found on a reboot (it is already out of + * the hash table, but after a crash, we just look at fid fields of + * stable (old) entries). + */ + adc->f.fid.Fid.Volume = 0; /* invalid */ - /* mark entry as modified */ - adc->dflags |= DFEntryMod; + /* mark entry as modified */ + adc->dflags |= DFEntryMod; + } /* all done */ return 0; } /*afs_HashOutDCache */ - /* * afs_FlushDCache * @@ -917,7 +920,7 @@ afs_FlushDCache(register struct dcache *adc) afs_stats_cmperf.cacheFlushes++; /* remove from all hash tables */ - afs_HashOutDCache(adc); + afs_HashOutDCache(adc, 1); /* Free its space; special case null operation, since truncate operation * in UFS is slow even in this case, and this allows us to pre-truncate @@ -1336,18 +1339,29 @@ int afs_DCacheMissingChunks(struct vcache *avc) { int i, index; - afs_size_t totalLength; - afs_uint32 totalChunks; + afs_size_t totalLength = 0; + afs_uint32 totalChunks = 0; struct dcache *tdc; totalLength = avc->m.Length; if (avc->truncPos < totalLength) totalLength = avc->truncPos; - totalChunks = AFS_CHUNK(totalLength) + 1; + /* Length is 0, no chunk missing. */ + if (totalLength == 0) + return 0; - /*printf("Should have %d chunks for %d bytes\n", totalChunks, totalLength);*/ - + /* If totalLength is a multiple of chunksize, the last byte appears + * as being part of the next chunk, which does not exist. + * Decrementing totalLength by one fixes that. + */ + totalLength--; + totalChunks = (AFS_CHUNK(totalLength) + 1); + + /* + printf("Should have %d chunks for %u bytes\n", + totalChunks, (totalLength + 1)); + */ i = DVHash(&avc->fid); MObtainWriteLock(&afs_xdcache, 1001); for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) { @@ -1637,6 +1651,102 @@ afs_UFSCacheFetchProc(register struct rx_call *acall, struct osi_file *afile, } /* afs_UFSCacheFetchProc */ +/*! + * Get a fresh dcache from the free or discarded list. + * + * \param avc Who's dcache is this going to be? + * \param chunk The position where it will be placed in. + * \param lock How are locks held. + * \param ashFid If this dcache going to be used for a shadow dir, + * this is it's fid. + * + * \note Required locks: + * - afs_xdcache (W) + * - avc (R if (lock & 1) set and W otherwise) + * \note It write locks the new dcache. The caller must unlock it. + * + * \return The new dcache. + */ +struct dcache *afs_AllocDCache(struct vcache *avc, + afs_int32 chunk, + afs_int32 lock, + struct VenusFid *ashFid) +{ + struct dcache *tdc = NULL; + afs_uint32 size = 0; + struct osi_file *file; + + if (afs_discardDCList == NULLIDX + || ((lock & 2) && afs_freeDCList != NULLIDX)) { + + afs_indexFlags[afs_freeDCList] &= ~IFFree; + tdc = afs_GetDSlot(afs_freeDCList, 0); + osi_Assert(tdc->refCount == 1); + ReleaseReadLock(&tdc->tlock); + ObtainWriteLock(&tdc->lock, 604); + afs_freeDCList = afs_dvnextTbl[tdc->index]; + afs_freeDCCount--; + } else { + afs_indexFlags[afs_discardDCList] &= ~IFDiscarded; + tdc = afs_GetDSlot(afs_discardDCList, 0); + osi_Assert(tdc->refCount == 1); + ReleaseReadLock(&tdc->tlock); + ObtainWriteLock(&tdc->lock, 605); + afs_discardDCList = afs_dvnextTbl[tdc->index]; + afs_discardDCCount--; + size = + ((tdc->f.chunkBytes + + afs_fsfragsize) ^ afs_fsfragsize) >> 10; + tdc->f.states &= ~(DRO|DBackup|DRW); + afs_DCMoveBucket(tdc, size, 0); + afs_blocksDiscarded -= size; + afs_stats_cmperf.cacheBlocksDiscarded = afs_blocksDiscarded; + if (lock & 2) { + /* Truncate the chunk so zeroes get filled properly */ + file = afs_CFileOpen(tdc->f.inode); + afs_CFileTruncate(file, 0); + afs_CFileClose(file); + afs_AdjustSize(tdc, 0); + } + } + + /* + * Locks held: + * avc->lock(R) if setLocks + * avc->lock(W) if !setLocks + * tdc->lock(W) + * afs_xdcache(W) + */ + + /* + * Fill in the newly-allocated dcache record. + */ + afs_indexFlags[tdc->index] &= ~(IFDirtyPages | IFAnyPages); + if (ashFid) + /* Use shadow fid if provided. */ + tdc->f.fid = *ashFid; + else + /* Use normal vcache's fid otherwise. */ + tdc->f.fid = avc->fid; + if (avc->states & CRO) + tdc->f.states = DRO; + else if (avc->states & CBackup) + tdc->f.states = DBackup; + else + tdc->f.states = DRW; + afs_DCMoveBucket(tdc, 0, afs_DCGetBucket(avc)); + afs_indexUnique[tdc->index] = tdc->f.fid.Fid.Unique; + if (!ashFid) + hones(tdc->f.versionNo); /* invalid value */ + tdc->f.chunk = chunk; + tdc->validPos = AFS_CHUNKTOBASE(chunk); + /* XXX */ + if (tdc->lruq.prev == &tdc->lruq) + osi_Panic("lruq 1"); + + return tdc; +} + /* * afs_GetDCache * @@ -1885,17 +1995,6 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte, * If we didn't find the entry, we'll create one. */ if (index == NULLIDX) { - /* If we're disconnected, we can't do anything */ - if (AFS_IS_DISCONNECTED) { - MReleaseWriteLock(&afs_xdcache); - if (setLocks) { - if (slowPass) - ReleaseWriteLock(&avc->lock); - else - ReleaseReadLock(&avc->lock); - } - return NULL; - } /* * Locks held: * avc->lock(R) if setLocks @@ -1931,67 +2030,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte, } } - if (afs_discardDCList == NULLIDX - || ((aflags & 2) && afs_freeDCList != NULLIDX)) { - - afs_indexFlags[afs_freeDCList] &= ~IFFree; - tdc = afs_GetDSlot(afs_freeDCList, 0); - osi_Assert(tdc->refCount == 1); - ReleaseReadLock(&tdc->tlock); - ObtainWriteLock(&tdc->lock, 604); - afs_freeDCList = afs_dvnextTbl[tdc->index]; - afs_freeDCCount--; - } else { - afs_indexFlags[afs_discardDCList] &= ~IFDiscarded; - tdc = afs_GetDSlot(afs_discardDCList, 0); - osi_Assert(tdc->refCount == 1); - ReleaseReadLock(&tdc->tlock); - ObtainWriteLock(&tdc->lock, 605); - afs_discardDCList = afs_dvnextTbl[tdc->index]; - afs_discardDCCount--; - size = - ((tdc->f.chunkBytes + - afs_fsfragsize) ^ afs_fsfragsize) >> 10; - tdc->f.states &= ~(DRO|DBackup|DRW); - afs_DCMoveBucket(tdc, size, 0); - afs_blocksDiscarded -= size; - afs_stats_cmperf.cacheBlocksDiscarded = afs_blocksDiscarded; - if (aflags & 2) { - /* Truncate the chunk so zeroes get filled properly */ - file = afs_CFileOpen(tdc->f.inode); - afs_CFileTruncate(file, 0); - afs_CFileClose(file); - afs_AdjustSize(tdc, 0); - } - } - - /* - * Locks held: - * avc->lock(R) if setLocks - * avc->lock(W) if !setLocks - * tdc->lock(W) - * afs_xdcache(W) - */ - - /* - * Fill in the newly-allocated dcache record. - */ - afs_indexFlags[tdc->index] &= ~(IFDirtyPages | IFAnyPages); - tdc->f.fid = avc->fid; - if (avc->states & CRO) - tdc->f.states = DRO; - else if (avc->states & CBackup) - tdc->f.states = DBackup; - else - tdc->f.states = DRW; - afs_DCMoveBucket(tdc, 0, afs_DCGetBucket(avc)); - afs_indexUnique[tdc->index] = tdc->f.fid.Fid.Unique; - hones(tdc->f.versionNo); /* invalid value */ - tdc->f.chunk = chunk; - tdc->validPos = AFS_CHUNKTOBASE(chunk); - /* XXX */ - if (tdc->lruq.prev == &tdc->lruq) - osi_Panic("lruq 1"); + tdc = afs_AllocDCache(avc, chunk, aflags, NULL); /* * Now add to the two hash chains - note that i is still set @@ -2154,7 +2193,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte, } /* Flush the Dcache */ afs_PutDCache(tdc); - + return NULL; } UpgradeSToWLock(&tdc->lock, 609); @@ -3573,3 +3612,211 @@ shutdown_dcache(void) QInit(&afs_DLRU); } + +#if defined(AFS_DISCON_ENV) + +/*! + * Make a shadow copy of a dir's dcaches. It's used for disconnected + * operations like remove/create/rename to keep the original directory data. + * On reconnection, we can diff the original data with the server and get the + * server changes and with the local data to get the local changes. + * + * \param avc The dir vnode. + * + * \return 0 for success. + * + * \note The only lock allowed to be set is the dir's vcache entry, and it + * must be set in write mode. + * \note The vcache entry must be write locked. + */ +int afs_MakeShadowDir(struct vcache *avc) +{ + int j, i, index, code, ret_code = 0, offset, trans_size, block; + struct dcache *tdc, *new_dc = NULL; + struct osi_file *tfile_src, *tfile_dst; + struct VenusFid shadow_fid; + char *data; + int lock_held = 0; + + /* Is this a dir? */ + if (vType(avc) != VDIR) + return ENOTDIR; + + /* Generate a fid for the shadow dir. */ + shadow_fid.Cell = avc->fid.Cell; + shadow_fid.Fid.Volume = avc->fid.Fid.Volume; + afs_GenShadowFid(&shadow_fid); + + /* For each dcache, do copy it into a new fresh one. */ + i = DVHash(&avc->fid); + for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) { + /* Making sure that this isn't going to get locked twice. */ + if (!lock_held) { + /* XXX: Moved it from outside of the loop. + * Maybe it's not quite okay because of the use of + * dvhashTbl (once) in the for statement. + */ + ObtainWriteLock(&afs_xdcache, 716); + lock_held = 1; + } + + i = afs_dvnextTbl[index]; + if (afs_indexUnique[index] == avc->fid.Fid.Unique) { + tdc = afs_GetDSlot(index, NULL); + + ReleaseReadLock(&tdc->tlock); + + if (!FidCmp(&tdc->f.fid, &avc->fid)) { + + /* Got a dir's dcache. */ + lock_held = 0; + + /* Get a fresh dcache. */ + new_dc = afs_AllocDCache(avc, 0, 0, &shadow_fid); + + /* Unlock hash for now. Don't need it during operations on the + * dcache. Oh, and we can't use it because of the locking + * hierarchy... + */ + /* XXX: So much for lock ierarchy, the afs_AllocDCache doesn't + * respect it. + */ + //ReleaseWriteLock(&afs_xdcache); + + ObtainReadLock(&tdc->lock); + + /* Set up the new fid. */ + /* Copy interesting data from original dir dcache. */ + new_dc->mflags = tdc->mflags; + new_dc->dflags = tdc->dflags; + new_dc->f.modTime = tdc->f.modTime; + new_dc->f.versionNo = tdc->f.versionNo; + new_dc->f.states = tdc->f.states; + new_dc->f.chunk= tdc->f.chunk; + new_dc->f.chunkBytes = tdc->f.chunkBytes; + + /* + * Now add to the two hash chains - note that i is still set + * from the above DCHash call. + */ + //ObtainWriteLock(&afs_xdcache, 713); + + j = DCHash(&shadow_fid, 0); + afs_dcnextTbl[new_dc->index] = afs_dchashTbl[j]; + afs_dchashTbl[j] = new_dc->index; + + j = DVHash(&shadow_fid); + afs_dvnextTbl[new_dc->index] = afs_dvhashTbl[j]; + afs_dvhashTbl[j] = new_dc->index; + afs_MaybeWakeupTruncateDaemon(); + + ReleaseWriteLock(&afs_xdcache); + + /* Alloc a 4k block. */ + data = (char *) afs_osi_Alloc(4096); + if (!data) { + printf("afs_MakeShadowDir: could not alloc data\n"); + ret_code = ENOMEM; + goto done; + } + + /* Open the files. */ + tfile_src = afs_CFileOpen(tdc->f.inode); + tfile_dst = afs_CFileOpen(new_dc->f.inode); + + /* Init no of blocks to be read and offset. */ + block = (tdc->f.chunkBytes / 4096); + offset = 0; + + /* And now copy dir dcache data into this dcache, + * 4k at a time. + */ + while (block >= 0) { + + /* Last chunk might have less bytes to transfer. */ + if (!block) { + /* Last block. */ + trans_size = (tdc->f.chunkBytes % 4096); + if (!trans_size) + /* An exact no of 4k blocks. */ + break; + } else + trans_size = 4096; + + /* Read a chunk from the dcache. */ + code = afs_CFileRead(tfile_src, offset, data, trans_size); + if (code < trans_size) { + /* Can't access file, stop doing stuff and return error. */ + ret_code = EIO; + break; + } + + /* Write it to the new dcache. */ + code = afs_CFileWrite(tfile_dst, offset, data, trans_size); + if (code < trans_size) { + ret_code = EIO; + break; + } + + block--; + offset += 4096; + } /* while (block) */ + + afs_CFileClose(tfile_dst); + afs_CFileClose(tfile_src); + + afs_osi_Free(data, 4096); + + ReleaseWriteLock(&new_dc->lock); + ReleaseReadLock(&tdc->lock); + + afs_PutDCache(new_dc); + } /* if dcache fid match */ + afs_PutDCache(tdc); + } /* if unuiquifier match */ + } +done: + if (lock_held) + ReleaseWriteLock(&afs_xdcache); + + if (!ret_code) { + if (!avc->ddirty_flags) { + ObtainWriteLock(&afs_DDirtyVCListLock, 763); + AFS_DISCON_ADD_DIRTY(avc); + ReleaseWriteLock(&afs_DDirtyVCListLock); + } + avc->shVnode = shadow_fid.Fid.Vnode; + avc->shUnique = shadow_fid.Fid.Unique; + avc->ddirty_flags |= VDisconShadowed; + } + + return ret_code; +} + +/*! + * Delete the dcaches of a shadow dir. + * + * \param avc The vcache containing the shadow fid. + * + * \note avc must be write locked. + */ +void afs_DeleteShadowDir(struct vcache *avc) +{ + struct dcache *tdc; + struct VenusFid shadow_fid; + + shadow_fid.Cell = avc->fid.Cell; + shadow_fid.Fid.Volume = avc->fid.Fid.Volume; + shadow_fid.Fid.Vnode = avc->shVnode; + shadow_fid.Fid.Unique = avc->shUnique; + + tdc = afs_FindDCacheByFid(&shadow_fid); + if (tdc) { + afs_HashOutDCache(tdc, 1); + afs_DiscardDCache(tdc); + afs_PutDCache(tdc); + } + /* Remove shadowed dir flag. */ + avc->ddirty_flags &= ~VDisconShadowed; +} +#endif diff --git a/src/afs/afs_disconnected.c b/src/afs/afs_disconnected.c index 5d8e33094..4d5f19223 100644 --- a/src/afs/afs_disconnected.c +++ b/src/afs/afs_disconnected.c @@ -3,7 +3,7 @@ * License. For details, see the LICENSE file in the top-level source * directory or online at http://www.openafs.org/dl/license10.html */ - + #include #include "afs/param.h" @@ -11,8 +11,1476 @@ RCSID("$Header$"); #include "afs/sysincludes.h" #include "afsincludes.h" - +#include "afs/afs_stats.h" /* statistics */ +#include "afs/lock.h" +#include "afs/afs_cbqueue.h" + #ifdef AFS_DISCON_ENV -/* Nothing here any more. Remove from the build unless stuff comes back! */ +#define dv_match(vc, fstat) \ + ((vc->m.DataVersion.low == fstat.DataVersion) && \ + (vc->m.DataVersion.high == fstat.dataVersionHigh)) + +/*! Global list of dirty vcaches. */ +/*! Last added element. */ +struct vcache *afs_DDirtyVCList = NULL; +/*! Head of list. */ +struct vcache *afs_DDirtyVCListStart = NULL; +/*! Previous element in the list. */ +struct vcache *afs_DDirtyVCListPrev = NULL; + +/*! Locks list of dirty vcaches. */ +afs_rwlock_t afs_DDirtyVCListLock; + +extern afs_int32 *afs_dvhashTbl; /*Data cache hash table */ +extern afs_int32 *afs_dchashTbl; /*Data cache hash table */ +extern afs_int32 *afs_dvnextTbl; /*Dcache hash table links */ +extern afs_int32 *afs_dcnextTbl; /*Dcache hash table links */ +extern struct dcache **afs_indexTable; /*Pointers to dcache entries */ + +/*! Vnode number. On file creation, use the current + * value and increment it. + */ +afs_uint32 afs_DisconVnode = 2; + +/*! Conflict policy. */ +enum { + CLIENT_WINS = 0, + SERVER_WINS, + LAST_CLOSER_WINS, + ASK +}; + +afs_int32 afs_ConflictPolicy = SERVER_WINS; + +/*! + * Find the first dcache of a file that has the specified fid. + * Similar to afs_FindDCache, only that it takes a fid instead + * of a vcache and it can get the first dcache. + * + * \param afid + * + * \return The found dcache or NULL. + */ +struct dcache *afs_FindDCacheByFid(register struct VenusFid *afid) +{ + register afs_int32 i, index; + register struct dcache *tdc = NULL; + + i = DVHash(afid); + ObtainWriteLock(&afs_xdcache, 758); + for (index = afs_dvhashTbl[i]; index != NULLIDX;) { + if (afs_indexUnique[index] == afid->Fid.Unique) { + tdc = afs_GetDSlot(index, NULL); + ReleaseReadLock(&tdc->tlock); + if (!FidCmp(&tdc->f.fid, afid)) { + break; /* leaving refCount high for caller */ + } + afs_PutDCache(tdc); + } + index = afs_dvnextTbl[index]; + } + ReleaseWriteLock(&afs_xdcache); + + if (index == NULLIDX) + tdc = NULL; + return tdc; +} + +/*! + * Generate a store status from a dirty vcache entry. + * + * \param avc Dirty vcache entry. + * \param astat + * + * \note The vnode must be share locked. It is called only on resync, + * where the vnode is write locked locally and and the server. + * + * \return Mask of operations. + */ +int afs_GenStoreStatus(struct vcache *avc, struct AFSStoreStatus *astat) +{ + if (!avc || !astat || !avc->ddirty_flags) + return 0; + + /* Clean up store stat. */ + memset(astat, 0, sizeof(struct AFSStoreStatus)); + + if (avc->ddirty_flags & VDisconSetTime) { + /* Update timestamp. */ + astat->ClientModTime = avc->m.Date; + astat->Mask |= AFS_SETMODTIME; + } + + if (avc->ddirty_flags & VDisconSetMode) { + /* Copy the mode bits. */ + astat->UnixModeBits = avc->m.Mode; + astat->Mask |= AFS_SETMODE; + } + + /* XXX: more to come... ?*/ + + return astat->Mask; +} + +/*! + * Hook for filtering the local dir fid by searching the "." entry. + * + * \param hdata The fid to be filled. + */ +int get_parent_dir_fid_hook(void *hdata, + char *aname, + afs_int32 vnode, + afs_int32 unique) +{ + struct VenusFid *tfid = (struct VenusFid *) hdata; + + if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) { + tfid->Fid.Vnode = vnode; + tfid->Fid.Unique = unique; + return 1; + } + + return 0; +} + +/*! + * Get a the dir's fid by looking in the vcache for simple files and + * in the ".." entry for directories. + * + * \param avc The file's vhash entry. + * \param afid Put the fid here. + */ +int afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid) +{ + struct dcache *tdc; + + afid->Cell = avc->fid.Cell; + afid->Fid.Volume = avc->fid.Fid.Volume; + + if (vType(avc) == VREG) { + /* Normal files have the dir fid embedded in the vcache. */ + afid->Fid.Vnode = avc->parentVnode; + afid->Fid.Unique = avc->parentUnique; + + } else if (vType(avc) == VDIR) { + /* If dir or parent dir created locally*/ + tdc = afs_FindDCacheByFid(&avc->fid); + if (tdc) { + /* Lookup each entry for the fid. It should be the first. */ + afs_dir_EnumerateDir(tdc, &get_parent_dir_fid_hook, afid); + afs_PutDCache(tdc); + } + } + + return 0; +} + +struct NameAndFid { + struct VenusFid *fid; + char *name; + int name_len; +}; + +/*! + * Hook that searches a certain fid's name. + * + * \param hdata NameAndFid structure containin a pointer to a fid + * and an allocate name. The name will be filled when hit. + */ +int get_vnode_name_hook(void *hdata, + char *aname, + afs_int32 vnode, + afs_int32 unique) +{ + struct NameAndFid *nf = (struct NameAndFid *) hdata; + + if ((nf->fid->Fid.Vnode == vnode) && + (nf->fid->Fid.Unique == unique)) { + nf->name_len = strlen(aname); + memcpy(nf->name, aname, nf->name_len); + nf->name[nf->name_len] = 0; + + return 1; + } + + return 0; +} + +/*! + * 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->fid.Cell; + parent_fid.Fid.Volume = avc->fid.Fid.Volume; + if (avc->ddirty_flags & VDisconRename) { + /* For renames the old dir fid is needed. */ + parent_fid.Fid.Vnode = avc->oldVnode; + parent_fid.Fid.Unique = avc->oldUnique; + } 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->fid.Cell; + shadow_fid.Fid.Volume = parent_vc->fid.Fid.Volume; + shadow_fid.Fid.Vnode = parent_vc->shVnode; + shadow_fid.Fid.Unique = parent_vc->shUnique; + + 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->fid; + tnf.name_len = 0; + tnf.name = aname; + afs_dir_EnumerateDir(tdc, &get_vnode_name_hook, &tnf); + afs_PutDCache(tdc); + } else { + code = ENOENT; + } + + return code; +} + +struct DirtyChildrenCount { + struct vcache *vc; + afs_uint32 count; +}; + +/*! + * Lookup dirty deleted vnodes in this dir. + */ +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->fid.Cell; + tfid.Fid.Volume = v->vc->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. VDisconShadowed can still be set, + * because we need it to remove the shadow dir. + */ + if (tvc && tvc->ddirty_flags) { + v->count++; + afs_PutVCache(tvc); + } + + return 0; +} + +/*! + * Check if entries have been deleted in a vnode's shadow + * dir. + * + * \return Returns the number of dirty children. + * + * \note afs_DDirtyVCListLock must be write locked. + */ +int afs_CheckDeletedChildren(struct vcache *avc) +{ + struct dcache *tdc; + struct DirtyChildrenCount dcc; + struct VenusFid shadow_fid; + + if (!(avc->ddirty_flags & VDisconShadowed)) + /* Empty dir. */ + return 0; + + shadow_fid.Cell = avc->fid.Cell; + shadow_fid.Fid.Volume = avc->fid.Fid.Volume; + shadow_fid.Fid.Vnode = avc->shVnode; + shadow_fid.Fid.Unique = avc->shUnique; + + dcc.count = 0; + + /* Get shadow dir's dcache. */ + tdc = afs_FindDCacheByFid(&shadow_fid); + if (tdc) { + dcc.vc = avc; + afs_dir_EnumerateDir(tdc, &chk_del_children_hook, &dcc); + afs_PutDCache(tdc); + } + + return dcc.count; +} + +/*! + * Changes a file's parent fid references. + */ +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->parentVnode = afid->Fid.Vnode; + tvc->parentUnique = 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; +} + +/*! + * Fixes the parentVnode and parentUnique fields of all + * files (not dirs) contained in the directory pointed by + * old_fid. This is useful on resync, when a locally created dir + * get's a new fid and all the children references must be updated + * to reflect the new fid. + * + * \note The dir's fid hasn't been changed yet, it is still referenced + * with the old fid. + * + * \param old_fid The current dir's fid. + * \param new_fid The new dir's fid. + */ +void afs_FixChildrenFids(struct VenusFid *old_fid, struct VenusFid *new_fid) +{ + struct dcache *tdc; + + /* Get shadow dir's dcache. */ + tdc = afs_FindDCacheByFid(old_fid); + /* Change the fids. */ + if (tdc) { + afs_dir_EnumerateDir(tdc, &fix_children_fids_hook, new_fid); + afs_PutDCache(tdc); + } +} + +int list_dir_hook(void *hdata, char *aname, afs_int32 vnode, afs_int32 unique) +{ + printf("list_dir_hook: %s v:%u u:%u\n", aname, vnode, unique); + return 0; +} + +void afs_DbgListDirEntries(struct VenusFid *afid) +{ + struct dcache *tdc; + + /* Get shadow dir's dcache. */ + tdc = afs_FindDCacheByFid(afid); + if (tdc) { + afs_dir_EnumerateDir(tdc, &list_dir_hook, NULL); + afs_PutDCache(tdc); + } +} + +/*! + * Handles file renaming on reconnection: + * - Get the old name from the old dir's shadow dir. + * - Get the new name from the current dir. + * - Old dir fid and new dir fid are collected along the way. + * */ +int afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq) +{ + struct VenusFid old_pdir_fid, new_pdir_fid; + char *old_name, *new_name; + struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus; + struct AFSVolSync tsync; + struct conn *tc; + afs_uint32 code = 0; + XSTATS_DECLS; + + /* Get old dir vcache. */ + old_pdir_fid.Cell = avc->fid.Cell; + old_pdir_fid.Fid.Volume = avc->fid.Fid.Volume; + old_pdir_fid.Fid.Vnode = avc->oldVnode; + old_pdir_fid.Fid.Unique = avc->oldUnique; + + /* Get old name. */ + old_name = (char *) afs_osi_Alloc(AFSNAMEMAX); + if (!old_name) { + printf("afs_ProcessOpRename: Couldn't alloc space for old name.\n"); + return ENOMEM; + } + code = afs_GetVnodeName(avc, &old_pdir_fid, old_name, 1); + if (code) { + printf("afs_ProcessOpRename: Couldn't find old name.\n"); + code = ENOENT; + goto end2; + } + + /* Alloc data first. */ + new_name = (char *) afs_osi_Alloc(AFSNAMEMAX); + if (!new_name) { + printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n"); + code = ENOMEM; + goto end2; + } + + if (avc->ddirty_flags & VDisconRenameSameDir) { + /* If we're in the same dir, don't do the lookups all over again, + * just copy fid and vcache from the old dir. + */ + memcpy(&new_pdir_fid, &old_pdir_fid, sizeof(struct VenusFid)); + } else { + /* Get parent dir's FID.*/ + new_pdir_fid.Fid.Unique = 0; + afs_GetParentDirFid(avc, &new_pdir_fid); + if (!new_pdir_fid.Fid.Unique) { + printf("afs_ProcessOpRename: Couldn't find new parent dir FID.\n"); + code = ENOENT; + goto end1; + } + } + + /* And finally get the new name. */ + code = afs_GetVnodeName(avc, &new_pdir_fid, new_name, 0); + if (code) { + printf("afs_ProcessOpRename: Couldn't find new name.\n"); + code = ENOENT; + goto end1; + } + + /* Send to data to server. */ + do { + tc = afs_Conn(&old_pdir_fid, areq, SHARED_LOCK); + if (tc) { + XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME); + RX_AFS_GUNLOCK(); + code = RXAFS_Rename(tc->id, + (struct AFSFid *)&old_pdir_fid.Fid, + old_name, + (struct AFSFid *)&new_pdir_fid.Fid, + new_name, + &OutOldDirStatus, + &OutNewDirStatus, + &tsync); + RX_AFS_GLOCK(); + XSTATS_END_TIME; + } else + code = -1; + + } while (afs_Analyze(tc, + code, + &new_pdir_fid, + areq, + AFS_STATS_FS_RPCIDX_RENAME, + SHARED_LOCK, + NULL)); + + if (code) + printf("afs_ProcessOpRename: server code=%u\n", code); +end1: + afs_osi_Free(new_name, AFSNAMEMAX); +end2: + afs_osi_Free(old_name, AFSNAMEMAX); + 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, + struct AFS_UCRED *acred) +{ + char *tname = NULL; + struct AFSStoreStatus InStatus; + struct AFSFetchStatus OutFidStatus, OutDirStatus; + struct VenusFid pdir_fid, newFid; + struct server *hostp = NULL; + struct AFSCallBack CallBack; + struct AFSVolSync tsync; + struct vcache *tdp = NULL, *tvc = NULL; + struct dcache *tdc = NULL; + struct conn *tc; + afs_int32 now, hash, new_hash, index; + int code = 0; + XSTATS_DECLS; + + /* Get parent dir's FID. */ + pdir_fid.Fid.Unique = 0; + afs_GetParentDirFid(avc, &pdir_fid); + if (!pdir_fid.Fid.Unique) { + printf("afs_ProcessOpCreate: Couldn't find parent dir'sFID.\n"); + return ENOENT; + } + + tname = afs_osi_Alloc(AFSNAMEMAX); + if (!tname) { + printf("afs_ProcessOpCreate: Couldn't find file name\n"); + return ENOMEM; + } + + /* Get vnode's name. */ + code = afs_GetVnodeName(avc, &pdir_fid, tname, 0); + if (code) { + printf("afs_ProcessOpCreate: Couldn't find file name\n"); + code = ENOENT; + goto end; + } + + /* Get parent dir vcache. */ + ObtainSharedLock(&afs_xvcache, 760); + tdp = afs_FindVCache(&pdir_fid, 0, 1); + ReleaseSharedLock(&afs_xvcache); + if (!tdp) { + printf("afs_ProcessOpCreate: Couldn't find parent dir's vcache\n"); + code = ENOENT; + goto end; + } + + if (tdp->ddirty_flags & VDisconCreate) { + /* If the parent dir has been created locally, defer + * this vnode for later by moving it to the end. + */ + afs_DDirtyVCList->ddirty_next = avc; + afs_DDirtyVCList = avc; + printf("afs_ProcessOpRemove: deferring this vcache\n"); + code = ENOTEMPTY; + goto end; + } + + /* Set status. */ + InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP; + InStatus.ClientModTime = avc->m.Date; + InStatus.Owner = avc->m.Owner; + InStatus.Group = (afs_int32) acred->cr_gid; + /* Only care about protection bits. */ + InStatus.UnixModeBits = avc->m.Mode & 0xffff; + + /* Connect to server. */ + if (vType(avc) == VREG) { + /* Make file on server. */ + do { + tc = afs_Conn(&tdp->fid, areq, SHARED_LOCK); + if (tc) { + /* Remember for callback processing. */ + hostp = tc->srvr->server; + now = osi_Time(); + XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_CREATEFILE); + RX_AFS_GUNLOCK(); + code = RXAFS_CreateFile(tc->id, + (struct AFSFid *)&tdp->fid.Fid, + tname, + &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, + code, + &tdp->fid, + areq, + AFS_STATS_FS_RPCIDX_CREATEFILE, + SHARED_LOCK, + NULL)); + + } else if (vType(avc) == VDIR) { + /* Make dir on server. */ + do { + tc = afs_Conn(&tdp->fid, areq, SHARED_LOCK); + if (tc) { + XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_MAKEDIR); + now = osi_Time(); + RX_AFS_GUNLOCK(); + code = RXAFS_MakeDir(tc->id, + (struct AFSFid *) &tdp->fid.Fid, + tname, + &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 at some point. */ + } else + code = -1; + } while (afs_Analyze(tc, + code, + &tdp->fid, + areq, + AFS_STATS_FS_RPCIDX_MAKEDIR, + SHARED_LOCK, + NULL)); + } /* Do server changes. */ + + /* TODO: Handle errors. */ + if (code) { + printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); + code = EIO; + goto end; + } + + /* The rpc doesn't set the cell number. */ + newFid.Cell = avc->fid.Cell; + + /* + * Change the fid in the dir entry. + */ + + /* Seek the dir's dcache. */ + tdc = afs_FindDCacheByFid(&tdp->fid); + if (tdc) { + /* And now change the fid in the parent dir entry. */ + afs_dir_ChangeFid(tdc, tname, &avc->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->fid); + if (tdc) { + afs_dir_ChangeFid(tdc, ".", &avc->fid.Fid.Vnode, &newFid.Fid.Vnode); + + if (avc->m.LinkCount >= 2) + /* For non empty dirs, fix children's parentVnode and parentUnique + * reference. + */ + afs_FixChildrenFids(&avc->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->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]; tdp; tdp = tdp->hnext) { + for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) { + if (tvc->hnext == avc) { + tvc->hnext = avc->hnext; + break; + } + } + } /* if (!afs_vhashT[i]->hnext) */ + QRemove(&afs_vhashTV[hash]); + + /* Insert hash in new position. */ + avc->hnext = afs_vhashT[new_hash]; + afs_vhashT[new_hash] = avc; + QAdd(&afs_vhashTV[new_hash], &avc->vhashq); + + ReleaseWriteLock(&afs_xvcache); + + /* Do the same thing for all dcaches. */ + hash = DVHash(&avc->fid); + ObtainWriteLock(&afs_xdcache, 743); + for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) { + hash = afs_dvnextTbl[index]; + tdc = afs_GetDSlot(index, NULL); + ReleaseReadLock(&tdc->tlock); + if (afs_indexUnique[index] == avc->fid.Fid.Unique) { + if (!FidCmp(&tdc->f.fid, &avc->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)); + //afs_MaybeWakeupTruncateDaemon(); + } /* 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->fid, &newFid, sizeof(struct VenusFid)); + + if (tdp) { + /* Unset parent dir CStat flag, so it will get refreshed on next + * online stat. + */ + ObtainWriteLock(&tdp->lock, 745); + tdp->states &= ~CStatd; + ReleaseWriteLock(&tdp->lock); + } +end: + if (tdp) + afs_PutVCache(tdp); + afs_osi_Free(tname, AFSNAMEMAX); + return code; +} + +/*! + * Remove a vnode on the server, be it file or directory. + * Not much to do here only get the parent dir's fid and call the + * removel rpc. + * + * \param avc The deleted vcache + * \param areq + * + * \note The vcache refcount should be dropped because it points to + * a deleted vnode and has served it's purpouse, but we drop refcount + * on shadow dir deletio (we still need it for that). + * + * \note avc must be write locked. + */ +int afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq) +{ + char *tname = NULL; + struct AFSFetchStatus OutDirStatus; + struct VenusFid pdir_fid; + struct AFSVolSync tsync; + struct conn *tc; + struct vcache *tdp = NULL; + int code = 0; + XSTATS_DECLS; + + /* Get parent dir's FID. */ + pdir_fid.Fid.Unique = 0; + afs_GetParentDirFid(avc, &pdir_fid); + if (!pdir_fid.Fid.Unique) { + printf("afs_ProcessOpRemove: Couldn't find parent dir's FID.\n"); + return ENOENT; + } + + tname = afs_osi_Alloc(AFSNAMEMAX); + if (!tname) { + printf("afs_ProcessOpRemove: Couldn't find file name\n"); + return ENOMEM; + } + + /* Get file name. */ + code = afs_GetVnodeName(avc, &pdir_fid, tname, 1); + if (code) { + printf("afs_ProcessOpRemove: Couldn't find file name\n"); + code = ENOENT; + goto end; + } + + if ((vType(avc) == VDIR) && (afs_CheckDeletedChildren(avc))) { + /* Deleted children of this dir remain unsynchronized. + * Defer this vcache. + */ + afs_DDirtyVCList->ddirty_next = avc; + afs_DDirtyVCList = avc; + code = ENOTEMPTY; + goto end; + } + + if (vType(avc) == VREG) { + /* Remove file on server. */ + do { + tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK); + if (tc) { + XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE); + RX_AFS_GUNLOCK(); + code = RXAFS_RemoveFile(tc->id, + &pdir_fid.Fid, + tname, + &OutDirStatus, + &tsync); + + RX_AFS_GLOCK(); + XSTATS_END_TIME; + } else + code = -1; + } while (afs_Analyze(tc, + code, + &pdir_fid, + areq, + AFS_STATS_FS_RPCIDX_REMOVEFILE, + SHARED_LOCK, + NULL)); + + } else if (vType(avc) == VDIR) { + /* Remove dir on server. */ + do { + tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK); + if (tc) { + XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR); + RX_AFS_GUNLOCK(); + code = RXAFS_RemoveDir(tc->id, + &pdir_fid.Fid, + tname, + &OutDirStatus, + &tsync); + RX_AFS_GLOCK(); + XSTATS_END_TIME; + } else + code = -1; + } while (afs_Analyze(tc, + code, + &pdir_fid, + areq, + AFS_STATS_FS_RPCIDX_REMOVEDIR, + SHARED_LOCK, + NULL)); + + } /* if (vType(avc) == VREG) */ + + if (code) + printf("afs_ProcessOpRemove: server returned code=%u\n", code); + + /* Remove the statd flag from parent dir's vcache. */ + ObtainSharedLock(&afs_xvcache, 761); + tdp = afs_FindVCache(&pdir_fid, 0, 1); + ReleaseSharedLock(&afs_xvcache); + if (tdp) { + ObtainWriteLock(&tdp->lock, 746); + tdp->states &= ~CStatd; + ReleaseWriteLock(&tdp->lock); + afs_PutVCache(tdp); + } +end: + afs_osi_Free(tname, AFSNAMEMAX); + return code; +} + +/*! + * Send disconnected file changes to the server. + * + * \note Call with vnode locked both locally and on the server. + * + * \param avc Vnode that gets synchronized to the server. + * \param areq Used for obtaining a conn struct. + * + * \return 0 for success. On failure, other error codes. + */ +int afs_SendChanges(struct vcache *avc, struct vrequest *areq) +{ + struct conn *tc; + struct AFSStoreStatus sstat; + struct AFSFetchStatus fstat; + struct AFSVolSync tsync; + int code = 0; + int flags = 0; + XSTATS_DECLS; + + /* Start multiplexing dirty operations from ddirty_flags field: */ + if (avc->ddirty_flags & VDisconSetAttrMask) { + /* Setattr OPS: */ + /* Turn dirty vc data into a new store status... */ + if (afs_GenStoreStatus(avc, &sstat) > 0) { + do { + tc = afs_Conn(&avc->fid, areq, SHARED_LOCK); + if (tc) { + /* ... and send it. */ + XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STORESTATUS); + RX_AFS_GUNLOCK(); + code = RXAFS_StoreStatus(tc->id, + (struct AFSFid *) &avc->fid.Fid, + &sstat, + &fstat, + &tsync); + + RX_AFS_GLOCK(); + XSTATS_END_TIME; + } else + code = -1; + + } while (afs_Analyze(tc, + code, + &avc->fid, + areq, + AFS_STATS_FS_RPCIDX_STORESTATUS, + SHARED_LOCK, + NULL)); + + } /* if (afs_GenStoreStatus() > 0)*/ + } /* disconnected SETATTR */ + + if (code) + return code; + + if (avc->ddirty_flags & + (VDisconTrunc + | VDisconWriteClose + | VDisconWriteFlush + | VDisconWriteOsiFlush)) { + + /* Truncate OP: */ + do { + tc = afs_Conn(&avc->fid, areq, SHARED_LOCK); + if (tc) { + /* Set storing flags. XXX: A tad inefficient ... */ + if (avc->ddirty_flags & VDisconWriteClose) + flags |= AFS_LASTSTORE; + if (avc->ddirty_flags & VDisconWriteOsiFlush) + flags |= (AFS_SYNC | AFS_LASTSTORE); + if (avc->ddirty_flags & VDisconWriteFlush) + flags |= AFS_SYNC; + + /* Try to send store to server. */ + /* XXX: AFS_LASTSTORE for writes? Or just AFS_SYNC for all? */ + code = afs_StoreAllSegments(avc, areq, flags); + } else + code = -1; + + } while (afs_Analyze(tc, + code, + &avc->fid, + areq, + AFS_STATS_FS_RPCIDX_STOREDATA, + SHARED_LOCK, + NULL)); + + } /* disconnected TRUNC | WRITE */ + + 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 1. + * + * \note For now, it's the request from the PDiscon pioctl. + * + */ +int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred) +{ + struct conn *tc; + struct vcache *tvc, *tmp; + struct AFSFetchStatus fstat; + struct AFSCallBack callback; + struct AFSVolSync tsync; + struct vcache *shList, *shListStart; + int code; + int sync_failed = 0; + int ret_code = 0; + int defered = 0; + afs_int32 start = 0; + XSTATS_DECLS; + //AFS_STATCNT(afs_ResyncDisconFiles); + + shList = shListStart = NULL; + + ObtainReadLock(&afs_DDirtyVCListLock); + + tvc = afs_DDirtyVCListStart; + while (tvc) { + + /* Get local write lock. */ + ObtainWriteLock(&tvc->lock, 704); + sync_failed = 0; + + if ((tvc->ddirty_flags & VDisconRemove) && + (tvc->ddirty_flags & VDisconCreate)) { + /* Data created and deleted locally. The server doesn't + * need to know about this, so we'll just skip this file + * from the dirty list. + */ + goto skip_file; + + } else if (tvc->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); + if (code == ENOTEMPTY) + defered = 1; + goto skip_file; + + } else if (tvc->ddirty_flags & VDisconCreate) { + /* For newly created files, we don't need a server lock. */ + code = afs_ProcessOpCreate(tvc, areq, acred); + if (code == ENOTEMPTY) + defered = 1; + if (code) + goto skip_file; + } + + /* Get server write lock. */ + do { + tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK); + if (tc) { + XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK); + RX_AFS_GUNLOCK(); + code = RXAFS_SetLock(tc->id, + (struct AFSFid *)&tvc->fid.Fid, + LockWrite, + &tsync); + RX_AFS_GLOCK(); + XSTATS_END_TIME; + } else + code = -1; + + } while (afs_Analyze(tc, + code, + &tvc->fid, + areq, + AFS_STATS_FS_RPCIDX_SETLOCK, + SHARED_LOCK, + NULL)); + + if (code) { + sync_failed = 1; + goto skip_file; + } + + if ((tvc->ddirty_flags & VDisconRename) && + !(tvc->ddirty_flags & VDisconCreate)) { + /* Rename file only if it hasn't been created locally. */ + code = afs_ProcessOpRename(tvc, areq); + if (code) + goto skip_file; + } + + /* Issue a FetchStatus to get info about DV and callbacks. */ + do { + tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK); + if (tc) { + tvc->callback = tc->srvr->server; + start = osi_Time(); + XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS); + RX_AFS_GUNLOCK(); + code = RXAFS_FetchStatus(tc->id, + (struct AFSFid *)&tvc->fid.Fid, + &fstat, + &callback, + &tsync); + RX_AFS_GLOCK(); + XSTATS_END_TIME; + } else + code = -1; + + } while (afs_Analyze(tc, + code, + &tvc->fid, + areq, + AFS_STATS_FS_RPCIDX_FETCHSTATUS, + SHARED_LOCK, + NULL)); + + if (code) { + sync_failed = 1; + goto unlock_srv_file; + } + + if ((dv_match(tvc, fstat) && (tvc->m.Date == fstat.ServerModTime)) || + (afs_ConflictPolicy == CLIENT_WINS) || + (tvc->ddirty_flags & VDisconCreate)) { + /* + * 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->fid, areq, &fstat, &callback, start); + code = afs_SendChanges(tvc, areq); + + } else if (afs_ConflictPolicy == SERVER_WINS) { + /* DV mismatch, apply collision resolution policy. */ + /* Dequeue whatever callback is on top (XXX: propably none). */ + ObtainWriteLock(&afs_xcbhash, 706); + afs_DequeueCallback(tvc); + tvc->callback = NULL; + tvc->states &= ~(CStatd | CDirty | CUnique); + ReleaseWriteLock(&afs_xcbhash); + + /* Save metadata. File length gets updated as well because we + * just removed CDirty from the avc. + */ + //afs_ProcessFS(tvc, &fstat, areq); + + /* Discard this files chunks and remove from current dir. */ + afs_TryToSmush(tvc, acred, 1); + osi_dnlc_purgedp(tvc); + if (tvc->linkData && !(tvc->states & CCore)) { + /* Take care of symlinks. */ + afs_osi_Free(tvc->linkData, strlen(tvc->linkData) + 1); + tvc->linkData = NULL; + } + + /* Otherwise file content's won't be synchronized. */ + tvc->truncPos = AFS_NOTRUNC; + + } else { + printf("afs_ResyncDisconFiles: no resolution policy selected.\n"); + } /* if DV match or client wins policy */ + + if (code) { + sync_failed = 1; + printf("Sync FAILED.\n"); + } + +unlock_srv_file: + /* Release server write lock. */ + do { + tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK); + if (tc) { + XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK); + RX_AFS_GUNLOCK(); + code = RXAFS_ReleaseLock(tc->id, + (struct AFSFid *) &tvc->fid.Fid, + &tsync); + RX_AFS_GLOCK(); + XSTATS_END_TIME; + } else + code = -1; + } while (afs_Analyze(tc, + code, + &tvc->fid, + areq, + AFS_STATS_FS_RPCIDX_RELEASELOCK, + SHARED_LOCK, + NULL)); + +skip_file: + /* Pop this dirty vc out. */ + tmp = tvc; + tvc = tvc->ddirty_next; + + if (!defered) { + /* Vnode not deferred. Clean it up. */ + if (!sync_failed) { + if (tmp->ddirty_flags == VDisconShadowed) { + /* Dirs that have only the shadow flag set might still + * be used so keep them in a different list, that gets + * deleted after resync is done. + */ + if (!shListStart) + shListStart = shList = tmp; + else { + shList->ddirty_next = tmp; + shList = tmp; + } + } else if (tmp->ddirty_flags & VDisconShadowed) + /* We can discard the shadow dir now. */ + afs_DeleteShadowDir(tmp); + + if (tmp->ddirty_flags & VDisconRemove) + /* Drop the refcount on the deleted vnodes, + * because we don't need them anymore. + */ + afs_PutVCache(tmp); + + /* Only if sync was successfull, + * clear flags and dirty references. + */ + tmp->ddirty_next = NULL; + tmp->ddirty_flags = 0; + } else + ret_code = 1; + } else { + tmp->ddirty_next = NULL; + defered = 0; + } /* if (!defered) */ + + /* Release local write lock. */ + ReleaseWriteLock(&tmp->lock); + } /* while (tvc) */ + + /* Delete the rest of shadow dirs. */ + tvc = shListStart; + while (tvc) { + ObtainWriteLock(&tvc->lock, 764); + + afs_DeleteShadowDir(tvc); + tvc->shVnode = 0; + tvc->shUnique = 0; + + tmp = tvc; + tvc = tvc->ddirty_next; + tmp->ddirty_next = NULL; + + ReleaseWriteLock(&tmp->lock); + } /* while (tvc) */ + + if (ret_code == 0) { + /* NULLIFY dirty list only if resync complete. */ + afs_DDirtyVCListStart = NULL; + afs_DDirtyVCList = NULL; + } + ReleaseReadLock(&afs_DDirtyVCListLock); + + return ret_code; +} + +/*! + * Print list of disconnected files. + * + * \note Call with afs_DDirtyVCListLock read locked. + */ +void afs_DbgDisconFiles() +{ + struct vcache *tvc; + int i = 0; + + tvc = afs_DDirtyVCListStart; + printf("List of dirty files: \n"); + while (tvc) { + printf("Cell=%u Volume=%u VNode=%u Unique=%u\n", + tvc->fid.Cell, + tvc->fid.Fid.Volume, + tvc->fid.Fid.Vnode, + tvc->fid.Fid.Unique); + tvc = tvc->ddirty_next; + i++; + if (i >= 30) + osi_Panic("afs_DbgDisconFiles: loop in dirty list\n"); + } +} + +/*! + * 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 { + 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); +} + +/*! + * Generate a fake fid (vnode and uniquifier) for a vcache + * (either dir or normal file). The vnode is generated via + * afs_DisconVNode and the uniquifier by getting the highest + * uniquifier on a hash chain and incrementing it by one. + * + * \param avc afid The fid structre that will be filled. + * \param avtype Vnode type: VDIR/VREG. + * + * \note The cell number must be completed somewhere else. + */ +void afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype) +{ + struct vcache *tvc; + afs_uint32 max_unique = 1, i; + + if (avtype == VDIR) + afid->Fid.Vnode = afs_DisconVnode + 1; + else if (avtype == VREG) + afid->Fid.Vnode = afs_DisconVnode; + + ObtainWriteLock(&afs_xvcache, 736); + i = VCHash(afid); + for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) { + if (tvc->fid.Fid.Unique > max_unique) + max_unique = tvc->fid.Fid.Unique; + } + ReleaseWriteLock(&afs_xvcache); + + afid->Fid.Unique = max_unique + 1; + afs_DisconVnode += 2; + if (!afs_DisconVnode) + afs_DisconVnode = 2; +} + +/*! + * Fill in stats for a newly created file/directory. + * + * \param adp The parent dir's vcache. + * \param avc The created vnode. + * \param afid The new fid. + * \param attrs + * \param areq + * \param file_type Specify if file or directory. + * + * \note Call with avc write locked. + */ +void afs_GenDisconStatus( + struct vcache *adp, + struct vcache *avc, + struct VenusFid *afid, + struct vattr *attrs, + struct vrequest *areq, + int file_type) +{ + memcpy(&avc->fid, afid, sizeof(struct VenusFid)); + avc->m.Mode = attrs->va_mode; + /* Used to do this: + * avc->m.Owner = attrs->va_uid; + * But now we use the parent dir's ownership, + * there's no other way to get a server owner id. + * XXX: Does it really matter? + */ + avc->m.Group = adp->m.Group; + avc->m.Owner = adp->m.Owner; + hset64(avc->m.DataVersion, 0, 0); + avc->m.Length = attrs->va_size; + avc->m.Date = osi_Time(); + if (file_type == VREG) { + vSetType(avc, VREG); + avc->m.Mode |= S_IFREG; + avc->m.LinkCount = 1; + } else if (file_type == VDIR) { + vSetType(avc, VDIR); + avc->m.Mode |= S_IFDIR; + avc->m.LinkCount = 2; + } + + avc->anyAccess = adp->anyAccess; + afs_AddAxs(avc->Access, areq->uid, adp->Access->axess); + + avc->callback = NULL; + avc->states |= CStatd; + avc->states &= ~CBulkFetching; +} #endif diff --git a/src/afs/afs_init.c b/src/afs/afs_init.c index baebcdec9..c4fd03164 100644 --- a/src/afs/afs_init.c +++ b/src/afs/afs_init.c @@ -47,6 +47,7 @@ static struct vnode *volumeVnode; #endif #if defined(AFS_DISCON_ENV) afs_rwlock_t afs_discon_lock; +extern afs_rwlock_t afs_DDirtyVCListLock; #endif /* @@ -115,6 +116,7 @@ afs_CacheInit(afs_int32 astatSize, afs_int32 afiles, afs_int32 ablocks, RWLOCK_INIT(&afs_xaxs, "afs_xaxs"); #ifdef AFS_DISCON_ENV RWLOCK_INIT(&afs_discon_lock, "afs_discon_lock"); + RWLOCK_INIT(&afs_DDirtyVCListLock, "afs_DDirtyVCListLock"); #endif osi_dnlc_init(); diff --git a/src/afs/afs_pioctl.c b/src/afs/afs_pioctl.c index b68ed304c..2389b5fda 100644 --- a/src/afs/afs_pioctl.c +++ b/src/afs/afs_pioctl.c @@ -33,6 +33,11 @@ afs_int32 afs_showflags = GAGUSER | GAGCONSOLE; /* show all messages */ #ifdef AFS_DISCON_ENV afs_int32 afs_is_disconnected; afs_int32 afs_is_logging; +afs_int32 afs_is_discon_rw; +/* On reconnection, turn this knob on until it finishes, + * then turn it off. + */ +afs_int32 afs_in_sync = 0; #endif #define DECL_PIOCTL(x) static int x(struct vcache *avc, int afun, struct vrequest *areq, \ @@ -3976,32 +3981,53 @@ DECL_PIOCTL(PCallBackAddr) DECL_PIOCTL(PDiscon) { #ifdef AFS_DISCON_ENV - static afs_int32 mode = 4; /* Start up in 'full' */ + static afs_int32 mode = 1; /* Start up in 'off' */ + afs_int32 force = 0; + int code = 0; - if (ainSize == sizeof(afs_int32)) { + if (ainSize) { if (!afs_osi_suser(*acred)) return EPERM; - memcpy(&mode, ain, sizeof(afs_int32)); + if (ain[0]) + mode = ain[0] - 1; + if (ain[1]) + afs_ConflictPolicy = ain[1] - 1; + if (ain[2]) + force = 1; /* * All of these numbers are hard coded in fs.c. If they * change here, they should change there and vice versa */ switch (mode) { - case 0: /* Disconnect, breaking all callbacks */ + case 0: /* Disconnect ("offline" mode), breaking all callbacks */ if (!AFS_IS_DISCONNECTED) { ObtainWriteLock(&afs_discon_lock, 999); afs_DisconGiveUpCallbacks(); afs_RemoveAllConns(); afs_is_disconnected = 1; + afs_is_discon_rw = 1; ReleaseWriteLock(&afs_discon_lock); } break; - case 4: /* Fully connected */ + case 1: /* Fully connected, ("online" mode). */ ObtainWriteLock(&afs_discon_lock, 998); - afs_is_disconnected = 0; + + afs_in_sync = 1; + code = afs_ResyncDisconFiles(areq, *acred); + afs_in_sync = 0; + + if (code && !force) { + printf("Files not synchronized properly, still in discon state. \ + Please retry or use \"force\".\n"); + } else { + afs_is_disconnected = 0; + afs_is_discon_rw = 0; + printf("\nSync succeeded. You are back online.\n"); + } + ReleaseWriteLock(&afs_discon_lock); break; default: diff --git a/src/afs/afs_prototypes.h b/src/afs/afs_prototypes.h index dfb9a538c..da0e66717 100644 --- a/src/afs/afs_prototypes.h +++ b/src/afs/afs_prototypes.h @@ -255,7 +255,7 @@ extern void afs_MaybeWakeupTruncateDaemon(void); extern void afs_CacheTruncateDaemon(void); extern void afs_AdjustSize(register struct dcache *adc, register afs_int32 newSize); -extern int afs_HashOutDCache(struct dcache *adc); +extern int afs_HashOutDCache(struct dcache *adc, int zap); extern int afs_MaybeFreeDiscardedDCache(void); extern int afs_RefDCache(struct dcache *adc); extern void afs_TryToSmush(register struct vcache *avc, diff --git a/src/afs/afs_segments.c b/src/afs/afs_segments.c index 3f3572d8e..23818dda3 100644 --- a/src/afs/afs_segments.c +++ b/src/afs/afs_segments.c @@ -215,7 +215,7 @@ afs_StoreAllSegments(register struct vcache *avc, struct vrequest *areq, #endif osi_VM_StoreAllSegments(avc); } - if (AFS_IS_DISCONNECTED) { + if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) { if (!AFS_IS_LOGGING) { /* This will probably make someone sad ... */ /*printf("Net down in afs_StoreSegments\n");*/ diff --git a/src/afs/afs_vcache.c b/src/afs/afs_vcache.c index 59b585bc9..79274d990 100644 --- a/src/afs/afs_vcache.c +++ b/src/afs/afs_vcache.c @@ -19,6 +19,7 @@ * afs_FlushActiveVcaches * afs_VerifyVCache2 * afs_WriteVCache + * afs_WriteVCacheDiscon * afs_SimpleVStat * afs_ProcessFS * TellALittleWhiteLie @@ -26,6 +27,7 @@ * afs_GetVCache * afs_LookupVCache * afs_GetRootVCache + * afs_UpdateStatus * afs_FetchStatus * afs_StuffVcache * afs_PutVCache @@ -942,6 +944,10 @@ restart: tvc->Access = NULL; tvc->callback = serverp; /* to minimize chance that clear * request is lost */ +#if defined(AFS_DISCON_ENV) + tvc->ddirty_next = NULL; + tvc->ddirty_flags = 0; +#endif i = VCHash(afid); j = VCHashV(afid); @@ -1507,7 +1513,6 @@ afs_WriteVCache(register struct vcache *avc, AFS_STATCNT(afs_WriteVCache); afs_Trace2(afs_iclSetp, CM_TRACE_WVCACHE, ICL_TYPE_POINTER, avc, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->m.Length)); - do { tc = afs_Conn(&avc->fid, areq, SHARED_LOCK); if (tc) { @@ -1547,6 +1552,98 @@ afs_WriteVCache(register struct vcache *avc, return code; } /*afs_WriteVCache */ +#if defined(AFS_DISCON_ENV) + +/*! + * Store status info only locally, set the proper disconnection flags + * and add to dirty list. + * + * \param avc The vcache to be written locally. + * \param astatus Get attr fields from local store. + * \param attrs This one is only of the vs_size. + * + * \note Must be called with a shared lock on the vnode + */ +int afs_WriteVCacheDiscon(register struct vcache *avc, + register struct AFSStoreStatus *astatus, + struct vattr *attrs) +{ + afs_int32 code = 0; + afs_int32 flags = 0; + + UpgradeSToWLock(&avc->lock, 700); + + if (!astatus->Mask) { + + return code; + + } else { + + /* Set attributes. */ + if (astatus->Mask & AFS_SETMODTIME) { + avc->m.Date = astatus->ClientModTime; + flags |= VDisconSetTime; + } + + if (astatus->Mask & AFS_SETOWNER) { + printf("Not allowed yet. \n"); + //avc->m.Owner = astatus->Owner; + } + + if (astatus->Mask & AFS_SETGROUP) { + printf("Not allowed yet. \n"); + //avc->m.Group = astatus->Group; + } + + if (astatus->Mask & AFS_SETMODE) { + avc->m.Mode = astatus->UnixModeBits; + +#if 0 /* XXX: Leaving this out, so it doesn't mess up the file type flag.*/ + + if (vType(avc) == VREG) { + avc->m.Mode |= S_IFREG; + } else if (vType(avc) == VDIR) { + avc->m.Mode |= S_IFDIR; + } else if (vType(avc) == VLNK) { + avc->m.Mode |= S_IFLNK; + if ((avc->m.Mode & 0111) == 0) + avc->mvstat = 1; + } +#endif + flags |= VDisconSetMode; + } /* if(astatus.Mask & AFS_SETMODE) */ + + } /* if (!astatus->Mask) */ + + if (attrs->va_size > 0) { + /* XXX: Do I need more checks? */ + /* Truncation operation. */ + flags |= VDisconTrunc; + } + + ObtainWriteLock(&afs_DDirtyVCListLock, 701); + + if (flags) { + /* Add to disconnected dirty list and set dirty flag.*/ + if (!avc->ddirty_flags || + (avc->ddirty_flags == VDisconShadowed)) { + /* Not in dirty list. */ + AFS_DISCON_ADD_DIRTY(avc); + } + + avc->ddirty_flags |= flags; + } + + ReleaseWriteLock(&afs_DDirtyVCListLock); + + /* XXX: How about the rest of the fields? */ + + ConvertWToSLock(&avc->lock); + + return code; +} + +#endif /* * afs_ProcessFS @@ -1918,12 +2015,23 @@ afs_GetVCache(register struct VenusFid *afid, struct vrequest *areq, tvc->parentUnique = OutStatus.ParentUnique; code = 0; } else { - /* If we've got here and we're disconnected, then we can go - * no further - */ + if (AFS_IS_DISCONNECTED) { - code = ENETDOWN; - /*printf("Network is down in afs_GetCache");*/ + if (AFS_IS_DISCON_RW) { + /* Seek the vnode manually. */ + ObtainSharedLock(&afs_xvcache, 738); + avc = afs_FindVCache(afid, NULL, 1); + ReleaseSharedLock(&afs_xvcache); + + if (vType(avc) == VDIR) + OutStatus.FileType = Directory; + + code = tvc?0:ENOENT; + } else { + /* Nothing to do otherwise...*/ + code = ENETDOWN; + printf("Network is down in afs_GetCache"); + } } else code = afs_FetchStatus(tvc, afid, areq, &OutStatus); @@ -2015,7 +2123,7 @@ afs_LookupVCache(struct VenusFid *afid, struct vrequest *areq, origCBs = afs_allCBs; /* if anything changes, we don't have a cb */ if (AFS_IS_DISCONNECTED) { - /*printf("Network is down in afs_LookupVcache\n");*/ + printf("Network is down in afs_LookupVcache\n"); code = ENETDOWN; } else code = @@ -2376,6 +2484,67 @@ afs_GetRootVCache(struct VenusFid *afid, struct vrequest *areq, } +/*! + * Update callback status and (sometimes) attributes of a vnode. + * Called after doing a fetch status RPC. Whilst disconnected, attributes + * shouldn't be written to the vcache here. + * + * \param avc + * \param afid + * \param areq + * \param Outsp Server status after rpc call. + * \param acb Callback for this vnode. + * + * \note The vcache must be write locked. + */ +void +afs_UpdateStatus(struct vcache *avc, + struct VenusFid *afid, + struct vrequest *areq, + struct AFSFetchStatus *Outsp, + struct AFSCallBack *acb, + afs_uint32 start) +{ + struct volume *volp; + + if (!AFS_IN_SYNC) + /* Dont write status in vcache if resyncing after a disconnection. */ + afs_ProcessFS(avc, Outsp, areq); + + volp = afs_GetVolume(afid, areq, READ_LOCK); + ObtainWriteLock(&afs_xcbhash, 469); + avc->states |= CTruth; + if (avc->callback /* check for race */ ) { + if (acb->ExpirationTime != 0) { + avc->cbExpires = acb->ExpirationTime + start; + avc->states |= CStatd; + avc->states &= ~CBulkFetching; + afs_QueueCallback(avc, CBHash(acb->ExpirationTime), volp); + } else if (avc->states & CRO) { + /* ordinary callback on a read-only volume -- AFS 3.2 style */ + avc->cbExpires = 3600 + start; + avc->states |= CStatd; + avc->states &= ~CBulkFetching; + afs_QueueCallback(avc, CBHash(3600), volp); + } else { + afs_DequeueCallback(avc); + avc->callback = NULL; + avc->states &= ~(CStatd | CUnique); + if ((avc->states & CForeign) || (avc->fid.Fid.Vnode & 1)) + osi_dnlc_purgedp(avc); /* if it (could be) a directory */ + } + } else { + afs_DequeueCallback(avc); + avc->callback = NULL; + avc->states &= ~(CStatd | CUnique); + if ((avc->states & CForeign) || (avc->fid.Fid.Vnode & 1)) + osi_dnlc_purgedp(avc); /* if it (could be) a directory */ + } + ReleaseWriteLock(&afs_xcbhash); + if (volp) + afs_PutVolume(volp, READ_LOCK); + +} /* * must be called with avc write-locked @@ -2391,7 +2560,6 @@ afs_FetchStatus(struct vcache * avc, struct VenusFid * afid, register struct conn *tc; struct AFSCallBack CallBack; struct AFSVolSync tsync; - struct volume *volp; XSTATS_DECLS; do { tc = afs_Conn(afid, areq, SHARED_LOCK); @@ -2415,38 +2583,7 @@ afs_FetchStatus(struct vcache * avc, struct VenusFid * afid, SHARED_LOCK, NULL)); if (!code) { - afs_ProcessFS(avc, Outsp, areq); - volp = afs_GetVolume(afid, areq, READ_LOCK); - ObtainWriteLock(&afs_xcbhash, 469); - avc->states |= CTruth; - if (avc->callback /* check for race */ ) { - if (CallBack.ExpirationTime != 0) { - avc->cbExpires = CallBack.ExpirationTime + start; - avc->states |= CStatd; - avc->states &= ~CBulkFetching; - afs_QueueCallback(avc, CBHash(CallBack.ExpirationTime), volp); - } else if (avc->states & CRO) { /* ordinary callback on a read-only volume -- AFS 3.2 style */ - avc->cbExpires = 3600 + start; - avc->states |= CStatd; - avc->states &= ~CBulkFetching; - afs_QueueCallback(avc, CBHash(3600), volp); - } else { - afs_DequeueCallback(avc); - avc->callback = NULL; - avc->states &= ~(CStatd | CUnique); - if ((avc->states & CForeign) || (avc->fid.Fid.Vnode & 1)) - osi_dnlc_purgedp(avc); /* if it (could be) a directory */ - } - } else { - afs_DequeueCallback(avc); - avc->callback = NULL; - avc->states &= ~(CStatd | CUnique); - if ((avc->states & CForeign) || (avc->fid.Fid.Vnode & 1)) - osi_dnlc_purgedp(avc); /* if it (could be) a directory */ - } - ReleaseWriteLock(&afs_xcbhash); - if (volp) - afs_PutVolume(volp, READ_LOCK); + afs_UpdateStatus(avc, afid, areq, Outsp, &CallBack, start); } else { /* used to undo the local callback, but that's too extreme. * There are plenty of good reasons that fetchstatus might return diff --git a/src/afs/discon.h b/src/afs/discon.h index 6107f410c..2ad358ee6 100644 --- a/src/afs/discon.h +++ b/src/afs/discon.h @@ -4,19 +4,77 @@ #ifndef AFS_DISCON_ENV #define AFS_IS_DISCONNECTED 0 #define AFS_IS_LOGGING 0 +#define AFS_IS_DISCON_RW 0 +#define AFS_IN_SYNC 0 #define AFS_DISCON_LOCK() #define AFS_DISCON_UNLOCK() +#define AFS_DISCON_ADD_DIRTY(avc) + #else extern afs_int32 afs_is_disconnected; extern afs_int32 afs_is_logging; +extern afs_int32 afs_is_discon_rw; +extern afs_int32 afs_in_sync; extern afs_rwlock_t afs_discon_lock; +extern struct vcache *afs_DDirtyVCList; +extern struct vcache *afs_DDirtyVCListStart; +extern struct vcache *afs_DDirtyVCListPrev; +extern afs_rwlock_t afs_DDirtyVCListLock; +extern afs_int32 afs_ConflictPolicy; + +extern void afs_RemoveAllConns(); +extern afs_uint32 afs_DisconVnode; /* XXX: not protected. */ + +/* For afs_GenFakeFid. */ +extern struct vcache *afs_FindVCache(struct VenusFid *afid, + afs_int32 *retry, + afs_int32 flag); + +extern int afs_WriteVCacheDiscon(register struct vcache *avc, + register struct AFSStoreStatus *astatus, + struct vattr *attrs); +extern int afs_ResyncDisconFiles(struct vrequest *areq, + struct AFS_UCRED *acred); +extern void afs_RemoveAllConns(); +extern void afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype); +extern void afs_GenShadowFid(struct VenusFid *afid); +extern void afs_GenDisconStatus(struct vcache *adp, + struct vcache *avc, + struct VenusFid *afid, + struct vattr *attrs, + struct vrequest *areq, + int file_type); +extern int afs_HashOutDCache(struct dcache *adc, int zap); +extern int afs_MakeShadowDir(struct vcache *avc); +extern void afs_DeleteShadowDir(struct vcache *avc); +extern struct dcache *afs_FindDCacheByFid(register struct VenusFid *afid); +extern void afs_UpdateStatus(struct vcache *avc, + struct VenusFid *afid, + struct vrequest *areq, + struct AFSFetchStatus *Outsp, + struct AFSCallBack *acb, + afs_uint32 start); +extern void afs_RemoveAllConns(); + #define AFS_IS_DISCONNECTED (afs_is_disconnected) #define AFS_IS_LOGGING (afs_is_logging) +#define AFS_IS_DISCON_RW (afs_is_discon_rw) +#define AFS_IN_SYNC (afs_in_sync) #define AFS_DISCON_LOCK() ObtainReadLock(&afs_discon_lock) #define AFS_DISCON_UNLOCK() ReleaseReadLock(&afs_discon_lock) +#define AFS_DISCON_ADD_DIRTY(avc) \ +do { \ + if (!afs_DDirtyVCListStart) { \ + afs_DDirtyVCListStart = afs_DDirtyVCList = avc; \ + } else { \ + afs_DDirtyVCList->ddirty_next = avc; \ + afs_DDirtyVCList = avc; \ + } \ +} while(0); + #endif /* AFS_DISCON_ENV */ #endif /* _DISCON_H */ diff --git a/src/afs/lock.h b/src/afs/lock.h index 0abe76400..7214aa26b 100644 --- a/src/afs/lock.h +++ b/src/afs/lock.h @@ -31,7 +31,7 @@ /* This is the max lock number in use. Please update it if you add any new * lock numbers. */ -#define MAX_LOCK_NUMBER 700 +#define MAX_LOCK_NUMBER 760 #endif struct afs_bozoLock { diff --git a/src/dir/dir.c b/src/dir/dir.c index ec4ad2632..3a5515a30 100644 --- a/src/dir/dir.c +++ b/src/dir/dir.c @@ -86,6 +86,11 @@ extern void *DNew(); #define LookupOffset afs_dir_LookupOffset #define EnumerateDir afs_dir_EnumerateDir #define IsEmpty afs_dir_IsEmpty + +#if defined(AFS_DISCON_ENV) +#define ChangeFid afs_dir_ChangeFid +#endif + #else /* KERNEL */ # ifdef HAVE_UNISTD_H @@ -390,15 +395,19 @@ LookupOffset(void *dir, char *entry, void *voidfid, long *offsetp) int EnumerateDir(void *dir, int (*hookproc) (), void *hook) { - /* Enumerate the contents of a directory. */ + /* Enumerate the contents of a directory. + * Break when hook function returns non 0. + */ register int i; int num; register struct DirHeader *dhp; register struct DirEntry *ep; + int code = 0; dhp = (struct DirHeader *)DRead(dir, 0); if (!dhp) return EIO; /* first page should be there */ + for (i = 0; i < NHASHENT; i++) { /* For each hash chain, enumerate everyone on the list. */ num = ntohs(dhp->hashTable[i]); @@ -414,10 +423,13 @@ EnumerateDir(void *dir, int (*hookproc) (), void *hook) } break; } + num = ntohs(ep->next); - (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode), + code = (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode), ntohl(ep->fid.vunique)); DRelease(ep, 0); + if (code) + break; } } DRelease(dhp, 0); @@ -531,3 +543,44 @@ FindItem(void *dir, char *ename, unsigned short **previtem) } } } + +#if defined(AFS_DISCON_ENV) +/*! + * Change an entry fid. + * + * \param dir + * \param entry The entry name. + * \param old_fid The old find in MKFid format (host order). + * It can be omitted if you don't need a safety check... + * \param new_fid The new find in MKFid format (host order). + */ +int ChangeFid(void *dir, + char *entry, + afs_uint32 *old_fid, + afs_uint32 *new_fid) +{ + struct DirEntry *firstitem; + unsigned short *previtem; + struct MKFid *fid_old = (struct MKFid *) old_fid; + struct MKFid *fid_new = (struct MKFid *) new_fid; + + /* Find entry. */ + firstitem = FindItem(dir, entry, &previtem); + if (firstitem == 0) { + return ENOENT; + } + DRelease(previtem, 1); + /* Replace fid. */ + if (!old_fid || + ((htonl(fid_old->vnode) == firstitem->fid.vnode) && + (htonl(fid_old->vunique) == firstitem->fid.vunique))) { + + firstitem->fid.vnode = htonl(fid_new->vnode); + firstitem->fid.vunique = htonl(fid_new->vunique); + } + + DRelease(firstitem, 1); + + return 0; +} +#endif diff --git a/src/venus/fs.c b/src/venus/fs.c index 2ab63eba8..5a797dbb4 100644 --- a/src/venus/fs.c +++ b/src/venus/fs.c @@ -3305,11 +3305,19 @@ GetCryptCmd(struct cmd_syndesc *as, void *arock) #ifdef AFS_DISCON_ENV static char *modenames[] = { - "readonly", + "offline", + "online", + "readonly", /* Not currently supported */ "fetchonly", /* Not currently supported */ "partial", /* Not currently supported */ - "nat", - "full", + NULL +}; + +static char *policynames[] = { + "client", + "server", + "closer", /* Not currently supported. */ + "manual", /* Not currently supported. */ NULL }; @@ -3318,13 +3326,16 @@ DisconCmd(struct cmd_syndesc *as, void *arock) { struct cmd_item *ti; char *modename; - int modelen; - afs_int32 mode, code; + char *policyname; + int modelen, policylen; + afs_int32 mode, policy, code; struct ViceIoctl blob; blob.in = NULL; blob.in_size = 0; + space[0] = space[1] = space[2] = 0; + ti = as->parms[0].items; if (ti) { modename = ti->data; @@ -3335,11 +3346,30 @@ DisconCmd(struct cmd_syndesc *as, void *arock) if (modenames[mode] == NULL) printf("Unknown discon mode \"%s\"\n", modename); else { - memcpy(space, &mode, sizeof mode); - blob.in = space; - blob.in_size = sizeof mode; + space[0] = mode + 1; } } + ti = as->parms[1].items; + if (ti) { + policyname = ti->data; + policylen = strlen(policyname); + for (policy = 0; policynames[policy] != NULL; policy++) + if (!strncasecmp(policyname, policynames[policy], policylen)) + break; + if (policynames[policy] == NULL) + printf("Unknown discon mode \"%s\"\n", policyname); + else { + space[1] = policy + 1; + } + } + + if (as->parms[2].items) { + space[2] = 1; + printf("force on\n"); + } + + blob.in = space; + blob.in_size = 3 * sizeof(afs_int32); blob.out_size = sizeof(mode); blob.out = space; @@ -3678,7 +3708,9 @@ defect 3069 #ifdef AFS_DISCON_ENV ts = cmd_CreateSyntax("discon", DisconCmd, NULL, "disconnection mode"); - cmd_AddParm(ts, "-mode", CMD_SINGLE, CMD_OPTIONAL, "readonly | nat | full"); + cmd_AddParm(ts, "-mode", CMD_SINGLE, CMD_REQUIRED, "offline | online"); + cmd_AddParm(ts, "-policy", CMD_SINGLE, CMD_OPTIONAL, "client | server"); + cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "Force reconnection, despite any synchronization issues."); #endif ts = cmd_CreateSyntax("nukenfscreds", NukeNFSCredsCmd, NULL, "nuke credentials for NFS client"); diff --git a/src/vol/vol-salvage.c b/src/vol/vol-salvage.c index 4e823cdc8..a3bc3390e 100644 --- a/src/vol/vol-salvage.c +++ b/src/vol/vol-salvage.c @@ -2403,7 +2403,7 @@ CopyAndSalvage(register struct DirSummary *dir) dir->dirHandle = newdir; } -void +int JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber, Unique unique) { @@ -2421,7 +2421,7 @@ JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber, CopyOnWrite(dir); assert(Delete(&dir->dirHandle, name) == 0); } - return; + return 0; } #ifdef AFS_AIX_ENV #ifndef AFS_NAMEI_ENV @@ -2435,7 +2435,7 @@ JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber, CopyOnWrite(dir); assert(Delete(&dir->dirHandle, name) == 0); } - return; + return 0; } #endif #endif @@ -2453,7 +2453,7 @@ JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber, CopyOnWrite(dir); assert(Delete(&dir->dirHandle, name) == 0); } - return; + return 0; } } @@ -2468,7 +2468,7 @@ JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber, * entry. Otherwise, it will get created in the next * salvage and deleted again here. So Just skip it. */ - return; + return 0; } todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0); @@ -2486,7 +2486,7 @@ JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber, assert(Create(&dir->dirHandle, name, &fid) == 0); } if (todelete) - return; /* no need to continue */ + return 0; /* no need to continue */ } if (strcmp(name, ".") == 0) { @@ -2543,7 +2543,7 @@ JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber, } vnodeEssence->claimed = 0; /* Not claimed: Orphaned */ vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */ - return; + return 0; } else { if (ShowSuid && (vnodeEssence->modeBits & 06000)) Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber); @@ -2560,14 +2560,14 @@ JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber, if (fdP == NULL) { Log("ERROR %s could not open mount point vnode %u\n", dir->vname, vnodeNumber); IH_RELEASE(ihP); - return; + return 0; } size = FDH_SIZE(fdP); if (size < 0) { Log("ERROR %s mount point has invalid size %d, vnode %u\n", dir->vname, size, vnodeNumber); FDH_REALLYCLOSE(fdP); IH_RELEASE(ihP); - return; + return 0; } if (size > 1024) @@ -2634,13 +2634,14 @@ JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber, CopyOnWrite(dir); assert(Delete(&dir->dirHandle, name) == 0); } - return; + return 0; } } /* This directory claims the vnode */ vnodeEssence->claimed = 1; } vnodeEssence->count--; + return 0; } void diff --git a/src/vol/vol-salvage.h b/src/vol/vol-salvage.h index 3eafb9a35..7c3abe09b 100644 --- a/src/vol/vol-salvage.h +++ b/src/vol/vol-salvage.h @@ -238,7 +238,7 @@ extern void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino, Unique * maxu); extern int GetInodeSummary(char *path, VolumeId singleVolumeNumber); extern void GetVolumeSummary(VolumeId singleVolumeNumber); -extern void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber, +extern int JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber, Unique unique); extern void MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe, int check);