Windows: Protect against null volp dereference
[openafs.git] / src / WINNT / afsd / cm_scache.c
index 910bd8d..b8034fa 100644 (file)
@@ -151,7 +151,6 @@ long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
        return -1;
     }
 
-
     if (scp->flags & CM_SCACHEFLAG_SMB_FID) {
        osi_Log1(afsd_logp,"cm_RecycleSCache CM_SCACHEFLAG_SMB_FID detected scp 0x%p", scp);
 #ifdef DEBUG
@@ -170,6 +169,18 @@ long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
 
     cm_RemoveSCacheFromHashTable(scp);
 
+    if (scp->fileType == CM_SCACHETYPE_DIRECTORY &&
+         !cm_accessPerFileCheck) {
+        cm_volume_t *volp = cm_GetVolumeByFID(&scp->fid);
+
+        if (volp) {
+            if (!(volp->flags & CM_VOLUMEFLAG_DFS_VOLUME))
+                cm_EAccesClearParentEntries(&fid);
+
+            cm_PutVolume(volp);
+        }
+    }
+
     /* invalidate so next merge works fine;
      * also initialize some flags */
     scp->fileType = 0;
@@ -179,8 +190,7 @@ long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
                     | CM_SCACHEFLAG_RO
                     | CM_SCACHEFLAG_PURERO
                     | CM_SCACHEFLAG_OVERQUOTA
-                    | CM_SCACHEFLAG_OUTOFSPACE
-                    | CM_SCACHEFLAG_EACCESS));
+                    | CM_SCACHEFLAG_OUTOFSPACE));
     scp->serverModTime = 0;
     scp->dataVersion = CM_SCACHE_VERSION_BAD;
     scp->bufDataVersionLow = CM_SCACHE_VERSION_BAD;
@@ -193,6 +203,7 @@ long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
         scp->cbServerp = NULL;
     }
     scp->cbExpires = 0;
+    scp->cbIssued = 0;
     scp->volumeCreationDate = 0;
 
     scp->fid.vnode = 0;
@@ -309,22 +320,21 @@ cm_GetNewSCache(afs_uint32 locked)
                     if (!buf_dirty && !buf_rdr) {
                         cm_fid_t   fid;
                         afs_uint32 fileType;
+                        int        success;
 
-                        if (!lock_TryWrite(&scp->rw)) {
-                            lock_ObtainWrite(&cm_scacheLock);
-                            if (scp_prev != (cm_scache_t *) osi_QPrev(&scp->q) &&
-                                scp_next != (cm_scache_t *) osi_QNext(&scp->q))
-                                break;
-                            else
-                                continue;
-                        }
+                        success = lock_TryWrite(&scp->rw);
 
                         lock_ObtainWrite(&cm_scacheLock);
                         if (scp_prev != (cm_scache_t *) osi_QPrev(&scp->q) &&
                             scp_next != (cm_scache_t *) osi_QNext(&scp->q))
                         {
-                            lock_ReleaseWrite(&scp->rw);
+                            osi_Log1(afsd_logp, "GetNewSCache scp 0x%p; LRU order changed", scp);
+                            if (success)
+                                lock_ReleaseWrite(&scp->rw);
                             break;
+                        } else if (!success) {
+                                osi_Log1(afsd_logp, "GetNewSCache failed to obtain lock scp 0x%p", scp);
+                                continue;
                         }
 
                         /* Found a likely candidate.  Save type and fid in case we succeed */
@@ -338,29 +348,42 @@ cm_GetNewSCache(afs_uint32 locked)
                              */
                             cm_AdjustScacheLRU(scp);
 
-                            /* and we're done */
+                            /* and we're done - SUCCESS */
                             osi_assertx(!(scp->flags & CM_SCACHEFLAG_INHASH), "CM_SCACHEFLAG_INHASH set");
                             goto done;
                         }
                         lock_ReleaseWrite(&scp->rw);
-                    } else if (!buf_rdr) {
-                        osi_Log1(afsd_logp, "GetNewSCache dirty buffers scp 0x%p", scp);
-                        lock_ObtainWrite(&cm_scacheLock);
-                        if (scp_prev != (cm_scache_t *) osi_QPrev(&scp->q) &&
-                            scp_next != (cm_scache_t *) osi_QNext(&scp->q))
-                            break;
                     } else {
-                        osi_Log1(afsd_logp,"GetNewSCache redirector is holding extents scp 0x%p", scp);
+                        if (buf_rdr)
+                            osi_Log1(afsd_logp,"GetNewSCache redirector is holding extents scp 0x%p", scp);
+                        else
+                            osi_Log1(afsd_logp, "GetNewSCache dirty buffers scp 0x%p", scp);
+
                         lock_ObtainWrite(&cm_scacheLock);
                         if (scp_prev != (cm_scache_t *) osi_QPrev(&scp->q) &&
                             scp_next != (cm_scache_t *) osi_QNext(&scp->q))
+                        {
+                            osi_Log1(afsd_logp, "GetNewSCache scp 0x%p; LRU order changed", scp);
                             break;
+                        }
                     }
                 }
             } /* for */
 
             osi_Log2(afsd_logp, "GetNewSCache all scache entries in use (attempt = %d, count = %u)", attempt, count);
+            if (scp == NULL) {
+                /*
+                * The entire LRU queue was walked and no available cm_scache_t was
+                * found.  Drop the cm_scacheLock and sleep for a moment to give a
+                * chance for cm_scache_t objects to be released.
+                */
+                lock_ReleaseWrite(&cm_scacheLock);
+                Sleep(50);
+                lock_ObtainWrite(&cm_scacheLock);
+            }
         }
+        /* FAILURE */
+        scp = NULL;
         goto done;
     }
 
@@ -380,6 +403,9 @@ cm_GetNewSCache(afs_uint32 locked)
 #endif
     lock_InitializeMutex(&scp->redirMx, "cm_scache_t redirMx", LOCK_HIERARCHY_SCACHE_REDIRMX);
     scp->serverLock = -1;
+    scp->dataVersion = CM_SCACHE_VERSION_BAD;
+    scp->bufDataVersionLow = CM_SCACHE_VERSION_BAD;
+    scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
 
     /* and put it in the LRU queue */
     osi_QAddH((osi_queue_t **) &cm_data.scacheLRUFirstp, (osi_queue_t **)&cm_data.scacheLRULastp, &scp->q);
@@ -428,6 +454,7 @@ void cm_fakeSCacheInit(int newFile)
         cm_data.fakeSCache.magic = CM_SCACHE_MAGIC;
         cm_data.fakeSCache.cbServerp = (struct cm_server *)(-1);
         cm_data.fakeSCache.cbExpires = (time_t)-1;
+        cm_data.fakeSCache.cbExpires = time(NULL);
         /* can leave clientModTime at 0 */
         cm_data.fakeSCache.fileType = CM_SCACHETYPE_FILE;
         cm_data.fakeSCache.unixModeBits = 0777;
@@ -610,6 +637,7 @@ cm_ShutdownSCache(void)
             scp->cbServerp = NULL;
         }
         scp->cbExpires = 0;
+        scp->cbIssued = 0;
         _InterlockedAnd(&scp->flags, ~CM_SCACHEFLAG_CALLBACK);
         lock_ReleaseWrite(&scp->rw);
 
@@ -654,6 +682,7 @@ void cm_InitSCache(int newFile, long maxSCaches)
 #endif
                 scp->cbServerp = NULL;
                 scp->cbExpires = 0;
+                scp->cbIssued = 0;
                 scp->volumeCreationDate = 0;
                 scp->fileLocksH = NULL;
                 scp->fileLocksT = NULL;
@@ -717,10 +746,10 @@ cm_scache_t *cm_FindSCache(cm_fid_t *fidp)
 }
 
 #ifdef DEBUG_REFCOUNT
-long cm_GetSCacheDbg(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
+long cm_GetSCacheDbg(cm_fid_t *fidp, cm_fid_t *parentFidp, cm_scache_t **outScpp, cm_user_t *userp,
                   cm_req_t *reqp, char * file, long line)
 #else
-long cm_GetSCache(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
+long cm_GetSCache(cm_fid_t *fidp, cm_fid_t *parentFidp, cm_scache_t **outScpp, cm_user_t *userp,
                   cm_req_t *reqp)
 #endif
 {
@@ -763,6 +792,10 @@ long cm_GetSCache(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
                 cm_data.fakeDirVersion != scp->dataVersion)
                 break;
 #endif
+            if (parentFidp && scp->parentVnode == 0) {
+                scp->parentVnode = parentFidp->vnode;
+                scp->parentUnique = parentFidp->unique;
+            }
             cm_HoldSCacheNoLock(scp);
             *outScpp = scp;
             lock_ConvertRToW(&cm_scacheLock);
@@ -808,10 +841,11 @@ long cm_GetSCache(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
             lock_ObtainWrite(&scp->rw);
         }
         scp->fid = *fidp;
-        scp->dotdotFid.cell=AFS_FAKE_ROOT_CELL_ID;
-        scp->dotdotFid.volume=AFS_FAKE_ROOT_VOL_ID;
-        scp->dotdotFid.unique=1;
-        scp->dotdotFid.vnode=1;
+        cm_SetFid(&scp->dotdotFid,AFS_FAKE_ROOT_CELL_ID,AFS_FAKE_ROOT_VOL_ID,1,1);
+        if (parentFidp) {
+            scp->parentVnode = parentFidp->vnode;
+            scp->parentUnique = parentFidp->unique;
+        }
         _InterlockedOr(&scp->flags, (CM_SCACHEFLAG_PURERO | CM_SCACHEFLAG_RO));
         lock_ObtainWrite(&cm_scacheLock);
         if (!(scp->flags & CM_SCACHEFLAG_INHASH)) {
@@ -900,6 +934,10 @@ long cm_GetSCache(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
            afsi_log("%s:%d cm_GetSCache (3) scp 0x%p ref %d", file, line, scp, scp->refCount);
            osi_Log1(afsd_logp,"cm_GetSCache (3) scp 0x%p", scp);
 #endif
+            if (parentFidp && scp->parentVnode == 0) {
+                scp->parentVnode = parentFidp->vnode;
+                scp->parentUnique = parentFidp->unique;
+            }
             if (volp)
                 cm_PutVolume(volp);
             cm_HoldSCacheNoLock(scp);
@@ -920,7 +958,7 @@ long cm_GetSCache(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
     scp->fid = *fidp;
     if (!cm_freelanceEnabled || !isRoot) {
         /* if this scache entry represents a volume root then we need
-         * to copy the dotdotFipd from the volume structure where the
+         * to copy the dotdotFid from the volume structure where the
          * "master" copy is stored (defect 11489)
          */
         if (volp->vol[ROVOL].ID == fidp->volume) {
@@ -936,6 +974,10 @@ long cm_GetSCache(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
                 scp->dotdotFid = cm_VolumeStateByType(volp, RWVOL)->dotdotFid;
         }
     }
+    if (parentFidp) {
+        scp->parentVnode = parentFidp->vnode;
+        scp->parentUnique = parentFidp->unique;
+    }
     if (volp)
         cm_PutVolume(volp);
 
@@ -975,6 +1017,9 @@ cm_scache_t * cm_FindSCacheParent(cm_scache_t * scp)
     cm_fid_t    parent_fid;
     cm_scache_t * pscp = NULL;
 
+    if (scp->parentVnode == 0)
+        return NULL;
+
     lock_ObtainWrite(&cm_scacheLock);
     cm_SetFid(&parent_fid, scp->fid.cell, scp->fid.volume, scp->parentVnode, scp->parentUnique);
 
@@ -1299,6 +1344,10 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
             if ((flags & CM_SCACHESYNC_FORCECB) || !cm_HaveCallback(scp)) {
                 osi_Log1(afsd_logp, "CM SyncOp getting callback on scp 0x%p",
                           scp);
+
+                if (cm_EAccesFindEntry(userp, &scp->fid))
+                    return CM_ERROR_NOACCESS;
+
                 if (bufLocked)
                    lock_ReleaseMutex(&bufp->mx);
                 code = cm_GetCallback(scp, userp, reqp, (flags & CM_SCACHESYNC_FORCECB)?1:0);
@@ -1551,6 +1600,15 @@ void cm_SyncOpDone(cm_scache_t *scp, cm_buf_t *bufp, afs_uint32 flags)
     }
 }
 
+static afs_uint32
+dv_diff(afs_uint64 dv1, afs_uint64 dv2)
+{
+    if ( dv1 - dv2 > 0x7FFFFFFF )
+        return (afs_uint32)(dv2 - dv1);
+    else
+        return (afs_uint32)(dv1 - dv2);
+}
+
 /* merge in a response from an RPC.  The scp must be locked, and the callback
  * is optional.
  *
@@ -1621,7 +1679,7 @@ void cm_MergeStatus(cm_scache_t *dscp,
         case UAEACCES:
         case EPERM:
         case UAEPERM:
-            _InterlockedOr(&scp->flags, CM_SCACHEFLAG_EACCESS);
+            cm_EAccesAddEntry(userp, &scp->fid, &dscp->fid);
         }
         osi_Log2(afsd_logp, "Merge, Failure scp 0x%p code 0x%x", scp, statusp->errorCode);
 
@@ -1645,7 +1703,7 @@ void cm_MergeStatus(cm_scache_t *dscp,
         scp->bufDataVersionLow = CM_SCACHE_VERSION_BAD;
         scp->fsLockCount = 0;
 
-       if (dscp) {
+       if (dscp && dscp != scp) {
             scp->parentVnode = dscp->fid.vnode;
             scp->parentUnique = dscp->fid.unique;
        } else {
@@ -1655,8 +1713,6 @@ void cm_MergeStatus(cm_scache_t *dscp,
 
         if (RDR_Initialized)
             rdr_invalidate = 1;
-    } else {
-       _InterlockedAnd(&scp->flags, ~CM_SCACHEFLAG_EACCESS);
     }
 
     dataVersion = statusp->dataVersionHigh;
@@ -1675,6 +1731,7 @@ void cm_MergeStatus(cm_scache_t *dscp,
                       scp->cbServerp->addr.sin_addr.s_addr,
                       volp ? volp->namep : "(unknown)");
         }
+
         osi_Log3(afsd_logp, "Bad merge, scp 0x%p, scp dv %d, RPC dv %d",
                   scp, scp->dataVersion, dataVersion);
         /* we have a number of data fetch/store operations running
@@ -1759,9 +1816,10 @@ void cm_MergeStatus(cm_scache_t *dscp,
         cm_AddACLCache(scp, userp, statusp->CallerAccess);
     }
 
-    if (dataVersion != 0 &&
-        (!(flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) && dataVersion != scp->dataVersion ||
-         (flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) && dataVersion - scp->dataVersion > activeRPCs)) {
+    if (dataVersion != 0 && scp->dataVersion != CM_SCACHE_VERSION_BAD &&
+        (!(flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) && (dataVersion != scp->dataVersion) ||
+         (flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) &&
+         (dv_diff(dataVersion, scp->dataVersion) > activeRPCs))) {
         /*
          * We now know that all of the data buffers that we have associated
          * with this scp are invalid.  Subsequent operations will go faster
@@ -1849,10 +1907,13 @@ void cm_MergeStatus(cm_scache_t *dscp,
      * object during an uncontested storeData operation.  As a result this
      * merge status no longer has performance characteristics derived from
      * the size of the file.
+     *
+     * For directory buffers, only current dataVersion values are up to date.
      */
-    if (((flags & CM_MERGEFLAG_STOREDATA) && dataVersion - scp->dataVersion > activeRPCs) ||
-         (!(flags & CM_MERGEFLAG_STOREDATA) && scp->dataVersion != dataVersion) ||
-         scp->bufDataVersionLow == 0)
+    if (((flags & (CM_MERGEFLAG_STOREDATA|CM_MERGEFLAG_DIROP)) && (dv_diff(dataVersion, scp->dataVersion) > activeRPCs)) ||
+         (!(flags & (CM_MERGEFLAG_STOREDATA|CM_MERGEFLAG_DIROP)) && (scp->dataVersion != dataVersion)) ||
+         scp->bufDataVersionLow == CM_SCACHE_VERSION_BAD ||
+         scp->fileType == CM_SCACHETYPE_DIRECTORY)
         scp->bufDataVersionLow = dataVersion;
 
     if (RDR_Initialized) {
@@ -1878,10 +1939,10 @@ void cm_MergeStatus(cm_scache_t *dscp,
             rdr_invalidate = 1;
         } else if (reqp->flags & CM_REQ_SOURCE_REDIR) {
             if (!(flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) &&
-                (dataVersion - scp->dataVersion > activeRPCs - 1)) {
+                (dv_diff(dataVersion, scp->dataVersion) > activeRPCs - 1)) {
                 rdr_invalidate = 1;
             } else if ((flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) &&
-                       dataVersion - scp->dataVersion > activeRPCs) {
+                       dv_diff(dataVersion, scp->dataVersion) > activeRPCs) {
                 rdr_invalidate = 1;
             }
         }
@@ -1914,6 +1975,14 @@ void cm_MergeStatus(cm_scache_t *dscp,
         }
     }
 
+    /* Remove cached EACCES / EPERM errors if the file is a directory */
+    if (scp->fileType == CM_SCACHETYPE_DIRECTORY &&
+        !(volp && (volp->flags & CM_VOLUMEFLAG_DFS_VOLUME)) &&
+        !cm_accessPerFileCheck)
+    {
+        cm_EAccesClearParentEntries(&scp->fid);
+    }
+
   done:
     if (volp)
         cm_PutVolume(volp);
@@ -1948,6 +2017,7 @@ void cm_DiscardSCache(cm_scache_t *scp)
        scp->cbServerp = NULL;
     }
     scp->cbExpires = 0;
+    scp->cbIssued = 0;
     scp->volumeCreationDate = 0;
     _InterlockedAnd(&scp->flags, ~(CM_SCACHEFLAG_CALLBACK | CM_SCACHEFLAG_LOCAL | CM_SCACHEFLAG_RDR_IN_USE));
     cm_dnlcPurgedp(scp);