Windows: fix indentation
[openafs.git] / src / WINNT / afsd / cm_aclent.c
index bef78b4..c6433e7 100644 (file)
-/* 
- * Copyright (C) 1998, 1989 Transarc Corporation - All rights reserved
- *
- * (C) COPYRIGHT IBM CORPORATION 1987, 1988
- * LICENSED MATERIALS - PROPERTY OF IBM
- *
- *
- */
 /*
- *      Copyright (C) 1994, 1990 Transarc Corporation
- *      All rights reserved.
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
  */
 
+#include <afsconfig.h>
 #include <afs/param.h>
+#include <roken.h>
+
 #include <afs/stds.h>
 
 #include <windows.h>
 #include <stdlib.h>
 #include <string.h>
-#include <malloc.h>
 
 #include "afsd.h"
+#include <osisleep.h>
 
-/* 
+/*
  * This next lock controls access to all cm_aclent structures in the system,
  * in either the free list or in the LRU queue.  A read lock prevents someone
  * from modifying the list(s), and a write lock is required for modifying
  * the list.  The actual data stored in the randomUid and randomAccess fields
- * is actually maintained as up-to-date or not via the scache llock.
+ * is actually maintained as up-to-date or not via the scache lock.
  * An aclent structure is free if it has no back vnode pointer.
  */
 osi_rwlock_t cm_aclLock;               /* lock for system's aclents */
-cm_aclent_t *cm_aclLRUp;               /* LRUQ for dudes in vnodes' lists */
-cm_aclent_t *cm_aclLRUEndp;            /* ditto */
 
-/* 
+/* This must be called with cm_aclLock and the aclp->back->mx held */
+static void CleanupACLEnt(cm_aclent_t * aclp)
+{
+    cm_aclent_t *taclp;
+    cm_aclent_t **laclpp;
+
+    if (aclp->backp) {
+        if (aclp->backp->randomACLp) {
+            /*
+             * Remove the entry from the vnode's list
+             */
+            lock_AssertWrite(&aclp->backp->rw);
+            laclpp = &aclp->backp->randomACLp;
+            for (taclp = *laclpp; taclp; laclpp = &taclp->nextp, taclp = *laclpp) {
+                if (taclp == aclp)
+                    break;
+            }
+            if (!taclp)
+                osi_panic("CleanupACLEnt race", __FILE__, __LINE__);
+            *laclpp = aclp->nextp;                     /* remove from vnode list */
+        }
+        aclp->backp = NULL;
+    }
+
+    /* release the old user */
+    if (aclp->userp) {
+        cm_ReleaseUser(aclp->userp);
+        aclp->userp = NULL;
+    }
+
+    aclp->randomAccess = 0;
+    aclp->tgtLifetime = 0;
+}
+
+/*
  * Get an acl cache entry for a particular user and file, or return that it doesn't exist.
  * Called with the scp locked.
  */
-long cm_FindACLCache(cm_scache_t *scp, cm_user_t *userp, long *rightsp)
+long cm_FindACLCache(cm_scache_t *scp, cm_user_t *userp, afs_uint32 *rightsp)
 {
-       cm_aclent_t *aclp;
-
-       lock_ObtainWrite(&cm_aclLock);
-       for (aclp = scp->randomACLp; aclp; aclp = aclp->nextp) {
-               if (aclp->userp == userp) {
-                       if (aclp->tgtLifetime && aclp->tgtLifetime <= (long) osi_Time()) {
-                               /* ticket expired */
-                               aclp->tgtLifetime = 0;
-                               *rightsp = 0;
-                               break; /* get a new acl from server */
-                       }
-                       else {
-                               *rightsp = aclp->randomAccess;
-                               if (cm_aclLRUEndp == aclp)
-                                       cm_aclLRUEndp = (cm_aclent_t *) osi_QPrev(&aclp->q);
-
-                               /* move to the head of the LRU queue */
-                               osi_QRemove((osi_queue_t **) &cm_aclLRUp, &aclp->q);
-                                osi_QAddH((osi_queue_t **) &cm_aclLRUp,
-                                       (osi_queue_t **) &cm_aclLRUEndp,
-                                        &aclp->q);
-                       }
-                       lock_ReleaseWrite(&cm_aclLock);
-                       return 0;
-               }
-       }
-
-       /* 
-        * If we make it here, this entry isn't present, so we're going to fail. 
-        */
-       lock_ReleaseWrite(&cm_aclLock);
-       return -1;
-}
+    cm_aclent_t *aclp;
+    long retval = -1;
+
+    lock_ObtainWrite(&cm_aclLock);
+    *rightsp = 0;   /* get a new acl from server if we don't find a
+                     * current entry
+                     */
+
+    for (aclp = scp->randomACLp; aclp; aclp = aclp->nextp) {
+        if (aclp->userp == userp) {
+            if (aclp->tgtLifetime && aclp->tgtLifetime <= time(NULL)) {
+                /* ticket expired */
+                osi_QRemoveHT((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
+                CleanupACLEnt(aclp);
 
+                /* move to the tail of the LRU queue */
+                osi_QAddT((osi_queue_t **) &cm_data.aclLRUp,
+                           (osi_queue_t **) &cm_data.aclLRUEndp,
+                           &aclp->q);
+            } else {
+                *rightsp = aclp->randomAccess;
+                if (cm_data.aclLRUp != aclp) {
+                    /* move to the head of the LRU queue */
+                    osi_QRemoveHT((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
+                    osi_QAddH((osi_queue_t **) &cm_data.aclLRUp,
+                              (osi_queue_t **) &cm_data.aclLRUEndp,
+                              &aclp->q);
+                }
+                retval = 0;     /* success */
+            }
+            break;
+        }
+    }
 
-/* 
+    lock_ReleaseWrite(&cm_aclLock);
+    return retval;
+}
+
+/*
  * This function returns a free (not in the LRU queue) acl cache entry.
- * It must be called with the cm_aclLock lock held.
+ * It must be called with the cm_aclLock lock held
  */
-static cm_aclent_t *GetFreeACLEnt(void)
+static cm_aclent_t *GetFreeACLEnt(cm_scache_t * scp)
 {
-       cm_aclent_t *aclp;
-        cm_aclent_t *taclp;
-        cm_aclent_t **laclpp;
-
-       if (cm_aclLRUp == NULL)
-               osi_panic("empty aclent LRU", __FILE__, __LINE__);
-
-       aclp = cm_aclLRUEndp;
-       if (aclp == cm_aclLRUEndp)
-               cm_aclLRUEndp = (cm_aclent_t *) osi_QPrev(&aclp->q);
-       osi_QRemove((osi_queue_t **) &cm_aclLRUp, &aclp->q);
-       if (aclp->backp) {
-               /* 
-                * Remove the entry from the vnode's list 
-                */
-               laclpp = &aclp->backp->randomACLp;
-               for (taclp = *laclpp; taclp; laclpp = &taclp->nextp, taclp = *laclpp) {
-                       if (taclp == aclp) 
-                               break;
-               }
-               if (!taclp) 
-                       osi_panic("GetFreeACLEnt race", __FILE__, __LINE__);
-               *laclpp = aclp->nextp;                  /* remove from vnode list */
-               aclp->backp = NULL;
-       }
-       
-        /* release the old user */
-        if (aclp->userp) {
-               cm_ReleaseUser(aclp->userp);
-                aclp->userp = NULL;
-       }
-       return aclp;
+    cm_aclent_t *aclp;
+    cm_scache_t *ascp = 0;
+
+    if (cm_data.aclLRUp == NULL)
+        osi_panic("empty aclent LRU", __FILE__, __LINE__);
+
+    if (cm_data.aclLRUEndp == NULL)
+        osi_panic("inconsistent aclent LRUEndp == NULL", __FILE__, __LINE__);
+
+    aclp = cm_data.aclLRUEndp;
+    osi_QRemoveHT((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
+
+    if (aclp->backp && scp != aclp->backp) {
+        ascp = aclp->backp;
+        lock_ReleaseWrite(&cm_aclLock);
+        lock_ObtainWrite(&ascp->rw);
+        lock_ObtainWrite(&cm_aclLock);
+    }
+    CleanupACLEnt(aclp);
+
+    if (ascp)
+        lock_ReleaseWrite(&ascp->rw);
+    return aclp;
+}
+
+time_t cm_TGTLifeTime(cm_user_t *userp, afs_uint32 cellID)
+{
+    cm_cell_t *cellp = NULL;
+    cm_ucell_t * ucp = NULL;
+    time_t      expirationTime = 0;
+
+    cellp = cm_FindCellByID(cellID, CM_FLAG_NOPROBE);
+    lock_ObtainMutex(&userp->mx);
+    ucp = cm_GetUCell(userp, cellp);
+    if (ucp->ticketp)
+        expirationTime = ucp->expirationTime;
+    lock_ReleaseMutex(&userp->mx);
+
+    return expirationTime;
 }
 
 
-/* 
- * Add rights to an acl cache entry.  Do the right thing if not present, 
+/*
+ * Add rights to an acl cache entry.  Do the right thing if not present,
  * including digging up an entry from the LRU queue.
  *
  * The scp must be locked when this function is called.
  */
-long cm_AddACLCache(cm_scache_t *scp, cm_user_t *userp, long rights)
+long cm_AddACLCache(cm_scache_t *scp, cm_user_t *userp, afs_uint32 rights)
+{
+    struct cm_aclent *aclp;
+
+    lock_ObtainWrite(&cm_aclLock);
+    for (aclp = scp->randomACLp; aclp; aclp = aclp->nextp) {
+        if (aclp->userp == userp) {
+            aclp->randomAccess = rights;
+            if (aclp->tgtLifetime == 0)
+                aclp->tgtLifetime = cm_TGTLifeTime(userp, scp->fid.cell);
+            if (cm_data.aclLRUp != aclp) {
+                /* move to the head of the LRU queue */
+                osi_QRemoveHT((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
+                osi_QAddH((osi_queue_t **) &cm_data.aclLRUp,
+                           (osi_queue_t **) &cm_data.aclLRUEndp,
+                           &aclp->q);
+            }
+            lock_ReleaseWrite(&cm_aclLock);
+            return 0;
+        }
+    }
+
+    /*
+     * Didn't find the dude we're looking for, so take someone from the LRUQ
+     * and  reuse. But first try the free list and see if there's already
+     * someone there.
+     */
+    aclp = GetFreeACLEnt(scp);          /* can't fail, panics instead */
+    osi_QAddH((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
+    aclp->backp = scp;
+    aclp->nextp = scp->randomACLp;
+    scp->randomACLp = aclp;
+    cm_HoldUser(userp);
+    aclp->userp = userp;
+    aclp->randomAccess = rights;
+    aclp->tgtLifetime = cm_TGTLifeTime(userp, scp->fid.cell);
+    lock_ReleaseWrite(&cm_aclLock);
+
+    return 0;
+}
+
+long cm_ShutdownACLCache(void)
 {
-       register struct cm_aclent *aclp;
-
-       lock_ObtainWrite(&cm_aclLock);
-       for (aclp = scp->randomACLp; aclp; aclp = aclp->nextp) {
-               if (aclp->userp == userp) {
-                       aclp->randomAccess = rights;
-                       if (aclp->tgtLifetime == 0) 
-                               aclp->tgtLifetime = cm_TGTLifeTime(pag);
-                       lock_ReleaseWrite(&cm_aclLock);
-                       return 0;
-               }
-       }
-
-       /* 
-        * Didn't find the dude we're looking for, so take someone from the LRUQ 
-        * and  reuse. But first try the free list and see if there's already 
-        * someone there.
-        */
-       aclp = GetFreeACLEnt();          /* can't fail, panics instead */
-       osi_QAddH((osi_queue_t **) &cm_aclLRUp, (osi_queue_t **) &cm_aclLRUEndp, &aclp->q);
-       aclp->backp = scp;
-       aclp->nextp = scp->randomACLp;
-       scp->randomACLp = aclp;
-       aclp->userp = userp;
-        cm_HoldUser(userp);
-       aclp->randomAccess = rights;
-       aclp->tgtLifetime = cm_TGTLifeTime(userp);
-       lock_ReleaseWrite(&cm_aclLock);
-
-       return 0;
+    return 0;
 }
 
-/* 
+long cm_ValidateACLCache(void)
+{
+    long size = cm_data.stats * 2;
+    long count;
+    cm_aclent_t * aclp;
+
+    if ( cm_data.aclLRUp == NULL && cm_data.aclLRUEndp != NULL ||
+         cm_data.aclLRUp != NULL && cm_data.aclLRUEndp == NULL) {
+        afsi_log("cm_ValidateACLCache failure: inconsistent LRU pointers");
+        fprintf(stderr, "cm_ValidateACLCache failure: inconsistent LRU pointers\n");
+        return -9;
+    }
+
+    for ( aclp = cm_data.aclLRUp, count = 0; aclp;
+          aclp = (cm_aclent_t *) osi_QNext(&aclp->q), count++ ) {
+        if (aclp->magic != CM_ACLENT_MAGIC) {
+            afsi_log("cm_ValidateACLCache failure: acpl->magic != CM_ACLENT_MAGIC");
+            fprintf(stderr, "cm_ValidateACLCache failure: acpl->magic != CM_ACLENT_MAGIC\n");
+            return -1;
+        }
+        if (aclp->nextp && aclp->nextp->magic != CM_ACLENT_MAGIC) {
+            afsi_log("cm_ValidateACLCache failure: acpl->nextp->magic != CM_ACLENT_MAGIC");
+            fprintf(stderr,"cm_ValidateACLCache failure: acpl->nextp->magic != CM_ACLENT_MAGIC\n");
+            return -2;
+        }
+        if (aclp->backp && aclp->backp->magic != CM_SCACHE_MAGIC) {
+            afsi_log("cm_ValidateACLCache failure: acpl->backp->magic != CM_SCACHE_MAGIC");
+            fprintf(stderr,"cm_ValidateACLCache failure: acpl->backp->magic != CM_SCACHE_MAGIC\n");
+            return -3;
+        }
+        if (count != 0 && aclp == cm_data.aclLRUp || count > size) {
+            afsi_log("cm_ValidateACLCache failure: loop in cm_data.aclLRUp list");
+            fprintf(stderr, "cm_ValidateACLCache failure: loop in cm_data.aclLRUp list\n");
+            return -4;
+        }
+    }
+
+    for ( aclp = cm_data.aclLRUEndp, count = 0; aclp;
+          aclp = (cm_aclent_t *) osi_QPrev(&aclp->q), count++ ) {
+        if (aclp->magic != CM_ACLENT_MAGIC) {
+            afsi_log("cm_ValidateACLCache failure: aclp->magic != CM_ACLENT_MAGIC");
+            fprintf(stderr, "cm_ValidateACLCache failure: aclp->magic != CM_ACLENT_MAGIC\n");
+            return -5;
+        }
+        if (aclp->nextp && aclp->nextp->magic != CM_ACLENT_MAGIC) {
+            afsi_log("cm_ValidateACLCache failure: aclp->nextp->magic != CM_ACLENT_MAGIC");
+            fprintf(stderr, "cm_ValidateACLCache failure: aclp->nextp->magic != CM_ACLENT_MAGIC\n");
+            return -6;
+        }
+        if (aclp->backp && aclp->backp->magic != CM_SCACHE_MAGIC) {
+            afsi_log("cm_ValidateACLCache failure: aclp->backp->magic != CM_SCACHE_MAGIC");
+            fprintf(stderr, "cm_ValidateACLCache failure: aclp->backp->magic != CM_SCACHE_MAGIC\n");
+            return -7;
+        }
+
+        if (count != 0 && aclp == cm_data.aclLRUEndp || count > size) {
+            afsi_log("cm_ValidateACLCache failure: loop in cm_data.aclLRUEndp list");
+            fprintf(stderr, "cm_ValidateACLCache failure: loop in cm_data.aclLRUEndp list\n");
+            return -8;
+        }
+    }
+
+    return 0;
+}
+
+/*
  * Initialize the cache to have an entries.  Called during system startup.
  */
-long cm_InitACLCache(long size)
+long cm_InitACLCache(int newFile, long size)
 {
-       cm_aclent_t *aclp;
-       long i;
-       static osi_once_t once;
-
-       
-       if (osi_Once(&once)) {
-               lock_InitializeRWLock(&cm_aclLock, "cm_aclLock");
-               osi_EndOnce(&once);
-       }
-
-       lock_ObtainWrite(&cm_aclLock);
-       cm_aclLRUp = cm_aclLRUEndp = NULL;
-       aclp = (cm_aclent_t *) malloc(size * sizeof(cm_aclent_t));
-       memset(aclp, 0, size * sizeof(cm_aclent_t));
-
-       /* 
-        * Put all of these guys on the LRU queue 
-        */
-       for (i = 0; i < size; i++) {
-               osi_QAddH((osi_queue_t **) &cm_aclLRUp, (osi_queue_t **) &cm_aclLRUEndp,
-                       &aclp->q);
-               aclp++;
-       }
-
-       lock_ReleaseWrite(&cm_aclLock);
-       return 0;
+    cm_aclent_t *aclp;
+    long i;
+    static osi_once_t once;
+
+    if (osi_Once(&once)) {
+        lock_InitializeRWLock(&cm_aclLock, "cm_aclLock", LOCK_HIERARCHY_ACL_GLOBAL);
+        osi_EndOnce(&once);
+    }
+
+    lock_ObtainWrite(&cm_aclLock);
+    if ( newFile ) {
+        cm_data.aclLRUp = cm_data.aclLRUEndp = NULL;
+        aclp = (cm_aclent_t *) cm_data.aclBaseAddress;
+        memset(aclp, 0, size * sizeof(cm_aclent_t));
+
+        /*
+         * Put all of these guys on the LRU queue
+         */
+        for (i = 0; i < size; i++) {
+            aclp->magic = CM_ACLENT_MAGIC;
+            osi_QAddH((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
+            aclp++;
+        }
+    } else {
+        aclp = (cm_aclent_t *) cm_data.aclBaseAddress;
+        for (i = 0; i < size; i++) {
+            aclp->userp = NULL;
+            aclp->tgtLifetime = 0;
+            aclp++;
+        }
+    }
+    lock_ReleaseWrite(&cm_aclLock);
+    return 0;
 }
 
 
-/* 
+/*
  * Free all associated acl entries.  We actually just clear the back pointer
  * since the acl entries are already in the free list.  The scp must be locked
  * or completely unreferenced (such as when called while recycling the scp).
  */
 void cm_FreeAllACLEnts(cm_scache_t *scp)
 {
-       cm_aclent_t *aclp;
-       cm_aclent_t *taclp;
-
-       lock_ObtainWrite(&cm_aclLock);
-       for (aclp = scp->randomACLp; aclp; aclp = taclp) {
-               taclp = aclp->nextp;
-               if (aclp->userp) {
-                       cm_ReleaseUser(aclp->userp);
-                       aclp->userp = NULL;
-               }
-               aclp->backp = (struct cm_scache *) 0;
-       }
-
-       scp->randomACLp = (struct cm_aclent *) 0;
-       scp->anyAccess = 0;             /* reset this, too */
-       lock_ReleaseWrite(&cm_aclLock);
+    cm_aclent_t *aclp;
+    cm_aclent_t *taclp;
+
+    lock_ObtainWrite(&cm_aclLock);
+    for (aclp = scp->randomACLp; aclp; aclp = taclp) {
+        taclp = aclp->nextp;
+        if (aclp->userp) {
+            cm_ReleaseUser(aclp->userp);
+            aclp->userp = NULL;
+        }
+        aclp->backp = (struct cm_scache *) 0;
+    }
+
+    scp->randomACLp = (struct cm_aclent *) 0;
+    scp->anyAccess = 0;                /* reset this, too */
+    lock_ReleaseWrite(&cm_aclLock);
 }
 
 
-/* 
+/*
  * Invalidate all ACL entries for particular user on this particular vnode.
  *
- * The scp must be locked.
+ * The scp must not be locked.
  */
 void cm_InvalidateACLUser(cm_scache_t *scp, cm_user_t *userp)
 {
-       cm_aclent_t *aclp;
-        cm_aclent_t **laclpp;
-
-       lock_ObtainWrite(&cm_aclLock);
-       laclpp = &scp->randomACLp;
-       for (aclp = *laclpp; aclp; laclpp = &aclp->nextp, aclp = *laclpp) {
-               if (userp == aclp->userp) {     /* One for a given user/scache */
-                       *laclpp = aclp->nextp;
-                        cm_ReleaseUser(aclp->userp);
-                        aclp->userp = NULL;
-                       aclp->backp = (struct cm_scache *) 0;
-                       break;
-               }
-       }
-       lock_ReleaseWrite(&cm_aclLock);
+    cm_aclent_t *aclp;
+    cm_aclent_t **laclpp;
+    int found = 0;
+    int callback = 0;
+
+    lock_ObtainWrite(&scp->rw);
+    lock_ObtainWrite(&cm_aclLock);
+    laclpp = &scp->randomACLp;
+    for (aclp = *laclpp; aclp; laclpp = &aclp->nextp, aclp = *laclpp) {
+        if (userp == aclp->userp) {    /* One for a given user/scache */
+            *laclpp = aclp->nextp;
+            cm_ReleaseUser(aclp->userp);
+            aclp->userp = NULL;
+            aclp->backp = (struct cm_scache *) 0;
+            found = 1;
+            break;
+        }
+    }
+    lock_ReleaseWrite(&cm_aclLock);
+    if (found)
+        callback = cm_HaveCallback(scp);
+    lock_ReleaseWrite(&scp->rw);
+
+    if (found && callback && RDR_Initialized)
+        RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                             scp->fid.hash, scp->fileType, AFS_INVALIDATE_CREDS);
+}
+
+/*
+ * Invalidate ACL info for a user that has just        obtained or lost tokens.
+ */
+void
+cm_ResetACLCache(cm_cell_t *cellp, cm_user_t *userp)
+{
+    cm_volume_t *volp, *nextVolp;
+    cm_scache_t *scp, *nextScp;
+    afs_uint32 hash;
+
+    lock_ObtainRead(&cm_scacheLock);
+    for (hash=0; hash < cm_data.scacheHashTableSize; hash++) {
+        for (scp=cm_data.scacheHashTablep[hash]; scp; scp=nextScp) {
+            nextScp = scp->nextp;
+            if (cellp == NULL ||
+                scp->fid.cell == cellp->cellID) {
+                cm_HoldSCacheNoLock(scp);
+                lock_ReleaseRead(&cm_scacheLock);
+                cm_InvalidateACLUser(scp, userp);
+                lock_ObtainRead(&cm_scacheLock);
+                cm_ReleaseSCacheNoLock(scp);
+            }
+        }
+    }
+    lock_ReleaseRead(&cm_scacheLock);
+
+    if (RDR_Initialized) {
+        lock_ObtainRead(&cm_volumeLock);
+        for (hash = 0; hash < cm_data.volumeHashTableSize; hash++) {
+            for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = nextVolp) {
+                nextVolp = volp->vol[RWVOL].nextp;
+                if ((cellp == NULL || cellp->cellID == volp->cellp->cellID) &&
+                    volp->vol[RWVOL].ID) {
+                    lock_ReleaseRead(&cm_volumeLock);
+                    RDR_InvalidateVolume(volp->cellp->cellID, volp->vol[RWVOL].ID, AFS_INVALIDATE_CREDS);
+                    lock_ObtainRead(&cm_volumeLock);
+                }
+            }
+            for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = nextVolp) {
+                nextVolp = volp->vol[ROVOL].nextp;
+                if ((cellp == NULL || cellp->cellID == volp->cellp->cellID) &&
+                    volp->vol[ROVOL].ID) {
+                    lock_ReleaseRead(&cm_volumeLock);
+                    RDR_InvalidateVolume(volp->cellp->cellID, volp->vol[ROVOL].ID, AFS_INVALIDATE_CREDS);
+                    lock_ObtainRead(&cm_volumeLock);
+                }
+            }
+            for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = nextVolp) {
+                nextVolp = volp->vol[BACKVOL].nextp;
+                if ((cellp == NULL || cellp->cellID == volp->cellp->cellID) &&
+                    volp->vol[BACKVOL].ID) {
+                    lock_ReleaseRead(&cm_volumeLock);
+                    RDR_InvalidateVolume(volp->cellp->cellID, volp->vol[BACKVOL].ID, AFS_INVALIDATE_CREDS);
+                    lock_ObtainRead(&cm_volumeLock);
+                }
+            }
+        }
+        lock_ReleaseRead(&cm_volumeLock);
+    }
 }
+
+