Windows: validate buffer hash tables in cm_MergeStatus
[openafs.git] / src / WINNT / afsd / cm_scache.c
index d4a8b7a..687b0cd 100644 (file)
@@ -139,6 +139,9 @@ void cm_ResetSCacheDirectory(cm_scache_t *scp, afs_int32 dirlock)
 /* called with cm_scacheLock and scp write-locked; recycles an existing scp. */
 long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
 {
+    lock_AssertWrite(&cm_scacheLock);
+    lock_AssertWrite(&scp->rw);
+
     if (scp->refCount != 0) {
        return -1;
     }
@@ -151,64 +154,6 @@ long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
        return -1;
     }
 
-    cm_RemoveSCacheFromHashTable(scp);
-
-#if 0
-    if (flags & CM_SCACHE_RECYCLEFLAG_DESTROY_BUFFERS) {
-       osi_queueData_t *qdp;
-       cm_buf_t *bufp;
-
-       while(qdp = scp->bufWritesp) {
-            bufp = osi_GetQData(qdp);
-           osi_QRemove((osi_queue_t **) &scp->bufWritesp, &qdp->q);
-           osi_QDFree(qdp);
-           if (bufp) {
-               lock_ObtainMutex(&bufp->mx);
-               bufp->cmFlags &= ~CM_BUF_CMSTORING;
-               bufp->flags &= ~CM_BUF_DIRTY;
-                bufp->dirty_offset = 0;
-                bufp->dirty_length = 0;
-               bufp->flags |= CM_BUF_ERROR;
-               bufp->error = VNOVNODE;
-               bufp->dataVersion = CM_BUF_VERSION_BAD; /* bad */
-               bufp->dirtyCounter++;
-               if (bufp->flags & CM_BUF_WAITING) {
-                   osi_Log2(afsd_logp, "CM RecycleSCache Waking [scp 0x%p] bufp 0x%x", scp, bufp);
-                   osi_Wakeup((long) &bufp);
-               }
-               lock_ReleaseMutex(&bufp->mx);
-               buf_Release(bufp);
-           }
-        }
-       while(qdp = scp->bufReadsp) {
-            bufp = osi_GetQData(qdp);
-           osi_QRemove((osi_queue_t **) &scp->bufReadsp, &qdp->q);
-           osi_QDFree(qdp);
-           if (bufp) {
-               lock_ObtainMutex(&bufp->mx);
-               bufp->cmFlags &= ~CM_BUF_CMFETCHING;
-               bufp->flags &= ~CM_BUF_DIRTY;
-                bufp->dirty_offset = 0;
-                bufp->dirty_length = 0;
-               bufp->flags |= CM_BUF_ERROR;
-               bufp->error = VNOVNODE;
-               bufp->dataVersion = CM_BUF_VERSION_BAD; /* bad */
-               bufp->dirtyCounter++;
-               if (bufp->flags & CM_BUF_WAITING) {
-                   osi_Log2(afsd_logp, "CM RecycleSCache Waking [scp 0x%p] bufp 0x%x", scp, bufp);
-                   osi_Wakeup((long) &bufp);
-               }
-               lock_ReleaseMutex(&bufp->mx);
-               buf_Release(bufp);
-           }
-        }
-       buf_CleanDirtyBuffers(scp);
-    } else {
-       /* look for things that shouldn't still be set */
-       osi_assertx(scp->bufWritesp == NULL, "non-null cm_scache_t bufWritesp");
-       osi_assertx(scp->bufReadsp == NULL, "non-null cm_scache_t bufReadsp");
-    }
-#endif
 
     /* invalidate so next merge works fine;
      * also initialize some flags */
@@ -276,7 +221,7 @@ long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
 /*
  * called with cm_scacheLock write-locked; find a vnode to recycle.
  * Can allocate a new one if desperate, or if below quota (cm_data.maxSCaches).
- * returns scp->mx held.
+ * returns scp->rw write-locked.
  */
 cm_scache_t *cm_GetNewSCache(void)
 {
@@ -284,52 +229,6 @@ cm_scache_t *cm_GetNewSCache(void)
     int retry = 0;
 
     lock_AssertWrite(&cm_scacheLock);
-#if 0
-    /* first pass - look for deleted objects */
-    for ( scp = cm_data.scacheLRULastp;
-         scp;
-         scp = (cm_scache_t *) osi_QPrev(&scp->q))
-    {
-       osi_assertx(scp >= cm_data.scacheBaseAddress && scp < (cm_scache_t *)cm_data.scacheHashTablep,
-                    "invalid cm_scache_t address");
-
-       if (scp->refCount == 0) {
-           if (scp->flags & CM_SCACHEFLAG_DELETED) {
-                if (!lock_TryWrite(&scp->rw))
-                    continue;
-
-               osi_Log1(afsd_logp, "GetNewSCache attempting to recycle deleted scp 0x%p", scp);
-               if (!cm_RecycleSCache(scp, CM_SCACHE_RECYCLEFLAG_DESTROY_BUFFERS)) {
-
-                   /* we found an entry, so return it */
-                   /* now remove from the LRU queue and put it back at the
-                    * head of the LRU queue.
-                    */
-                   cm_AdjustScacheLRU(scp);
-
-                   /* and we're done */
-                   return scp;
-               }
-                lock_ReleaseWrite(&scp->rw);
-               osi_Log1(afsd_logp, "GetNewSCache recycled failed scp 0x%p", scp);
-           } else if (!(scp->flags & CM_SCACHEFLAG_INHASH)) {
-                if (!lock_TryWrite(&scp->rw))
-                    continue;
-
-               /* we found an entry, so return it */
-               /* now remove from the LRU queue and put it back at the
-               * head of the LRU queue.
-               */
-               cm_AdjustScacheLRU(scp);
-
-               /* and we're done */
-               return scp;
-           }
-       }
-    }
-    osi_Log0(afsd_logp, "GetNewSCache no deleted or recycled entries available for reuse");
-#endif
-
     if (cm_data.currentSCaches >= cm_data.maxSCaches) {
        /* There were no deleted scache objects that we could use.  Try to find
         * one that simply hasn't been used in a while.
@@ -583,20 +482,24 @@ cm_SuspendSCache(void)
 long
 cm_ShutdownSCache(void)
 {
-    cm_scache_t * scp;
+    cm_scache_t * scp, * nextp;
 
     cm_GiveUpAllCallbacksAllServersMulti(FALSE);
 
     lock_ObtainWrite(&cm_scacheLock);
 
     for ( scp = cm_data.allSCachesp; scp;
-          scp = scp->allNextp ) {
+          scp = nextp ) {
+        nextp = scp->allNextp;
+        lock_ReleaseWrite(&cm_scacheLock);
+#ifdef USE_BPLUS
+        lock_ObtainWrite(&scp->dirlock);
+#endif
+        lock_ObtainWrite(&scp->rw);
+        lock_ObtainWrite(&cm_scacheLock);
+
         if (scp->randomACLp) {
-            lock_ReleaseWrite(&cm_scacheLock);
-            lock_ObtainWrite(&scp->rw);
-            lock_ObtainWrite(&cm_scacheLock);
             cm_FreeAllACLEnts(scp);
-            lock_ReleaseWrite(&scp->rw);
         }
 
         if (scp->cbServerp) {
@@ -612,6 +515,7 @@ cm_ShutdownSCache(void)
             freeBtree(scp->dirBplus);
         scp->dirBplus = NULL;
         scp->dirDataVersion = CM_SCACHE_VERSION_BAD;
+        lock_ReleaseWrite(&scp->dirlock);
         lock_FinalizeRWLock(&scp->dirlock);
 #endif
         lock_FinalizeRWLock(&scp->rw);
@@ -1285,7 +1189,7 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
             if ((rights & (PRSFS_WRITE|PRSFS_DELETE)) && (scp->flags & CM_SCACHEFLAG_RO))
                 return CM_ERROR_READONLY;
 
-            if (cm_HaveAccessRights(scp, userp, rights, &outRights)) {
+            if (cm_HaveAccessRights(scp, userp, reqp, rights, &outRights)) {
                 if (~outRights & rights)
                    return CM_ERROR_NOACCESS;
             }
@@ -1386,7 +1290,7 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
         osi_SetQData(qdp, bufp);
 
         buf_Hold(bufp);
-        bufp->cmFlags |= CM_BUF_CMFETCHING;
+        _InterlockedOr(&bufp->cmFlags, CM_BUF_CMFETCHING);
         osi_QAdd((osi_queue_t **) &scp->bufReadsp, &qdp->q);
     }
 
@@ -1404,13 +1308,13 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
         qdp = osi_QDAlloc();
         osi_SetQData(qdp, bufp);
         buf_Hold(bufp);
-        bufp->cmFlags |= CM_BUF_CMSTORING;
+        _InterlockedOr(&bufp->cmFlags, CM_BUF_CMSTORING);
         osi_QAdd((osi_queue_t **) &scp->bufWritesp, &qdp->q);
     }
 
     if (bufp && (flags & CM_SCACHESYNC_WRITE)) {
         /* mark the buffer as being written to. */
-        bufp->cmFlags |= CM_BUF_CMWRITING;
+        _InterlockedOr(&bufp->cmFlags, CM_BUF_CMWRITING);
     }
 
     return 0;
@@ -1459,7 +1363,7 @@ void cm_SyncOpDone(cm_scache_t *scp, cm_buf_t *bufp, afs_uint32 flags)
            osi_QDFree(qdp);
            release = 1;
        }
-        bufp->cmFlags &= ~(CM_BUF_CMFETCHING | CM_BUF_CMFULLYFETCHED);
+        _InterlockedAnd(&bufp->cmFlags, ~(CM_BUF_CMFETCHING | CM_BUF_CMFULLYFETCHED));
         if (bufp->flags & CM_BUF_WAITING) {
             osi_Log2(afsd_logp, "CM SyncOpDone FetchData Waking [scp 0x%p] bufp 0x%p", scp, bufp);
             osi_Wakeup((LONG_PTR) &bufp);
@@ -1482,7 +1386,7 @@ void cm_SyncOpDone(cm_scache_t *scp, cm_buf_t *bufp, afs_uint32 flags)
            osi_QDFree(qdp);
            release = 1;
        }
-        bufp->cmFlags &= ~CM_BUF_CMSTORING;
+        _InterlockedAnd(&bufp->cmFlags, ~CM_BUF_CMSTORING);
         if (bufp->flags & CM_BUF_WAITING) {
             osi_Log2(afsd_logp, "CM SyncOpDone StoreData Waking [scp 0x%p] bufp 0x%p", scp, bufp);
             osi_Wakeup((LONG_PTR) &bufp);
@@ -1493,7 +1397,7 @@ void cm_SyncOpDone(cm_scache_t *scp, cm_buf_t *bufp, afs_uint32 flags)
 
     if (bufp && (flags & CM_SCACHESYNC_WRITE)) {
         osi_assertx(bufp->cmFlags & CM_BUF_CMWRITING, "!CM_BUF_CMWRITING");
-        bufp->cmFlags &= ~CM_BUF_CMWRITING;
+        _InterlockedAnd(&bufp->cmFlags, ~CM_BUF_CMWRITING);
     }
 
     /* and wakeup anyone who is waiting */
@@ -1525,6 +1429,8 @@ void cm_MergeStatus(cm_scache_t *dscp,
     struct cm_volume *volp = NULL;
     struct cm_cell *cellp = NULL;
 
+    lock_AssertWrite(&scp->rw);
+
     // yj: i want to create some fake status for the /afs directory and the
     // entries under that directory
 #ifdef AFS_FREELANCE_CLIENT
@@ -1681,7 +1587,9 @@ void cm_MergeStatus(cm_scache_t *dscp,
     /* and other stuff */
     scp->parentVnode = statusp->ParentVnode;
     scp->parentUnique = statusp->ParentUnique;
-    scp->fsLockCount = statusp->lockCount;
+
+    /* -1 is a write lock; any positive values are read locks */
+    scp->fsLockCount = (afs_int32)statusp->lockCount;
 
     /* and merge in the private acl cache info, if this is more than the public
      * info; merge in the public stuff in any case.
@@ -1738,15 +1646,18 @@ void cm_MergeStatus(cm_scache_t *dscp,
 
                     j = BUF_HASH(&bp->fid, &bp->offset);
                     lbpp = &(cm_data.buf_scacheHashTablepp[j]);
-                    for(tbp = *lbpp; tbp; lbpp = &tbp->hashp, tbp = *lbpp) {
+                    for(tbp = *lbpp; tbp; lbpp = &tbp->hashp, tbp = tbp->hashp) {
                         if (tbp == bp)
                             break;
                     }
 
+                    /* we better find it */
+                    osi_assertx(tbp != NULL, "cm_MergeStatus: buf_scacheHashTablepp table screwup");
+
                     *lbpp = bp->hashp; /* hash out */
                     bp->hashp = NULL;
 
-                    bp->qFlags &= ~CM_BUF_QINHASH;
+                    _InterlockedAnd(&bp->qFlags, ~CM_BUF_QINHASH);
                 }
                 lock_ReleaseMutex(&bp->mx);
             }