discon remove dirty vcaches from queue when discarding
[openafs.git] / src / afs / afs_disconnected.c
index 4d5f192..2097761 100644 (file)
@@ -7,7 +7,6 @@
 #include <afsconfig.h>
 #include "afs/param.h"
  
-RCSID("$Header$");
  
 #include "afs/sysincludes.h"
 #include "afsincludes.h"
@@ -15,22 +14,19 @@ RCSID("$Header$");
 #include "afs/lock.h"
 #include "afs/afs_cbqueue.h"
 
-#ifdef AFS_DISCON_ENV
-
 #define dv_match(vc, fstat)                             \
-       ((vc->m.DataVersion.low == fstat.DataVersion) && \
-       (vc->m.DataVersion.high == fstat.dataVersionHigh))
+       ((vc->f.m.DataVersion.low == fstat.DataVersion) && \
+       (vc->f.m.DataVersion.high == fstat.dataVersionHigh))
+
+/*! Circular queue of dirty vcaches */
+struct afs_q afs_disconDirty;
 
-/*! 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;
+/*! Circular queue of vcaches with shadow directories */
+struct afs_q afs_disconShadow;
 
-/*! Locks list of dirty vcaches. */
-afs_rwlock_t afs_DDirtyVCListLock;
+/*! Locks both of these lists. Must be write locked for anything other than
+ *  list traversal */
+afs_rwlock_t afs_disconDirtyLock;
 
 extern afs_int32 *afs_dvhashTbl;       /*Data cache hash table */
 extern afs_int32 *afs_dchashTbl;       /*Data cache hash table */
@@ -38,8 +34,7 @@ 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.
+/*! Vnode number. On file creation, use the current value and increment it.
  */
 afs_uint32 afs_DisconVnode = 2;
 
@@ -53,6 +48,10 @@ enum {
 
 afs_int32 afs_ConflictPolicy = SERVER_WINS;
 
+static void afs_DisconDiscardAllShadows(int, afs_ucred_t *);
+void afs_DbgListDirEntries(struct VenusFid *afid);
+
+
 /*!
  * Find the first dcache of a file that has the specified fid.
  * Similar to afs_FindDCache, only that it takes a fid instead
@@ -62,10 +61,11 @@ afs_int32 afs_ConflictPolicy = SERVER_WINS;
  *
  * \return The found dcache or NULL.
  */
-struct dcache *afs_FindDCacheByFid(register struct VenusFid *afid)
+struct dcache *
+afs_FindDCacheByFid(struct VenusFid *afid)
 {
-    register afs_int32 i, index;
-    register struct dcache *tdc = NULL;
+    afs_int32 i, index;
+    struct dcache *tdc = NULL;
 
     i = DVHash(afid);
     ObtainWriteLock(&afs_xdcache, 758);
@@ -98,23 +98,24 @@ struct dcache *afs_FindDCacheByFid(register struct VenusFid *afid)
  *
  * \return Mask of operations.
  */
-int afs_GenStoreStatus(struct vcache *avc, struct AFSStoreStatus *astat)
+int
+afs_GenStoreStatus(struct vcache *avc, struct AFSStoreStatus *astat)
 {
-    if (!avc || !astat || !avc->ddirty_flags)
+    if (!avc || !astat || !avc->f.ddirty_flags)
        return 0;
 
     /* Clean up store stat. */
     memset(astat, 0, sizeof(struct AFSStoreStatus));
 
-    if (avc->ddirty_flags & VDisconSetTime) {
+    if (avc->f.ddirty_flags & VDisconSetTime) {
        /* Update timestamp. */
-       astat->ClientModTime = avc->m.Date;
+       astat->ClientModTime = avc->f.m.Date;
        astat->Mask |= AFS_SETMODTIME;
     }
 
-    if (avc->ddirty_flags & VDisconSetMode) {
+    if (avc->f.ddirty_flags & VDisconSetMode) {
        /* Copy the mode bits. */
-       astat->UnixModeBits = avc->m.Mode;
+       astat->UnixModeBits = avc->f.m.Mode;
        astat->Mask |= AFS_SETMODE;
    }
 
@@ -128,10 +129,9 @@ int afs_GenStoreStatus(struct vcache *avc, struct AFSStoreStatus *astat)
  *
  * \param hdata The fid to be filled.
  */
-int get_parent_dir_fid_hook(void *hdata,
-                               char *aname,
-                               afs_int32 vnode,
-                               afs_int32 unique)
+static int
+get_parent_dir_fid_hook(void *hdata, char *aname, afs_int32 vnode,
+                       afs_int32 unique)
 {
     struct VenusFid *tfid = (struct VenusFid *) hdata;
 
@@ -150,27 +150,42 @@ int get_parent_dir_fid_hook(void *hdata,
  *
  * \param avc The file's vhash entry.
  * \param afid Put the fid here.
+ *
+ * \return 0 on success, -1 on failure
  */
-int afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid)
+int
+afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid)
 {
     struct dcache *tdc;
 
-    afid->Cell = avc->fid.Cell;
-    afid->Fid.Volume = avc->fid.Fid.Volume;
+    afid->Cell = avc->f.fid.Cell;
+    afid->Fid.Volume = avc->f.fid.Fid.Volume;
 
-    if (vType(avc) == VREG) {
+    switch (vType(avc)) {
+    case VREG:
+    case VLNK:
        /* 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) {
+       afid->Fid.Vnode = avc->f.parent.vnode;
+       afid->Fid.Unique = avc->f.parent.unique;
+       break;
+    case VDIR:
        /* If dir or parent dir created locally*/
-       tdc = afs_FindDCacheByFid(&avc->fid);
+       tdc = afs_FindDCacheByFid(&avc->f.fid);
        if (tdc) {
+           afid->Fid.Unique = 0;
            /* Lookup each entry for the fid. It should be the first. */
            afs_dir_EnumerateDir(tdc, &get_parent_dir_fid_hook, afid);
            afs_PutDCache(tdc);
+           if (afid->Fid.Unique == 0) {
+               return -1;
+           }
+       } else {
+           return -1;
        }
+       break;
+    default:
+       return -1;
+       break;
     }
 
     return 0;
@@ -188,10 +203,9 @@ struct NameAndFid {
  * \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)
+static int
+get_vnode_name_hook(void *hdata, char *aname, afs_int32 vnode,
+                   afs_int32 unique)
 {
     struct NameAndFid *nf = (struct NameAndFid *) hdata;
 
@@ -217,10 +231,9 @@ int get_vnode_name_hook(void *hdata,
  * \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
+afs_GetVnodeName(struct vcache *avc, struct VenusFid *afid, char *aname,
+                int deleted)
 {
     int code = 0;
     struct dcache *tdc;
@@ -234,12 +247,12 @@ int afs_GetVnodeName(struct vcache *avc,
        /* 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) {
+       parent_fid.Cell = avc->f.fid.Cell;
+       parent_fid.Fid.Volume = avc->f.fid.Fid.Volume;
+       if (avc->f.ddirty_flags & VDisconRename) {
            /* For renames the old dir fid is needed. */
-           parent_fid.Fid.Vnode = avc->oldVnode;
-           parent_fid.Fid.Unique = avc->oldUnique;
+           parent_fid.Fid.Vnode = avc->f.oldParent.vnode;
+           parent_fid.Fid.Unique = avc->f.oldParent.unique;
        } else {
            parent_fid.Fid.Vnode = afid->Fid.Vnode;
            parent_fid.Fid.Unique = afid->Fid.Unique;
@@ -253,10 +266,10 @@ int afs_GetVnodeName(struct vcache *avc,
            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;
+       shadow_fid.Cell = parent_vc->f.fid.Cell;
+       shadow_fid.Fid.Volume = parent_vc->f.fid.Fid.Volume;
+       shadow_fid.Fid.Vnode = parent_vc->f.shadow.vnode;
+       shadow_fid.Fid.Unique = parent_vc->f.shadow.unique;
 
        afs_PutVCache(parent_vc);
 
@@ -270,12 +283,15 @@ int afs_GetVnodeName(struct vcache *avc,
     }                  /* if (deleted) */
 
     if (tdc) {
-       tnf.fid = &avc->fid;
-       tnf.name_len = 0;
+       tnf.fid = &avc->f.fid;
+       tnf.name_len = -1;
        tnf.name = aname;
        afs_dir_EnumerateDir(tdc, &get_vnode_name_hook, &tnf);
        afs_PutDCache(tdc);
+       if (tnf.name_len == -1)
+           code = ENOENT;
     } else {
+       /* printf("Directory dcache not found!\n"); */
         code = ENOENT;
     }
 
@@ -290,10 +306,9 @@ struct DirtyChildrenCount {
 /*!
  * Lookup dirty deleted vnodes in this dir.
  */
-int chk_del_children_hook(void *hdata,
-                               char *aname,
-                               afs_int32 vnode,
-                               afs_int32 unique)
+static int
+chk_del_children_hook(void *hdata, char *aname, afs_int32 vnode,
+                     afs_int32 unique)
 {
     struct VenusFid tfid;
     struct DirtyChildrenCount *v = (struct DirtyChildrenCount *) hdata;
@@ -310,8 +325,8 @@ int chk_del_children_hook(void *hdata,
        return 0;
 
     /* Get this file's vcache. */
-    tfid.Cell = v->vc->fid.Cell;
-    tfid.Fid.Volume = v->vc->fid.Fid.Volume;
+    tfid.Cell = v->vc->f.fid.Cell;
+    tfid.Fid.Volume = v->vc->f.fid.Fid.Volume;
     tfid.Fid.Vnode = vnode;
     tfid.Fid.Unique = unique;
 
@@ -319,11 +334,13 @@ int chk_del_children_hook(void *hdata,
     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++;
+    /* Count unfinished dirty children. */
+    if (tvc) {
+       ObtainReadLock(&tvc->lock);
+       if (tvc->f.ddirty_flags)
+           v->count++;
+       ReleaseReadLock(&tvc->lock);
+
        afs_PutVCache(tvc);
     }
 
@@ -338,20 +355,21 @@ int chk_del_children_hook(void *hdata,
  *
  * \note afs_DDirtyVCListLock must be write locked.
  */
-int afs_CheckDeletedChildren(struct vcache *avc)
+int
+afs_CheckDeletedChildren(struct vcache *avc)
 {
     struct dcache *tdc;
     struct DirtyChildrenCount dcc;
     struct VenusFid shadow_fid;
 
-    if (!(avc->ddirty_flags & VDisconShadowed))
+    if (!avc->f.shadow.vnode)
        /* 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;
+    shadow_fid.Cell = avc->f.fid.Cell;
+    shadow_fid.Fid.Volume = avc->f.fid.Fid.Volume;
+    shadow_fid.Fid.Vnode = avc->f.shadow.vnode;
+    shadow_fid.Fid.Unique = avc->f.shadow.unique;
 
     dcc.count = 0;
 
@@ -369,10 +387,9 @@ int afs_CheckDeletedChildren(struct vcache *avc)
 /*!
  * Changes a file's parent fid references.
  */
-int fix_children_fids_hook(void *hdata,
-                               char *aname,
-                               afs_int32 vnode,
-                               afs_int32 unique)
+static int
+fix_children_fids_hook(void *hdata, char *aname, afs_int32 vnode,
+                      afs_int32 unique)
 {
     struct VenusFid tfid;
     struct VenusFid *afid = (struct VenusFid *) hdata;
@@ -400,8 +417,8 @@ int fix_children_fids_hook(void *hdata,
 
        /* Change the fields. */
        if (tvc) {
-           tvc->parentVnode = afid->Fid.Vnode;
-           tvc->parentUnique = afid->Fid.Unique;
+           tvc->f.parent.vnode = afid->Fid.Vnode;
+           tvc->f.parent.unique = afid->Fid.Unique;
 
            afs_PutVCache(tvc);
        }
@@ -432,7 +449,8 @@ int fix_children_fids_hook(void *hdata,
  * \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)
+void
+afs_FixChildrenFids(struct VenusFid *old_fid, struct VenusFid *new_fid)
 {
     struct dcache *tdc;
 
@@ -445,13 +463,15 @@ void afs_FixChildrenFids(struct VenusFid *old_fid, struct VenusFid *new_fid)
     }
 }
 
-int list_dir_hook(void *hdata, char *aname, afs_int32 vnode, afs_int32 unique)
+static 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);
+    /* printf("list_dir_hook: %s v:%u u:%u\n", aname, vnode, unique); */
     return 0;
 }
 
-void afs_DbgListDirEntries(struct VenusFid *afid)
+void
+afs_DbgListDirEntries(struct VenusFid *afid)
 {
     struct dcache *tdc;
 
@@ -464,70 +484,123 @@ void afs_DbgListDirEntries(struct VenusFid *afid)
 }
 
 /*!
+ * Find the parent vcache for a given child
+ *
+ * \param avc  The vcache whose parent is required
+ * \param afid  Fid structure in which parent's fid should be stored
+ * \param aname An AFSNAMEMAX sized buffer to hold the parents name
+ * \param adp  A pointer to a struct vcache* which will be set to the
+ *             parent vcache
+ *
+ * \return An error code. 0 indicates success, EAGAIN that the vnode should
+ *        be deferred to later in the resync process
+ */
+
+int
+afs_GetParentVCache(struct vcache *avc, int deleted, struct VenusFid *afid, 
+                   char *aname, struct vcache **adp)
+{
+    int code;
+
+    *adp = NULL;
+
+    if (afs_GetParentDirFid(avc, afid)) {
+       /* printf("afs_GetParentVCache: Couldn't find parent dir's FID.\n"); */
+       return ENOENT;
+    }
+
+    code = afs_GetVnodeName(avc, afid, aname, deleted);
+    if (code) {
+       /* printf("afs_GetParentVCache: Couldn't find file name\n"); */
+       goto end;
+    }
+
+    ObtainSharedLock(&afs_xvcache, 766);
+    *adp = afs_FindVCache(afid, 0, 1);
+    ReleaseSharedLock(&afs_xvcache);
+    if (!*adp) {
+       /* printf("afs_GetParentVCache: Couldn't find parent dir's vcache\n"); */
+       code = ENOENT;
+       goto end;
+    }
+
+    if ((*adp)->f.ddirty_flags & VDisconCreate) {
+       /* printf("afs_GetParentVCache: deferring until parent exists\n"); */
+       code = EAGAIN;
+       goto end;
+    }
+
+end:
+    if (code && *adp) {
+       afs_PutVCache(*adp);
+       *adp = NULL;
+    }
+    return code;
+}
+
+
+/*!
  * 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)
+int
+afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq)
 {
     struct VenusFid old_pdir_fid, new_pdir_fid;
-    char *old_name, *new_name;
+    char *old_name = NULL, *new_name = NULL;
     struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
     struct AFSVolSync tsync;
-    struct conn *tc;
+    struct afs_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;
+    old_pdir_fid.Cell = avc->f.fid.Cell;
+    old_pdir_fid.Fid.Volume = avc->f.fid.Fid.Volume;
+    old_pdir_fid.Fid.Vnode = avc->f.oldParent.vnode;
+    old_pdir_fid.Fid.Unique = avc->f.oldParent.unique;
 
     /* Get old name. */
     old_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
     if (!old_name) {
-       printf("afs_ProcessOpRename: Couldn't alloc space for old name.\n");
+       /* 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;
+       /* printf("afs_ProcessOpRename: Couldn't find old name.\n"); */
+       goto done;
     }
 
     /* Alloc data first. */
     new_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
     if (!new_name) {
-       printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n");
+       /* printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n"); */
        code = ENOMEM;
-       goto end2;
+       goto done;
     }
 
-    if (avc->ddirty_flags & VDisconRenameSameDir) {
+    if (avc->f.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");
+       if (afs_GetParentDirFid(avc, &new_pdir_fid)) {
+           /* printf("afs_ProcessOpRename: Couldn't find new parent dir FID.\n"); */
            code = ENOENT;
-           goto end1;
+           goto done;
         }
     }
 
     /* 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;
+       /* printf("afs_ProcessOpRename: Couldn't find new name.\n"); */
+       goto done;
     }
 
     /* Send to data to server. */
@@ -557,12 +630,12 @@ int afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq)
                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);
+    /* if (code) printf("afs_ProcessOpRename: server code=%u\n", code); */
+done:
+    if (new_name)
+       afs_osi_Free(new_name, AFSNAMEMAX);
+    if (old_name)
+       afs_osi_Free(old_name, AFSNAMEMAX);
     return code;
 }
 
@@ -573,159 +646,147 @@ end2:
  * - 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)
+int
+afs_ProcessOpCreate(struct vcache *avc, struct vrequest *areq,
+                   afs_ucred_t *acred)
 {
-    char *tname = NULL;
+    char *tname = NULL, *ttargetName = 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;
+    struct afs_conn *tc;
+    afs_int32 hash, new_hash, index;
+    afs_size_t tlen;
+    int code, op = 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");
+    if (!tname)
        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;
+    code = afs_GetParentVCache(avc, 0, &pdir_fid, tname, &tdp);
+    if (code) 
        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;
-    }
+    /* This data may also be in linkData, but then we have to deal with
+     * the joy of terminating NULLs and . and file modes. So just get
+     * it from the dcache where it won't have been fiddled with.
+     */
+    if (vType(avc) == VLNK) {
+       afs_size_t offset;
+       struct dcache *tdc;
+       struct osi_file *tfile;
 
-    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;
-    }
+       tdc = afs_GetDCache(avc, 0, areq, &offset, &tlen, 0);
+       if (!tdc) {
+           code = ENOENT;
+           goto end;
+       }
 
+       if (tlen > 1024) {
+           afs_PutDCache(tdc);
+           code = EFAULT;
+           goto end;
+       }
+
+       tlen++; /* space for NULL */
+       ttargetName = afs_osi_Alloc(tlen);
+       if (!ttargetName) {
+           afs_PutDCache(tdc);
+           return ENOMEM;
+       }
+       ObtainReadLock(&tdc->lock);
+       tfile = afs_CFileOpen(&tdc->f.inode);
+       code = afs_CFileRead(tfile, 0, ttargetName, tlen);
+       ttargetName[tlen-1] = '\0';
+       afs_CFileClose(tfile);
+       ReleaseReadLock(&tdc->lock);
+       afs_PutDCache(tdc);
+    }
+       
     /* Set status. */
     InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
-    InStatus.ClientModTime = avc->m.Date;
-    InStatus.Owner = avc->m.Owner;
-    InStatus.Group = (afs_int32) acred->cr_gid;
+    InStatus.ClientModTime = avc->f.m.Date;
+    InStatus.Owner = avc->f.m.Owner;
+    InStatus.Group = (afs_int32) afs_cr_gid(acred);
     /* Only care about protection bits. */
-    InStatus.UnixModeBits = avc->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);
+    InStatus.UnixModeBits = avc->f.m.Mode & 0xffff;
+
+    do {
+       tc = afs_Conn(&tdp->f.fid, areq, SHARED_LOCK);
+       if (tc) {
+           switch (vType(avc)) {
+           case VREG:
+                /* Make file on server. */
+               op = AFS_STATS_FS_RPCIDX_CREATEFILE;
+               XSTATS_START_TIME(op);
                 RX_AFS_GUNLOCK();
                 code = RXAFS_CreateFile(tc->id,
-                                (struct AFSFid *)&tdp->fid.Fid,
-                                tname,
-                                &InStatus,
-                                (struct AFSFid *) &newFid.Fid,
-                                &OutFidStatus,
-                                &OutDirStatus,
-                                &CallBack,
-                                &tsync);
+                                       (struct AFSFid *)&tdp->f.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();
+               break;
+           case VDIR:
+               /* Make dir on server. */
+               op = AFS_STATS_FS_RPCIDX_MAKEDIR;
+               XSTATS_START_TIME(op);
                 RX_AFS_GUNLOCK();
-                code = RXAFS_MakeDir(tc->id,
-                                (struct AFSFid *) &tdp->fid.Fid,
-                                tname,
-                                &InStatus,
-                                (struct AFSFid *) &newFid.Fid,
-                                &OutFidStatus,
-                                &OutDirStatus,
-                                &CallBack,
-                                &tsync);
+               code = RXAFS_MakeDir(tc->id, (struct AFSFid *) &tdp->f.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. */
+               break;
+           case VLNK:
+               /* Make symlink on server. */
+               op = AFS_STATS_FS_RPCIDX_SYMLINK;
+               XSTATS_START_TIME(op);
+               RX_AFS_GUNLOCK();
+               code = RXAFS_Symlink(tc->id,
+                               (struct AFSFid *) &tdp->f.fid.Fid,
+                               tname, ttargetName, &InStatus,
+                               (struct AFSFid *) &newFid.Fid,
+                               &OutFidStatus, &OutDirStatus, &tsync);
+               RX_AFS_GLOCK();
+               XSTATS_END_TIME;
+               break;
+           default:
+               op = AFS_STATS_FS_RPCIDX_CREATEFILE;
+               code = 1;
+               break;
+           }
+        } else
+           code = -1;
+    } while (afs_Analyze(tc, code, &tdp->f.fid, areq, op, SHARED_LOCK, NULL));
 
     /* TODO: Handle errors. */
     if (code) {
-       printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code);
-       code = EIO;
+       /* printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); */
        goto end;
     }
 
     /* The rpc doesn't set the cell number. */
-    newFid.Cell = avc->fid.Cell;
+    newFid.Cell = avc->f.fid.Cell;
 
     /*
      * Change the fid in the dir entry.
      */
 
     /* Seek the dir's dcache. */
-    tdc = afs_FindDCacheByFid(&tdp->fid);
+    tdc = afs_FindDCacheByFid(&tdp->f.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_dir_ChangeFid(tdc, tname, &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode);
        afs_PutDCache(tdc);
     }
 
@@ -733,15 +794,16 @@ int afs_ProcessOpCreate(struct vcache *avc,
        /* 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);
+       tdc = afs_FindDCacheByFid(&avc->f.fid);
        if (tdc) {
-           afs_dir_ChangeFid(tdc, ".", &avc->fid.Fid.Vnode, &newFid.Fid.Vnode);
+           afs_dir_ChangeFid(tdc, ".", &avc->f.fid.Fid.Vnode, 
+                             &newFid.Fid.Vnode);
 
-           if (avc->m.LinkCount >= 2)
-               /* For non empty dirs, fix children's parentVnode and parentUnique
-                * reference.
+           if (avc->f.m.LinkCount >= 2)
+               /* For non empty dirs, fix children's parentVnode and 
+                * parentUnique reference.
                 */
-               afs_FixChildrenFids(&avc->fid, &newFid);
+               afs_FixChildrenFids(&avc->f.fid, &newFid);
 
            afs_PutDCache(tdc);
        }
@@ -755,7 +817,7 @@ int afs_ProcessOpCreate(struct vcache *avc,
     ObtainWriteLock(&afs_xvcache, 735);
 
     /* Old fid hash. */
-    hash = VCHash(&avc->fid);
+    hash = VCHash(&avc->f.fid);
     /* New fid hash. */
     new_hash = VCHash(&newFid);
 
@@ -768,7 +830,6 @@ int afs_ProcessOpCreate(struct vcache *avc,
        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;
@@ -776,24 +837,24 @@ int afs_ProcessOpCreate(struct vcache *avc,
            }
         }
     }                           /* if (!afs_vhashT[i]->hnext) */
-    QRemove(&afs_vhashTV[hash]);
+    QRemove(&avc->vhashq);
 
     /* Insert hash in new position. */
     avc->hnext = afs_vhashT[new_hash];
     afs_vhashT[new_hash] = avc;
-    QAdd(&afs_vhashTV[new_hash], &avc->vhashq);
+    QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq);
 
     ReleaseWriteLock(&afs_xvcache);
 
     /* Do the same thing for all dcaches. */
-    hash = DVHash(&avc->fid);
+    hash = DVHash(&avc->f.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)) {
+       if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
+            if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
 
                /* Safer but slower. */
                afs_HashOutDCache(tdc, 0);
@@ -809,7 +870,6 @@ int afs_ProcessOpCreate(struct vcache *avc,
 
                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)
@@ -818,27 +878,21 @@ int afs_ProcessOpCreate(struct vcache *avc,
     ReleaseWriteLock(&afs_xdcache);
 
     /* Now we can set the new fid. */
-    memcpy(&avc->fid, &newFid, sizeof(struct VenusFid));
+    memcpy(&avc->f.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);
+    if (ttargetName) 
+       afs_osi_Free(ttargetName, tlen);
     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.
+ * removal rpc.
  *
  * \param avc The deleted vcache
  * \param areq
@@ -849,50 +903,37 @@ end:
  *
  * \note avc must be write locked.
  */
-int afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq)
+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 afs_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");
+       /* printf("afs_ProcessOpRemove: Couldn't alloc space for 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;
+    code = afs_GetParentVCache(avc, 1, &pdir_fid, tname, &tdp);
+    if (code)
        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;
+       code = EAGAIN;
        goto end;
     }
 
-    if (vType(avc) == VREG) {
+    if (vType(avc) == VREG || vType(avc) == VLNK) {
        /* Remove file on server. */
        do {
            tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK);
@@ -943,19 +984,8 @@ int afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq)
 
     }                          /* if (vType(avc) == VREG) */
 
-    if (code)
-       printf("afs_ProcessOpRemove: server returned code=%u\n", code);
+    /* 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;
@@ -971,9 +1001,10 @@ end:
  *
  * \return 0 for success. On failure, other error codes.
  */
-int afs_SendChanges(struct vcache *avc, struct vrequest *areq)
+int
+afs_SendChanges(struct vcache *avc, struct vrequest *areq)
 {
-    struct conn *tc;
+    struct afs_conn *tc;
     struct AFSStoreStatus sstat;
     struct AFSFetchStatus fstat;
     struct AFSVolSync tsync;
@@ -982,18 +1013,18 @@ int afs_SendChanges(struct vcache *avc, struct vrequest *areq)
     XSTATS_DECLS;
 
     /* Start multiplexing dirty operations from ddirty_flags field: */
-    if (avc->ddirty_flags & VDisconSetAttrMask) {
+    if (avc->f.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);
+               tc = afs_Conn(&avc->f.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,
+                               (struct AFSFid *) &avc->f.fid.Fid,
                                &sstat,
                                &fstat,
                                &tsync);
@@ -1005,7 +1036,7 @@ int afs_SendChanges(struct vcache *avc, struct vrequest *areq)
 
        } while (afs_Analyze(tc,
                        code,
-                       &avc->fid,
+                       &avc->f.fid,
                        areq,
                        AFS_STATS_FS_RPCIDX_STORESTATUS,
                        SHARED_LOCK,
@@ -1017,7 +1048,7 @@ int afs_SendChanges(struct vcache *avc, struct vrequest *areq)
     if (code)
        return code;
 
-    if (avc->ddirty_flags &
+    if (avc->f.ddirty_flags &
        (VDisconTrunc
        | VDisconWriteClose
        | VDisconWriteFlush
@@ -1025,14 +1056,14 @@ int afs_SendChanges(struct vcache *avc, struct vrequest *areq)
 
        /* Truncate OP: */
        do {
-           tc = afs_Conn(&avc->fid, areq, SHARED_LOCK);
+           tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
            if (tc) {
                /* Set storing flags. XXX: A tad inefficient ... */
-               if (avc->ddirty_flags & VDisconWriteClose)
+               if (avc->f.ddirty_flags & VDisconWriteClose)
                    flags |= AFS_LASTSTORE;
-               if (avc->ddirty_flags & VDisconWriteOsiFlush)
+               if (avc->f.ddirty_flags & VDisconWriteOsiFlush)
                    flags |= (AFS_SYNC | AFS_LASTSTORE);
-               if (avc->ddirty_flags & VDisconWriteFlush)
+               if (avc->f.ddirty_flags & VDisconWriteFlush)
                    flags |= AFS_SYNC;
 
                /* Try to send store to server. */
@@ -1043,7 +1074,7 @@ int afs_SendChanges(struct vcache *avc, struct vrequest *areq)
 
        } while (afs_Analyze(tc,
                        code,
-                       &avc->fid,
+                       &avc->f.fid,
                        areq,
                        AFS_STATS_FS_RPCIDX_STOREDATA,
                        SHARED_LOCK,
@@ -1062,73 +1093,61 @@ int afs_SendChanges(struct vcache *avc, struct vrequest *areq)
  * \param acred User credentials.
  *
  * \return If all files synchronized succesfully, return 0, otherwise
- * return 1.
+ * return error code
  *
  * \note For now, it's the request from the PDiscon pioctl.
  *
  */
-int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred)
+int
+afs_ResyncDisconFiles(struct vrequest *areq, afs_ucred_t *acred)
 {
-    struct conn *tc;
-    struct vcache *tvc, *tmp;
+    struct afs_conn *tc;
+    struct vcache *tvc;
     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;
+    int code = 0;
     afs_int32 start = 0;
     XSTATS_DECLS;
-    //AFS_STATCNT(afs_ResyncDisconFiles);
+    /*AFS_STATCNT(afs_ResyncDisconFiles);*/
 
-    shList = shListStart = NULL;
+    ObtainWriteLock(&afs_disconDirtyLock, 707);
 
-    ObtainReadLock(&afs_DDirtyVCListLock);
+    while (!QEmpty(&afs_disconDirty)) {
+       tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
 
-    tvc = afs_DDirtyVCListStart;
-    while (tvc) {
+       /* Can't lock tvc whilst holding the discon dirty lock */
+       ReleaseWriteLock(&afs_disconDirtyLock);
 
        /* 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;
+       ObtainWriteLock(&tvc->lock, 705);
 
-       } else if (tvc->ddirty_flags & VDisconRemove) {
+       if (tvc->f.ddirty_flags & VDisconRemove) {
            /* Delete the file on the server and just move on
             * to the next file. After all, it has been deleted
             * we can't replay any other operation it.
             */
            code = afs_ProcessOpRemove(tvc, areq);
-           if (code == ENOTEMPTY)
-               defered = 1;
-           goto skip_file;
+           goto next_file;
 
-       } else if (tvc->ddirty_flags & VDisconCreate) {
+       } else if (tvc->f.ddirty_flags & VDisconCreate) {
            /* For newly created files, we don't need a server lock. */
            code = afs_ProcessOpCreate(tvc, areq, acred);
-           if (code == ENOTEMPTY)
-               defered = 1;
            if (code)
-               goto skip_file;
-       }
+               goto next_file;
 
+           tvc->f.ddirty_flags &= ~VDisconCreate;
+           tvc->f.ddirty_flags |= VDisconCreated;
+       }
+#if 0
        /* Get server write lock. */
        do {
-           tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK);
+           tc = afs_Conn(&tvc->f.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,
+                                       (struct AFSFid *)&tvc->f.fid.Fid,
                                        LockWrite,
                                        &tsync);
                RX_AFS_GLOCK();
@@ -1138,35 +1157,32 @@ int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred)
 
        } while (afs_Analyze(tc,
                        code,
-                       &tvc->fid,
+                       &tvc->f.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. */
+       if (code)
+           goto next_file;
+#endif
+       if (tvc->f.ddirty_flags & VDisconRename) {
+           /* If we're renaming the file, do so now */
            code = afs_ProcessOpRename(tvc, areq);
            if (code)
-               goto skip_file;
+               goto unlock_srv_file;
        }
 
        /* Issue a FetchStatus to get info about DV and callbacks. */
        do {
-           tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK);
+           tc = afs_Conn(&tvc->f.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,
+                               (struct AFSFid *)&tvc->f.fid.Fid,
                                &fstat,
                                &callback,
                                &tsync);
@@ -1177,20 +1193,19 @@ int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred)
 
        } while (afs_Analyze(tc,
                        code,
-                       &tvc->fid,
+                       &tvc->f.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)) ||
+       if ((dv_match(tvc, fstat) && (tvc->f.m.Date == fstat.ServerModTime)) ||
                (afs_ConflictPolicy == CLIENT_WINS) ||
-               (tvc->ddirty_flags & VDisconCreate)) {
+               (tvc->f.ddirty_flags & VDisconCreated)) {
            /*
             * Send changes to the server if there's data version match, or
             * client wins policy has been selected or file has been created
@@ -1200,135 +1215,143 @@ int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred)
            * 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);
+           afs_UpdateStatus(tvc, &tvc->f.fid, areq, &fstat, &callback, start);
            code = afs_SendChanges(tvc, areq);
 
        } else if (afs_ConflictPolicy == SERVER_WINS) {
            /* DV mismatch, apply collision resolution policy. */
-           /* 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;
-
+           afs_ResetVCache(tvc, acred);
+           tvc->f.truncPos = AFS_NOTRUNC;
        } else {
-           printf("afs_ResyncDisconFiles: no resolution policy selected.\n");
+           /* 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. */
+#if 0
        do {
-           tc = afs_Conn(&tvc->fid, areq, SHARED_LOCK);
+           tc = afs_Conn(&tvc->f.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,
+               ucode = RXAFS_ReleaseLock(tc->id,
+                               (struct AFSFid *) &tvc->f.fid.Fid,
                                &tsync);
                RX_AFS_GLOCK();
                XSTATS_END_TIME;
            } else
-               code = -1;
+               ucode = -1;
        } while (afs_Analyze(tc,
-                       code,
-                       &tvc->fid,
+                       ucode,
+                       &tvc->f.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;
+#endif
+next_file:
+       ObtainWriteLock(&afs_disconDirtyLock, 710);
+       if (code == 0) {
+           /* Replayed successfully - pull the vcache from the 
+            * disconnected list */
+           tvc->f.ddirty_flags = 0;
+           QRemove(&tvc->dirtyq);
+           afs_PutVCache(tvc);
        } else {
-           tmp->ddirty_next = NULL;
-           defered = 0;
-       }                       /* if (!defered) */
+           if (code == EAGAIN) {
+               /* Operation was deferred. Pull it from the current place in 
+                * the list, and stick it at the end again */
+               QRemove(&tvc->dirtyq);
+               QAdd(&afs_disconDirty, &tvc->dirtyq);
+           } else {
+               /* Failed - keep state as is, and let the user know we died */
+
+               ReleaseWriteLock(&tvc->lock);
+               break;
+           }
+       }
 
        /* Release local write lock. */
-       ReleaseWriteLock(&tmp->lock);
+       ReleaseWriteLock(&tvc->lock);
     }                  /* while (tvc) */
 
-    /* Delete the rest of shadow dirs. */
-    tvc = shListStart;
-    while (tvc) {
-       ObtainWriteLock(&tvc->lock, 764);
+    if (code) {
+        ReleaseWriteLock(&afs_disconDirtyLock);
+       return code;
+    }
 
-       afs_DeleteShadowDir(tvc);
-       tvc->shVnode = 0;
-       tvc->shUnique = 0;
+    /* Dispose of all of the shadow directories */
+    afs_DisconDiscardAllShadows(0, acred);
+
+    ReleaseWriteLock(&afs_disconDirtyLock);
+    return code;
+}
+
+/*!
+ * Discard all of our shadow directory copies. If squash is true, then
+ * we also invalidate the vcache holding the shadow directory, to ensure
+ * that any disconnected changes are deleted
+ * 
+ * \param squash
+ * \param acred
+ *
+ * \note afs_disconDirtyLock must be held on entry. It will be released
+ * and reobtained
+ */
+
+static void
+afs_DisconDiscardAllShadows(int squash, afs_ucred_t *acred)
+{
+   struct vcache *tvc;
+
+   while (!QEmpty(&afs_disconShadow)) {
+       tvc = QEntry(QNext(&afs_disconShadow), struct vcache, shadowq);
+
+       /* Must release the dirty lock to be able to get a vcache lock */
+       ReleaseWriteLock(&afs_disconDirtyLock);
+       ObtainWriteLock(&tvc->lock, 706);
 
-       tmp = tvc;
-       tvc = tvc->ddirty_next;
-       tmp->ddirty_next = NULL;
+       if (squash)
+          afs_ResetVCache(tvc, acred);
 
-       ReleaseWriteLock(&tmp->lock);
+       afs_DeleteShadowDir(tvc);
+
+       ReleaseWriteLock(&tvc->lock);
+       ObtainWriteLock(&afs_disconDirtyLock, 709);
     }                          /* while (tvc) */
+}
+
+/*!
+ * This function throws away the whole disconnected state, allowing
+ * the cache manager to reconnect to a server if we get into a state
+ * where reconiliation is impossible.
+ *
+ * \param acred
+ *
+ */
+void 
+afs_DisconDiscardAll(afs_ucred_t *acred)
+{
+    struct vcache *tvc;
 
-    if (ret_code == 0) {
-       /* NULLIFY dirty list only if resync complete. */
-       afs_DDirtyVCListStart = NULL;
-       afs_DDirtyVCList = NULL;
+    ObtainWriteLock(&afs_disconDirtyLock, 717);
+    while (!QEmpty(&afs_disconDirty)) {
+       tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
+       QRemove(&tvc->dirtyq);
+       ReleaseWriteLock(&afs_disconDirtyLock);
+
+       ObtainWriteLock(&tvc->lock, 718);
+       afs_ResetVCache(tvc, acred);
+       tvc->f.truncPos = AFS_NOTRUNC;
+       ReleaseWriteLock(&tvc->lock);
+       ObtainWriteLock(&afs_disconDirtyLock, 719);
+       afs_PutVCache(tvc);
     }
-    ReleaseReadLock(&afs_DDirtyVCListLock);
 
-    return ret_code;
+    afs_DisconDiscardAllShadows(1, acred);
+
+    ReleaseWriteLock(&afs_disconDirtyLock);
 }
 
 /*!
@@ -1336,24 +1359,30 @@ skip_file:
  *
  * \note Call with afs_DDirtyVCListLock read locked.
  */
-void afs_DbgDisconFiles()
+void
+afs_DbgDisconFiles(void)
 {
     struct vcache *tvc;
+    struct afs_q *q;
     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;
+    afs_warn("List of dirty files: \n");
+
+    ObtainReadLock(&afs_disconDirtyLock);
+    for (q = QPrev(&afs_disconDirty); q != &afs_disconDirty; q = QPrev(q)) {
+        tvc = QEntry(q, struct vcache, dirtyq);
+
+       afs_warn("Cell=%u Volume=%u VNode=%u Unique=%u\n",
+               tvc->f.fid.Cell,
+               tvc->f.fid.Fid.Volume,
+               tvc->f.fid.Fid.Vnode,
+               tvc->f.fid.Fid.Unique);
+
        i++;
        if (i >= 30)
            osi_Panic("afs_DbgDisconFiles: loop in dirty list\n");
     }
+    ReleaseReadLock(&afs_disconDirtyLock);
 }
 
 /*!
@@ -1366,13 +1395,15 @@ void afs_DbgDisconFiles()
  *
  * \note Don't forget to fill in afid with Cell and Volume.
  */
-void afs_GenShadowFid(struct VenusFid *afid)
+void
+afs_GenShadowFid(struct VenusFid *afid)
 {
     afs_uint32 i, index, max_unique = 1;
     struct vcache *tvc = NULL;
 
     /* Try generating a fid that isn't used in the vhash. */
     do {
+       /* Shadow Fids are always directories */
        afid->Fid.Vnode = afs_DisconVnode + 1;
 
        i = DVHash(afid);
@@ -1404,28 +1435,38 @@ void afs_GenShadowFid(struct VenusFid *afid)
  * 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 afid  The fid structre that will be filled.
  * \param avtype Vnode type: VDIR/VREG.
+ * \param lock  True indicates that xvcache may be obtained,
+ *              False that it is already held
  *
  * \note The cell number must be completed somewhere else.
  */
-void afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype)
+void
+afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype, int lock)
 {
     struct vcache *tvc;
-    afs_uint32 max_unique = 1, i;
+    afs_uint32 max_unique = 0, i;
 
-    if (avtype == VDIR)
+    switch (avtype) {
+    case VDIR:
        afid->Fid.Vnode = afs_DisconVnode + 1;
-    else if (avtype == VREG)
+       break;
+    case VREG:
+    case VLNK:
        afid->Fid.Vnode = afs_DisconVnode;
+       break;
+    }
 
-    ObtainWriteLock(&afs_xvcache, 736);
+    if (lock)
+       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;
+        if (tvc->f.fid.Fid.Unique > max_unique)
+           max_unique = tvc->f.fid.Fid.Unique;
     }
-    ReleaseWriteLock(&afs_xvcache);
+    if (lock)
+       ReleaseWriteLock(&afs_xvcache);
 
     afid->Fid.Unique = max_unique + 1;
     afs_DisconVnode += 2;
@@ -1445,42 +1486,52 @@ void afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype)
  *
  * \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)
+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;
+    memcpy(&avc->f.fid, afid, sizeof(struct VenusFid));
+    avc->f.m.Mode = attrs->va_mode;
     /* Used to do this:
-     * avc->m.Owner = attrs->va_uid;
+     * avc->f.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) {
+    avc->f.m.Group = adp->f.m.Group;
+    avc->f.m.Owner = adp->f.m.Owner;
+    hset64(avc->f.m.DataVersion, 0, 0);
+    avc->f.m.Length = attrs->va_size;
+    avc->f.m.Date = osi_Time();
+    switch(file_type) {
+      case VREG:
+       vSetType(avc, VREG);
+        avc->f.m.Mode |= S_IFREG;
+       avc->f.m.LinkCount = 1;
+       avc->f.parent.vnode = adp->f.fid.Fid.Vnode;
+       avc->f.parent.unique = adp->f.fid.Fid.Unique;
+       break;
+      case VDIR:
         vSetType(avc, VDIR);
-        avc->m.Mode |= S_IFDIR;
-       avc->m.LinkCount = 2;
+        avc->f.m.Mode |= S_IFDIR;
+       avc->f.m.LinkCount = 2;
+       break;
+      case VLNK:
+       vSetType(avc, VLNK);
+       avc->f.m.Mode |= S_IFLNK;
+       if ((avc->f.m.Mode & 0111) == 0)
+           avc->mvstat = 1;
+       avc->f.parent.vnode = adp->f.fid.Fid.Vnode;
+       avc->f.parent.unique = adp->f.fid.Fid.Unique;
+       break;
+      default:
+       break;
     }
-
-    avc->anyAccess = adp->anyAccess;
+    avc->f.anyAccess = adp->f.anyAccess;
     afs_AddAxs(avc->Access, areq->uid, adp->Access->axess);
 
     avc->callback = NULL;
-    avc->states |= CStatd;
-    avc->states &= ~CBulkFetching;
+    avc->f.states |= CStatd;
+    avc->f.states &= ~CBulkFetching;
 }
-#endif