disconnected-rw-20080922
authorDragos Tatulea <dragos.tatulea@gmail.com>
Mon, 22 Sep 2008 14:36:16 +0000 (14:36 +0000)
committerDerrick Brashear <shadow@dementia.org>
Mon, 22 Sep 2008 14:36:16 +0000 (14:36 +0000)
LICENSE IPL10
FIXES 114605 114606 114607

add read-write disconnected support

28 files changed:
src/afs/LINUX/osi_vnodeops.c
src/afs/VNOPS/afs_vnop_access.c
src/afs/VNOPS/afs_vnop_attrs.c
src/afs/VNOPS/afs_vnop_create.c
src/afs/VNOPS/afs_vnop_dirops.c
src/afs/VNOPS/afs_vnop_lookup.c
src/afs/VNOPS/afs_vnop_open.c
src/afs/VNOPS/afs_vnop_read.c
src/afs/VNOPS/afs_vnop_remove.c
src/afs/VNOPS/afs_vnop_rename.c
src/afs/VNOPS/afs_vnop_write.c
src/afs/afs.h
src/afs/afs_analyze.c
src/afs/afs_callback.c
src/afs/afs_conn.c
src/afs/afs_dcache.c
src/afs/afs_disconnected.c
src/afs/afs_init.c
src/afs/afs_pioctl.c
src/afs/afs_prototypes.h
src/afs/afs_segments.c
src/afs/afs_vcache.c
src/afs/discon.h
src/afs/lock.h
src/dir/dir.c
src/venus/fs.c
src/vol/vol-salvage.c
src/vol/vol-salvage.h

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