windows-vc-locks-and-more-20060115
[openafs.git] / src / WINNT / afsd / smb.c
index 2a80dab..a5cfe6b 100644 (file)
@@ -211,6 +211,53 @@ int smb_ServerLanManagerLength = sizeof(smb_ServerLanManager);
 /* Faux server GUID. This is never checked. */
 GUID smb_ServerGUID = { 0x40015cb8, 0x058a, 0x44fc, { 0xae, 0x7e, 0xbb, 0x29, 0x52, 0xee, 0x7e, 0xff }};
 
+const char * ncb_error_string(int code)
+{
+    const char * s;
+    switch ( code ) {
+    case 0x01: s = "llegal buffer length";                     break; 
+    case 0x03: s = "illegal command";                          break; 
+    case 0x05: s = "command timed out";                        break; 
+    case 0x06: s = "message incomplete, issue another command"; break; 
+    case 0x07: s = "illegal buffer address";                   break; 
+    case 0x08: s = "session number out of range";              break; 
+    case 0x09: s = "no resource available";                    break; 
+    case 0x0a: s = "session closed";                           break; 
+    case 0x0b: s = "command cancelled";                        break; 
+    case 0x0d: s = "duplicate name";                           break; 
+    case 0x0e: s = "name table full";                          break; 
+    case 0x0f: s = "no deletions, name has active sessions";   break; 
+    case 0x11: s = "local session table full";                         break; 
+    case 0x12: s = "remote session table full";                break; 
+    case 0x13: s = "illegal name number";                      break; 
+    case 0x14: s = "no callname";                              break; 
+    case 0x15: s = "cannot put * in NCB_NAME";                         break; 
+    case 0x16: s = "name in use on remote adapter";            break; 
+    case 0x17: s = "name deleted";                             break; 
+    case 0x18: s = "session ended abnormally";                         break; 
+    case 0x19: s = "name conflict detected";                   break; 
+    case 0x21: s = "interface busy, IRET before retrying";     break; 
+    case 0x22: s = "too many commands outstanding, retry later";break;
+    case 0x23: s = "ncb_lana_num field invalid";               break; 
+    case 0x24: s = "command completed while cancel occurring "; break; 
+    case 0x26: s = "command not valid to cancel";              break; 
+    case 0x30: s = "name defined by anther local process";     break; 
+    case 0x34: s = "environment undefined. RESET required";    break; 
+    case 0x35: s = "required OS resources exhausted";          break; 
+    case 0x36: s = "max number of applications exceeded";      break; 
+    case 0x37: s = "no saps available for netbios";            break; 
+    case 0x38: s = "requested resources are not available";    break; 
+    case 0x39: s = "invalid ncb address or length > segment";  break; 
+    case 0x3B: s = "invalid NCB DDID";                                 break; 
+    case 0x3C: s = "lock of user area failed";                         break; 
+    case 0x3f: s = "NETBIOS not loaded";                       break; 
+    case 0x40: s = "system error";                             break;                 
+    default:   s = "unknown error";
+    }
+    return s;
+}
+
+
 char * myCrt_Dispatch(int i)
 {
     switch (i)
@@ -825,10 +872,10 @@ smb_vc_t *smb_FindVC(unsigned short lsn, int flags, int lana)
         vcp = malloc(sizeof(*vcp));
         memset(vcp, 0, sizeof(*vcp));
         vcp->vcID = numVCs++;
-        vcp->refCount = 1;
+        vcp->refCount = 2;     /* smb_allVCsp and caller */
         vcp->tidCounter = 1;
         vcp->fidCounter = 1;
-        vcp->uidCounter = 1;  /* UID 0 is reserved for blank user */
+        vcp->uidCounter = 1;   /* UID 0 is reserved for blank user */
         vcp->nextp = smb_allVCsp;
         smb_allVCsp = vcp;
         lock_InitializeMutex(&vcp->mx, "vc_t mutex");
@@ -886,25 +933,31 @@ int smb_IsStarMask(char *maskp)
     return 0;
 }
 
-void smb_ReleaseVCNoLock(smb_vc_t *vcp)
+void smb_ReleaseVCInternal(smb_vc_t *vcp)
 {
-    osi_Log2(smb_logp,"smb_ReleaseVCNoLock vcp %x ref %d",vcp, vcp->refCount);
 #ifdef DEBUG
     osi_assert(vcp->refCount-- != 0);
 #else
     vcp->refCount--;
 #endif
+
+    if (vcp->refCount == 0) {
+       memset(vcp,0,sizeof(smb_vc_t));
+       free(vcp);
+    }
+}
+
+void smb_ReleaseVCNoLock(smb_vc_t *vcp)
+{
+    osi_Log2(smb_logp,"smb_ReleaseVCNoLock vcp %x ref %d",vcp, vcp->refCount);
+    smb_ReleaseVCInternal(vcp);
 }       
 
 void smb_ReleaseVC(smb_vc_t *vcp)
 {
     lock_ObtainWrite(&smb_rctLock);
     osi_Log2(smb_logp,"smb_ReleaseVC       vcp %x ref %d",vcp, vcp->refCount);
-#ifdef DEBUG
-    osi_assert(vcp->refCount-- != 0);
-#else
-    vcp->refCount--;
-#endif
+    smb_ReleaseVCInternal(vcp);
     lock_ReleaseWrite(&smb_rctLock);
 }       
 
@@ -922,6 +975,99 @@ void smb_HoldVC(smb_vc_t *vcp)
     lock_ReleaseWrite(&smb_rctLock);
 }       
 
+void smb_CleanupDeadVC(smb_vc_t *vcp)
+{
+    smb_fid_t *fidpIter;
+    smb_fid_t *fidpNext;
+    smb_fid_t *fidp;
+    unsigned short fid;
+    smb_tid_t *tidpIter;
+    smb_tid_t *tidpNext;
+    smb_tid_t *tidp;
+    unsigned short tid;
+    smb_user_t *userpIter;
+    smb_user_t *userpNext;
+    smb_user_t *userp;
+    unsigned short uid;
+    smb_vc_t **vcpp;
+
+    osi_Log1(smb_logp, "Cleaning up dead vcp 0x%x", vcp);
+
+    lock_ObtainRead(&smb_rctLock);
+    for (fidpIter = vcp->fidsp; fidpIter; fidpIter = fidpNext) {
+        fidpNext = (smb_fid_t *) osi_QNext(&fidpIter->q);
+
+        if (fidpIter->flags & SMB_FID_DELETE)
+            continue;
+
+        fid = fidpIter->fid;
+       osi_Log2(smb_logp, " Cleanup FID %d (fidp=0x%x)", fid, fidpIter);
+        lock_ReleaseRead(&smb_rctLock);
+
+        fidp = smb_FindFID(vcp, fid, 0);
+        osi_assert(fidp);
+        smb_CloseFID(vcp, fidp, NULL, 0);
+        smb_ReleaseFID(fidp);
+
+        lock_ObtainRead(&smb_rctLock);
+    }
+
+    for (tidpIter = vcp->tidsp; tidpIter; tidpIter = tidpNext) {
+       tidpNext = tidpIter->nextp;
+
+       if (tidpIter->flags & SMB_TIDFLAG_DELETE)
+           continue;
+
+       tid = tidpIter->tid;
+       osi_Log2(smb_logp, "  Cleanup TID %d (tidp=0x%x)", tid, tidpIter);
+       lock_ReleaseRead(&smb_rctLock);
+
+       tidp = smb_FindTID(vcp, tid, 0);
+       osi_assert(tidp);
+
+       lock_ObtainMutex(&tidp->mx);
+       tidp->flags |= SMB_TIDFLAG_DELETE;
+       lock_ReleaseMutex(&tidp->mx);
+
+       smb_ReleaseTID(tidp);
+
+       lock_ObtainRead(&smb_rctLock);
+    }
+
+    for (userpIter = vcp->usersp; userpIter; userpIter = userpNext) {
+       userpNext = userpIter->nextp;
+
+       if (userpIter->flags & SMB_USERFLAG_DELETE)
+           continue;
+
+       uid = userpIter->userID;
+       osi_Log2(smb_logp, "  Cleanup UID %d (userp=0x%x)", uid, userpIter);
+       lock_ReleaseRead(&smb_rctLock);
+
+       userp = smb_FindUID(vcp, uid, 0);
+       osi_assert(userp);
+
+       lock_ObtainMutex(&userp->mx);
+       userp->flags |= SMB_USERFLAG_DELETE;
+       lock_ReleaseMutex(&userp->mx);
+
+       smb_ReleaseUID(userp);
+
+       lock_ObtainRead(&smb_rctLock);
+    }
+
+    /* remove VCP from smb_allVCsp */
+    for (vcpp = &smb_allVCsp; *vcpp; vcpp = &((*vcpp)->nextp)) {
+       if (*vcpp == vcp) {
+           *vcpp = vcp->nextp;
+           smb_ReleaseVCNoLock(vcp);
+           break;
+       }
+    } 
+    lock_ReleaseRead(&smb_rctLock);
+    osi_Log0(smb_logp, "Done cleaning up dead vcp");
+}
+
 smb_tid_t *smb_FindTID(smb_vc_t *vcp, unsigned short tid, int flags)
 {
     smb_tid_t *tidp;
@@ -994,7 +1140,7 @@ smb_user_t *smb_FindUID(smb_vc_t *vcp, unsigned short uid, int flags)
         uidp = malloc(sizeof(*uidp));
         memset(uidp, 0, sizeof(*uidp));
         uidp->nextp = vcp->usersp;
-        uidp->refCount = 1;
+        uidp->refCount = 2; /* one for the vcp and one for the caller */
         uidp->vcp = vcp;
         smb_HoldVCNoLock(vcp);
         vcp->usersp = uidp;
@@ -1053,16 +1199,46 @@ smb_user_t *smb_FindUserByNameThisSession(smb_vc_t *vcp, char *usern)
     lock_ReleaseWrite(&smb_rctLock);
     return uidp;
 }       
+
+void smb_ReleaseUsername(smb_username_t *unp)
+{
+    smb_username_t *up;
+    smb_username_t **lupp;
+    cm_user_t *userp = NULL;
+
+    lock_ObtainWrite(&smb_rctLock);
+    osi_assert(unp->refCount-- > 0);
+    if (unp->refCount == 0) {
+        lupp = &usernamesp;
+        for(up = *lupp; up; lupp = &up->nextp, up = *lupp) {
+            if (up == unp) 
+                break;
+        }
+        osi_assert(up != NULL);
+        *lupp = up->nextp;
+        lock_FinalizeMutex(&unp->mx);
+       userp = unp->userp;
+       free(unp->name);
+       free(unp->machine);
+       free(unp);
+    }          
+    lock_ReleaseWrite(&smb_rctLock);
+
+    if (userp) {
+        cm_ReleaseUserVCRef(userp);
+        cm_ReleaseUser(userp);
+    }  
+}      
+
 void smb_ReleaseUID(smb_user_t *uidp)
 {
     smb_user_t *up;
     smb_user_t **lupp;
-    cm_user_t *userp;
+    smb_username_t *unp = NULL;
 
-    userp = NULL;
     lock_ObtainWrite(&smb_rctLock);
     osi_assert(uidp->refCount-- > 0);
-    if (uidp->refCount == 0 && (uidp->flags & SMB_USERFLAG_DELETE)) {
+    if (uidp->refCount == 0) {
         lupp = &uidp->vcp->usersp;
         for(up = *lupp; up; lupp = &up->nextp, up = *lupp) {
             if (up == uidp) 
@@ -1071,20 +1247,15 @@ void smb_ReleaseUID(smb_user_t *uidp)
         osi_assert(up != NULL);
         *lupp = up->nextp;
         lock_FinalizeMutex(&uidp->mx);
-        if (uidp->unp) {
-            userp = uidp->unp->userp;   /* avoid deadlock by releasing */
-            uidp->unp->userp = NULL;    /* after releasing the lock */
-        }       
+       unp = uidp->unp;
         smb_ReleaseVCNoLock(uidp->vcp);
-        uidp->vcp = NULL;
+       free(uidp);
     }          
     lock_ReleaseWrite(&smb_rctLock);
-    if (userp) {
-        cm_ReleaseUserVCRef(userp);
-        cm_ReleaseUser(userp);
-    }  
-}      
 
+    if (unp)
+       smb_ReleaseUsername(unp);
+}      
 
 /* retrieve a held reference to a user structure corresponding to an incoming
  * request.
@@ -1093,21 +1264,22 @@ void smb_ReleaseUID(smb_user_t *uidp)
 cm_user_t *smb_GetUser(smb_vc_t *vcp, smb_packet_t *inp)
 {
     smb_user_t *uidp;
-    cm_user_t *up;
+    cm_user_t *up = NULL;
     smb_t *smbp;
 
     smbp = (smb_t *) inp;
     uidp = smb_FindUID(vcp, smbp->uid, 0);
-    if ((!uidp) ||  (!uidp->unp))
-        return NULL;
-
+    if (!uidp)
+       return NULL;
+    
     lock_ObtainMutex(&uidp->mx);
-    up = uidp->unp->userp;
-    cm_HoldUser(up);
+    if (uidp->unp) {
+       up = uidp->unp->userp;
+       cm_HoldUser(up);
+    }
     lock_ReleaseMutex(&uidp->mx);
 
     smb_ReleaseUID(uidp);
-
     return up;
 }
 
@@ -1177,8 +1349,11 @@ smb_fid_t *smb_FindFID(smb_vc_t *vcp, unsigned short fid, int flags)
         if (fid == fidp->fid) {
             if (newFid) {
                 fid++;
-                if (fid == 0) 
+                if (fid == 0) {
+                    osi_Log1(smb_logp,
+                             "New FID number wraps on vcp 0x%x", vcp);
                     fid = 1;
+                }
                 goto retry;
             }
             fidp->refCount++;
@@ -1195,8 +1370,10 @@ smb_fid_t *smb_FindFID(smb_vc_t *vcp, unsigned short fid, int flags)
             osi_Log1(smb_logp, "Event Object Already Exists: %s", osi_LogSaveString(smb_logp, eventName));
             thrd_CloseHandle(event);
             fid++;
-            if (fid == 0)
+            if (fid == 0) {
+                osi_Log1(smb_logp, "New FID wraps around for vcp 0x%x", vcp);
                 fid = 1;
+            }
             goto retry;
         }
 
@@ -1212,10 +1389,13 @@ smb_fid_t *smb_FindFID(smb_vc_t *vcp, unsigned short fid, int flags)
         fidp->raw_write_event = event;
         if (newFid) {
             vcp->fidCounter = fid+1;
-            if (vcp->fidCounter == 0) 
+            if (vcp->fidCounter == 0) {
+                osi_Log1(smb_logp, "fidCounter wrapped around for vcp 0x%x",
+                         vcp);
                 vcp->fidCounter = 1;
         }
     }
+    }
 
     lock_ReleaseWrite(&smb_rctLock);
     return fidp;
@@ -1224,6 +1404,7 @@ smb_fid_t *smb_FindFID(smb_vc_t *vcp, unsigned short fid, int flags)
 void smb_ReleaseFID(smb_fid_t *fidp)
 {
     cm_scache_t *scp;
+    cm_user_t *userp;
     smb_vc_t *vcp = NULL;
     smb_ioctl_t *ioctlp;
 
@@ -1231,13 +1412,16 @@ void smb_ReleaseFID(smb_fid_t *fidp)
         return;
 
     scp = NULL;
+    userp = NULL;
     lock_ObtainWrite(&smb_rctLock);
     osi_assert(fidp->refCount-- > 0);
     if (fidp->refCount == 0 && (fidp->flags & SMB_FID_DELETE)) {
         vcp = fidp->vcp;
-        fidp->vcp = 0;
+        fidp->vcp = NULL;
         scp = fidp->scp;    /* release after lock is released */
-        fidp->scp = 0;
+        fidp->scp = NULL;
+        userp = fidp->userp;
+        fidp->userp = NULL;
 
         osi_QRemove((osi_queue_t **) &vcp->fidsp, &fidp->q);
         thrd_CloseHandle(fidp->raw_write_event);
@@ -1263,6 +1447,9 @@ void smb_ReleaseFID(smb_fid_t *fidp)
     /* now release the scache structure */
     if (scp) 
         cm_ReleaseSCache(scp);
+
+    if (userp)
+        cm_ReleaseUser(userp);
 }       
 
 /*
@@ -2253,49 +2440,22 @@ void smb_SendPacket(smb_vc_t *vcp, smb_packet_t *inp)
 #endif /* !DJGPP */
         
     if (code != 0) {
-        char * s;
-        switch ( code ) {
-        case 0x01: s = "llegal buffer length                     "; break; 
-        case 0x03: s = "illegal command                          "; break; 
-        case 0x05: s = "command timed out                        "; break; 
-        case 0x06: s = "message incomplete, issue another command"; break; 
-        case 0x07: s = "illegal buffer address                   "; break; 
-        case 0x08: s = "session number out of range              "; break; 
-        case 0x09: s = "no resource available                    "; break; 
-        case 0x0a: s = "session closed                           "; break; 
-        case 0x0b: s = "command cancelled                        "; break; 
-        case 0x0d: s = "duplicate name                           "; break; 
-        case 0x0e: s = "name table full                          "; break; 
-        case 0x0f: s = "no deletions, name has active sessions   "; break; 
-        case 0x11: s = "local session table full                 "; break; 
-        case 0x12: s = "remote session table full                "; break; 
-        case 0x13: s = "illegal name number                      "; break; 
-        case 0x14: s = "no callname                              "; break; 
-        case 0x15: s = "cannot put * in NCB_NAME                 "; break; 
-        case 0x16: s = "name in use on remote adapter            "; break; 
-        case 0x17: s = "name deleted                             "; break; 
-        case 0x18: s = "session ended abnormally                 "; break; 
-        case 0x19: s = "name conflict detected                   "; break; 
-        case 0x21: s = "interface busy, IRET before retrying     "; break; 
-        case 0x22: s = "too many commands outstanding, retry later"; break;
-        case 0x23: s = "ncb_lana_num field invalid               "; break; 
-        case 0x24: s = "command completed while cancel occurring "; break; 
-        case 0x26: s = "command not valid to cancel              "; break; 
-        case 0x30: s = "name defined by anther local process     "; break; 
-        case 0x34: s = "environment undefined. RESET required    "; break; 
-        case 0x35: s = "required OS resources exhausted          "; break; 
-        case 0x36: s = "max number of applications exceeded      "; break; 
-        case 0x37: s = "no saps available for netbios            "; break; 
-        case 0x38: s = "requested resources are not available    "; break; 
-        case 0x39: s = "invalid ncb address or length > segment  "; break; 
-        case 0x3B: s = "invalid NCB DDID                         "; break; 
-        case 0x3C: s = "lock of user area failed                 "; break; 
-        case 0x3f: s = "NETBIOS not loaded                       "; break; 
-        case 0x40: s = "system error                             "; break;                 
-        default:
-            s = "unknown error";
-        }
+       const char * s = ncb_error_string(code);
         osi_Log2(smb_logp, "SendPacket failure code %d \"%s\"", code, s);
+#ifndef DJGPP
+       LogEvent(EVENTLOG_WARNING_TYPE, MSG_SMB_SEND_PACKET_FAILURE, s);
+#endif /* !DJGPP */
+
+       osi_Log2(smb_logp, "setting dead_vcp 0x%x, user struct 0x%x",
+                 vcp, vcp->usersp);
+       smb_HoldVC(vcp);
+       if (dead_vcp) {
+           osi_Log1(smb_logp,"Previous dead_vcp %x", dead_vcp);
+           smb_CleanupDeadVC(dead_vcp);
+           smb_ReleaseVC(dead_vcp);
+       }
+       dead_vcp = vcp;
+       vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
     }
 
     if (localNCB)
@@ -2850,19 +3010,20 @@ long smb_ReceiveNegotiate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
                         ongoingOps - 1);
     if (!isGateway) {
         if (active_vcp) {
-            DWORD now = GetCurrentTime();
-            if (now - last_msg_time >= 30000
-                 && now - last_msg_time <= 90000) {
-                osi_Log1(smb_logp,
-                          "Setting dead_vcp %x", active_vcp);
-                if (dead_vcp) {
+            DWORD now = GetTickCount();
+            if (now - last_msg_time >= 30000) {
+               smb_vc_t *avcp = active_vcp;
+               active_vcp = NULL;
+                osi_Log1(smb_logp,"Setting dead_vcp %x", avcp);
+               if (dead_vcp) {
+                    osi_Log1(smb_logp,"Previous dead_vcp %x", dead_vcp);
+                   smb_CleanupDeadVC(dead_vcp);
                     smb_ReleaseVC(dead_vcp);
-                    osi_Log1(smb_logp,
-                             "Previous dead_vcp %x", dead_vcp);
                 }
-                smb_HoldVC(active_vcp);
-                dead_vcp = active_vcp;
+                smb_HoldVC(avcp);
+                dead_vcp = avcp;
                 dead_vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
+               smb_ReleaseVC(avcp);
             }
         }
     }
@@ -3112,8 +3273,10 @@ void smb_WaitingLocksDaemon()
             osi_SleepW((LONG_PTR)&smb_allWaitingLocks, &smb_globalLock);
             thrd_Sleep(1000);
             continue;
-        } else 
+        } else {
             first = 1;
+            osi_Log0(smb_logp, "smb_WaitingLocksDaemon starting wait lock check");
+        }
 
         do {
             if (first)
@@ -3121,6 +3284,8 @@ void smb_WaitingLocksDaemon()
             else
                 lock_ObtainWrite(&smb_globalLock);
 
+            osi_Log1(smb_logp, "    Checking waiting lock request %p", nwlRequest);
+
             wlRequest = nwlRequest;
             nwlRequest = (smb_waitingLockRequest_t *) osi_QNext(&wlRequest->q);
             lock_ReleaseWrite(&smb_globalLock);
@@ -3130,6 +3295,8 @@ void smb_WaitingLocksDaemon()
             for (wl = wlRequest->locks; wl; wl = (smb_waitingLock_t *) osi_QNext(&wl->q)) {
                 if (wl->state == SMB_WAITINGLOCKSTATE_DONE)
                     continue;
+
+                osi_assert(wl->state != SMB_WAITINGLOCKSTATE_ERROR);
                 
                 /* wl->state is either _DONE or _WAITING.  _ERROR
                    would no longer be on the queue. */
@@ -3160,6 +3327,9 @@ void smb_WaitingLocksDaemon()
                 cm_scache_t * scp;
                 cm_req_t req;
 
+                osi_Log1(smb_logp, "smb_WaitingLocksDaemon discarding lock req %p",
+                         wlRequest);
+
                 scp = wlRequest->scp;
 
                 cm_InitReq(&req);
@@ -3180,6 +3350,10 @@ void smb_WaitingLocksDaemon()
                 lock_ReleaseMutex(&scp->mx);
 
             } else {
+
+                osi_Log1(smb_logp, "smb_WaitingLocksDaemon granting lock req %p",
+                         wlRequest);
+
                 for (wl = wlRequest->locks; wl; wl = wlNext) {
                     wlNext = (smb_waitingLock_t *) osi_QNext(&wl->q);
                     osi_QRemove((osi_queue_t **) &wlRequest->locks, &wl->q);
@@ -3841,7 +4015,7 @@ long smb_ReceiveCoreSearchDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *ou
                      LargeIntegerGreaterThanOrEqualTo(thyper, 
                                                       scp->bulkStatProgress)) {
                     /* Don't bulk stat if risking timeout */
-                    int now = GetCurrentTime();
+                    int now = GetTickCount();
                     if (now - req.startTime > 5000) {
                         scp->bulkStatProgress = thyper;
                         scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
@@ -4569,6 +4743,9 @@ long smb_ReceiveCoreOpen(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
     /* save a pointer to the vnode */
     fidp->scp = scp;
+    /* and the user */
+    cm_HoldUser(userp);
+    fidp->userp = userp;
 
     if ((share & 0xf) == 0)
         fidp->flags |= SMB_FID_OPENREAD;
@@ -5380,29 +5557,25 @@ void smb_FullName(cm_scache_t *dscp, cm_scache_t *scp, char *pathp,
         *newPathp = strdup(pathp);
 }
 
-long smb_ReceiveCoreClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
-{
-    unsigned short fid;
-    smb_fid_t *fidp;
-    cm_user_t *userp;
-    afs_uint32 dosTime;
+long smb_CloseFID(smb_vc_t *vcp, smb_fid_t *fidp, cm_user_t *userp,
+                  afs_uint32 dosTime) {
     long code = 0;
     cm_req_t req;
 
-    cm_InitReq(&req);
-
-    fid = smb_GetSMBParm(inp, 0);
-    dosTime = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
+    osi_Log3(smb_logp, "smb_CloseFID Closing fidp 0x%x (fid=%d vcp=0x%x)",
+             fidp, fidp->fid, vcp);
 
-    osi_Log1(smb_logp, "SMB close fid %d", fid);
-
-    fid = smb_ChainFID(fid, inp);
-    fidp = smb_FindFID(vcp, fid, 0);
-    if (!fidp) {
+    if (!userp) {
+        if (!fidp->userp) {
+            osi_Log0(smb_logp, "  No user specified.  Not closing fid");
         return CM_ERROR_BADFD;
     }
         
-    userp = smb_GetUser(vcp, inp);
+        userp = fidp->userp;    /* no hold required since fidp is held
+                                   throughout the function */
+    }
+
+    cm_InitReq(&req);
 
     lock_ObtainMutex(&fidp->mx);
 
@@ -5434,12 +5607,12 @@ long smb_ReceiveCoreClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     if (!(fidp->flags & SMB_FID_IOCTL) && fidp->scp &&
         fidp->scp->fileType == CM_SCACHETYPE_FILE) {
         cm_key_t key;
-        unsigned pid;
         cm_scache_t * scp;
         long tcode;
 
-        pid = ((smb_t *) inp)->pid;
-        key = cm_GenerateKey(vcp->vcID, pid, fid);
+        /* CM_UNLOCK_BY_FID doesn't look at the process ID.  We pass
+           in zero. */
+        key = cm_GenerateKey(vcp->vcID, 0, fidp->fid);
         scp = fidp->scp;
         cm_HoldSCache(scp);
         lock_ObtainMutex(&scp->mx);
@@ -5450,7 +5623,8 @@ long smb_ReceiveCoreClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
                           | CM_SCACHESYNC_LOCK);
 
         if (tcode) {
-            osi_Log1(smb_logp, "smb CoreClose SyncOp failure code 0x%x", tcode);
+            osi_Log1(smb_logp,
+                     "smb CoreClose SyncOp failure code 0x%x", tcode);
             goto post_syncopdone;
         }
 
@@ -5476,9 +5650,7 @@ long smb_ReceiveCoreClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
                 smb_NotifyChange(FILE_ACTION_REMOVED,
                                  FILE_NOTIFY_CHANGE_DIR_NAME,
                                  dscp, fullPathp, NULL, TRUE);
-        }
-        else 
-        {
+        } else {
             code = cm_Unlink(dscp, fullPathp, userp, &req);
             if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
                 smb_NotifyChange(FILE_ACTION_REMOVED,
@@ -5492,9 +5664,38 @@ long smb_ReceiveCoreClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     if (fidp->flags & SMB_FID_NTOPEN) {
         cm_ReleaseSCache(fidp->NTopen_dscp);
         free(fidp->NTopen_pathp);
+        fidp->NTopen_pathp = NULL;
     }
-    if (fidp->NTopen_wholepathp)
+    if (fidp->NTopen_wholepathp) {
         free(fidp->NTopen_wholepathp);
+        fidp->NTopen_wholepathp = NULL;
+    }
+
+    return code;
+}
+
+long smb_ReceiveCoreClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
+{
+    unsigned short fid;
+    smb_fid_t *fidp;
+    cm_user_t *userp;
+    long code = 0;
+    afs_uint32 dosTime;
+
+    fid = smb_GetSMBParm(inp, 0);
+    dosTime = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
+
+    osi_Log1(smb_logp, "SMB ReceiveCoreClose fid %d", fid);
+
+    fid = smb_ChainFID(fid, inp);
+    fidp = smb_FindFID(vcp, fid, 0);
+    if (!fidp) {
+        return CM_ERROR_BADFD;
+    }
+        
+    userp = smb_GetUser(vcp, inp);
+
+    code = smb_CloseFID(vcp, fidp, userp, dosTime);
     
     smb_ReleaseFID(fidp);
     cm_ReleaseUser(userp);
@@ -5754,7 +5955,7 @@ long smb_WriteData(smb_fid_t *fidp, osi_hyper_t *offsetp, long count, char *op,
         /* handle over quota or out of space */
         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
             *writtenp = written;
-            code = CM_ERROR_QUOTA;
+            code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
             break;
         }
 
@@ -6612,6 +6813,9 @@ long smb_ReceiveCoreCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
        
     /* save a pointer to the vnode */
     fidp->scp = scp;
+    /* and the user */
+    cm_HoldUser(userp);
+    fidp->userp = userp;
         
     /* always create it open for read/write */
     fidp->flags |= (SMB_FID_OPENREAD | SMB_FID_OPENWRITE);
@@ -6735,7 +6939,7 @@ void smb_DispatchPacket(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp,
 
     /* Remember session generation number and time */
     oldGen = sessionGen;
-    oldTime = GetCurrentTime();
+    oldTime = GetTickCount();
 
     while (inp->inCom != 0xff) {
         dp = &smb_dispatchTable[inp->inCom];
@@ -6793,7 +6997,7 @@ void smb_DispatchPacket(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp,
             }   
 
             if (oldGen != sessionGen) {
-                newTime = GetCurrentTime();
+                newTime = GetTickCount();
 #ifndef DJGPP
                LogEvent(EVENTLOG_WARNING_TYPE, MSG_BAD_SMB_WRONG_SESSION, 
                         newTime - oldTime, ncbp->ncb_length);
@@ -6949,19 +7153,18 @@ void smb_DispatchPacket(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp,
     if (!(vcp->flags & SMB_VCFLAG_ALREADYDEAD)) {
         if (active_vcp != vcp) {
             if (active_vcp) {
-                smb_ReleaseVC(active_vcp);
                 osi_Log2(smb_logp,
                       "Replacing active_vcp %x with %x", active_vcp, vcp);
+                smb_ReleaseVC(active_vcp);
             }
             smb_HoldVC(vcp);
             active_vcp = vcp;
         }
-        last_msg_time = GetCurrentTime();
-    } else if (active_vcp == vcp) {
+        last_msg_time = GetTickCount();
+    } else if (active_vcp == vcp) {    /* the vcp is dead */
         smb_ReleaseVC(active_vcp);
         active_vcp = NULL;
     }
-
     return;
 }
 
@@ -7226,124 +7429,8 @@ void smb_Server(VOID *parmp)
         idx_session = NCBsessions[idx_NCB];
         rc = ncbp->ncb_retcode;
 
-        if (rc != NRC_PENDING && rc != NRC_GOODRET) {
-            switch (rc) {
-            case 0x01:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: illegal buffer length", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x03:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: illegal command", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x05:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: command timed out", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x06:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: message incomplete, issue another command", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x07:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: illegal buffer address", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x08:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: session number out of range", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x09:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: no resource available", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x0a:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: session closed", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x0b:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: command cancelled", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x0d:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: duplicate name", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x0e:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: name table full", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x0f:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: no deletions, name has active lsn %d sessions", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x11:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: local session table full", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x12:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: remote session table full", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x13:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: illegal name number", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x14:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: no callname", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x15:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: cannot put * in NCB_NAME", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x16:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: name in use on remote adapter", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x17:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: name deleted", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x18:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: session ended abnormally", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x19:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: name conflict detected", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x21:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: interface busy, IRET before retrying", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x22:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: too many commands outstanding, retry later", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x23:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: ncb_lana_num field invalid", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x24:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: command completed while cancel occurring", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x26:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: command not valid to cancel", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x30:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: name defined by anther local process", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x34:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: environment undefined. RESET required", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x35:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: required OS resources exhausted", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x36:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: max number of applications exceeded", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x37:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: no saps available for netbios", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x38:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: requested resources are not available", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x39:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: invalid ncb address or length > segment", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x3B:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: invalid NCB DDID", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x3C:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: lock of user area failed", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x3f:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: NETBIOS not loaded", ncbp->ncb_lsn, idx_session);
-                break;
-            case 0x40:
-                osi_Log2(smb_logp, "NCBRECV failure lsn %d session %d: system error", ncbp->ncb_lsn, idx_session);
-                break;
-            default:
-                osi_Log3(smb_logp, "NCBRECV failure lsn %d session %d code %d", ncbp->ncb_lsn, idx_session, rc);
-                break;
-            }
-        }
+        if (rc != NRC_PENDING && rc != NRC_GOODRET)
+           osi_Log3(smb_logp, "NCBRECV failure lsn %d session %d: %s", ncbp->ncb_lsn, idx_session, ncb_error_string(rc));
 
         switch (rc) {
         case NRC_GOODRET: 
@@ -7354,35 +7441,34 @@ void smb_Server(VOID *parmp)
             osi_Log2(smb_logp, "NCBRECV pending lsn %d session %d", ncbp->ncb_lsn, idx_session);
             continue;
 
-        case NRC_SCLOSED:
         case NRC_SNUMOUT:
+       case NRC_SABORT:
+#ifndef DJGPP
+           LogEvent(EVENTLOG_WARNING_TYPE, MSG_UNEXPECTED_SMB_SESSION_CLOSE, ncb_error_string(rc));
+           /* fallthrough */
+#endif /* !DJGPP */
+       case NRC_SCLOSED:
             /* Client closed session */
-            dead_sessions[idx_session] = TRUE;
+           dead_sessions[idx_session] = TRUE;
             if (vcp)
                 smb_ReleaseVC(vcp);
             vcp = smb_FindVC(ncbp->ncb_lsn, 0, lanas[idx_session]);
-            /* Should also release vcp.  [done] 2004-05-11 jaltman
-             * Also, should do
-             * sanity check that all TID's are gone. 
-             *
-             * TODO: check if we could use LSNs[idx_session] instead, 
-             * also cleanup after dead vcp 
-             */
             if (vcp) {
-                if (dead_vcp == vcp)
+               if (dead_vcp == vcp)
                     osi_Log1(smb_logp, "dead_vcp already set, 0x%x", dead_vcp);
                 else if (!(vcp->flags & SMB_VCFLAG_ALREADYDEAD)) {
                     osi_Log2(smb_logp, "setting dead_vcp 0x%x, user struct 0x%x",
                              vcp, vcp->usersp);
-                    smb_HoldVC(vcp);
+                   smb_HoldVC(vcp);
                     if (dead_vcp) {
+                        osi_Log1(smb_logp,"Previous dead_vcp %x", dead_vcp);
+                       smb_CleanupDeadVC(dead_vcp);
                         smb_ReleaseVC(dead_vcp);
-                        osi_Log1(smb_logp,
-                                  "Previous dead_vcp %x", dead_vcp);
                     }
                     dead_vcp = vcp;
                     vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
                 }
+
                 if (vcp->justLoggedOut) {
                     loggedOut = 1;
                     loggedOutTime = vcp->logoffTime;