return 0;
}
+ AFS_DISCON_LOCK();
+
credp = crref();
vcp = VTOAFS(FILE_INODE(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);
}
/* 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;
}
extern struct vfs *afs_globalVFS;
#endif
+
/* copy out attributes from cache entry */
int
afs_CopyOutAttrs(register struct vcache *avc, register struct vattr *attrs)
}
}
- if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) {
+ if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
code = ENETDOWN;
goto done;
}
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 */
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;
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--;
goto done;
}
- if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) {
+ if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
code = ENETDOWN;
goto done;
}
}
}
- 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) {
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);
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 {
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;
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);
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:
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)
}
}
- 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) {
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 */
}
code = ENOENT;
} else {
- /*printf("Network down in afs_lookup\n");*/
+ printf("Network down in afs_lookup\n");
code = ENETDOWN;
}
}
#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;
}
(tvc, ((tvc->states & CForeign) ? PRSFS_READ : PRSFS_LOOKUP),
&treq, CHECK_MODE_BITS)) {
code = EACCES;
+ printf("afs_Open: no access for dir\n");
goto done;
}
}
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;
}
struct AFSFetchStatus OutDirStatus;
struct AFSVolSync tsync;
XSTATS_DECLS;
-
if (!AFS_IS_DISCONNECTED) {
do {
tc = afs_Conn(&adp->fid, treqp, SHARED_LOCK);
ReleaseSharedLock(&tdc->lock);
afs_PutDCache(tdc);
}
+
if (tvc)
afs_PutVCache(tvc);
}
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) {
#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);
}
}
/* 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
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);
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);
}
}
+#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);
}
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);
}
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;
goto done;
}
- if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) {
+ if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
code = ENETDOWN;
goto done;
}
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. */
* 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 */
}
/* 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) {
ReleaseWriteLock(&aodp->lock);
if (!oneDir)
ReleaseWriteLock(&andp->lock);
-
if (returnCode) {
code = returnCode;
goto done;
* 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;
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...
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;
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();
/* 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
afs_PutFakeStat(&fakestat);
return code;
}
+ AFS_DISCON_LOCK();
#ifdef AFS_SUN5_ENV
if (avc->flockCount) {
HandleFlock(avc, LOCK_UN, &treq, 0, 1 /*onlymine */ );
#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 .. */
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 */
}
#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);
#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;
}
afs_remunlink(avc, 1); /* ignore any return code */
}
#endif
+ AFS_DISCON_UNLOCK();
afs_PutFakeStat(&fakestat);
code = afs_CheckCode(code, &treq, 5);
return code;
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)
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) {
code = ENOENT;
}
#endif
-
+ AFS_DISCON_UNLOCK();
code = afs_CheckCode(code, &treq, 33);
ReleaseSharedLock(&avc->lock);
return code;
#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
#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;
afs_int32 markeddown;
- if (AFS_IS_DISCONNECTED) {
+
+ if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) {
+ /* On reconnection, act as connected. XXX: for now.... */
/* SXW - This may get very tired after a while. We should try and
* intercept all RPCs before they get here ... */
/*printf("afs_Analyze: disconnected\n");*/
{ "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;
return NULL;
}
- if (AFS_IS_DISCONNECTED) {
+ if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) {
afs_warnuser("afs_ConnBySA: disconnected\n");
ReleaseSharedLock(&afs_xconn);
return NULL;
AFS_STATCNT(afs_ConnByHost);
- if (AFS_IS_DISCONNECTED) {
+ if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) {
afs_warnuser("afs_ConnByHost: disconnected\n");
return NULL;
}
static void afs_DCSizeInit(void);
static afs_int32 afs_DCWhichBucket(afs_int32, afs_int32);
-
/*
* --------------------- Exported definitions ---------------------
*/
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();
}
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)
* 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 */
}
}
- /* 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
*
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
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) {
} /* 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
*
* 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
}
}
- 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
}
/* Flush the Dcache */
afs_PutDCache(tdc);
-
+
return NULL;
}
UpgradeSToWLock(&tdc->lock, 609);
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
* License. For details, see the LICENSE file in the top-level source
* directory or online at http://www.openafs.org/dl/license10.html
*/
-
+
#include <afsconfig.h>
#include "afs/param.h"
#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
#endif
#if defined(AFS_DISCON_ENV)
afs_rwlock_t afs_discon_lock;
+extern afs_rwlock_t afs_DDirtyVCListLock;
#endif
/*
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();
#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, \
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:
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,
#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");*/
* afs_FlushActiveVcaches
* afs_VerifyVCache2
* afs_WriteVCache
+ * afs_WriteVCacheDiscon
* afs_SimpleVStat
* afs_ProcessFS
* TellALittleWhiteLie
* afs_GetVCache
* afs_LookupVCache
* afs_GetRootVCache
+ * afs_UpdateStatus
* afs_FetchStatus
* afs_StuffVcache
* afs_PutVCache
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);
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) {
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
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);
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 =
}
+/*!
+ * 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
register struct conn *tc;
struct AFSCallBack CallBack;
struct AFSVolSync tsync;
- struct volume *volp;
XSTATS_DECLS;
do {
tc = afs_Conn(afid, areq, SHARED_LOCK);
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
#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 */
/* 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 {
#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
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]);
}
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);
}
}
}
+
+#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
#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
};
{
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;
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;
#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");
dir->dirHandle = newdir;
}
-void
+int
JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
Unique unique)
{
CopyOnWrite(dir);
assert(Delete(&dir->dirHandle, name) == 0);
}
- return;
+ return 0;
}
#ifdef AFS_AIX_ENV
#ifndef AFS_NAMEI_ENV
CopyOnWrite(dir);
assert(Delete(&dir->dirHandle, name) == 0);
}
- return;
+ return 0;
}
#endif
#endif
CopyOnWrite(dir);
assert(Delete(&dir->dirHandle, name) == 0);
}
- return;
+ return 0;
}
}
* 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);
assert(Create(&dir->dirHandle, name, &fid) == 0);
}
if (todelete)
- return; /* no need to continue */
+ return 0; /* no need to continue */
}
if (strcmp(name, ".") == 0) {
}
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);
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)
CopyOnWrite(dir);
assert(Delete(&dir->dirHandle, name) == 0);
}
- return;
+ return 0;
}
}
/* This directory claims the vnode */
vnodeEssence->claimed = 1;
}
vnodeEssence->count--;
+ return 0;
}
void
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);