Windows: do not call time() in a loop
[openafs.git] / src / WINNT / afsd / cm_aclent.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <roken.h>
13
14 #include <afs/stds.h>
15
16 #include <windows.h>
17 #include <stdlib.h>
18 #include <string.h>
19
20 #include "afsd.h"
21 #include <osisleep.h>
22
23 /*
24  * This next lock controls access to all cm_aclent structures in the system,
25  * in either the free list or in the LRU queue.  A read lock prevents someone
26  * from modifying the list(s), and a write lock is required for modifying
27  * the list.  The actual data stored in the randomUid and randomAccess fields
28  * is actually maintained as up-to-date or not via the scache lock.
29  * An aclent structure is free if it has no back vnode pointer.
30  */
31 osi_rwlock_t cm_aclLock;                /* lock for system's aclents */
32
33 /* This must be called with cm_aclLock and the aclp->back->mx held */
34 static void CleanupACLEnt(cm_aclent_t * aclp)
35 {
36     cm_aclent_t *taclp;
37     cm_aclent_t **laclpp;
38
39     if (aclp->backp) {
40         if (aclp->backp->randomACLp) {
41             /*
42              * Remove the entry from the vnode's list
43              */
44             lock_AssertWrite(&aclp->backp->rw);
45             laclpp = &aclp->backp->randomACLp;
46             for (taclp = *laclpp; taclp; laclpp = &taclp->nextp, taclp = *laclpp) {
47                 if (taclp == aclp)
48                     break;
49             }
50             if (!taclp)
51                 osi_panic("CleanupACLEnt race", __FILE__, __LINE__);
52             *laclpp = aclp->nextp;                      /* remove from vnode list */
53         }
54         aclp->backp = NULL;
55     }
56
57     /* release the old user */
58     if (aclp->userp) {
59         cm_ReleaseUser(aclp->userp);
60         aclp->userp = NULL;
61     }
62
63     aclp->randomAccess = 0;
64     aclp->tgtLifetime = 0;
65 }
66
67 /*
68  * Get an acl cache entry for a particular user and file, or return that it doesn't exist.
69  * Called with the scp locked.
70  */
71 long cm_FindACLCache(cm_scache_t *scp, cm_user_t *userp, afs_uint32 *rightsp)
72 {
73     cm_aclent_t *aclp;
74     long retval = -1;
75     time_t now = time(NULL);
76
77     lock_ObtainWrite(&cm_aclLock);
78     *rightsp = 0;   /* get a new acl from server if we don't find a
79                      * current entry
80                      */
81
82     for (aclp = scp->randomACLp; aclp; aclp = aclp->nextp) {
83         if (aclp->userp == userp) {
84             if (aclp->tgtLifetime && aclp->tgtLifetime <= now) {
85                 /* ticket expired */
86                 osi_QRemoveHT((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
87                 CleanupACLEnt(aclp);
88
89                 /* move to the tail of the LRU queue */
90                 osi_QAddT((osi_queue_t **) &cm_data.aclLRUp,
91                            (osi_queue_t **) &cm_data.aclLRUEndp,
92                            &aclp->q);
93             } else {
94                 *rightsp = aclp->randomAccess;
95                 if (cm_data.aclLRUp != aclp) {
96                     /* move to the head of the LRU queue */
97                     osi_QRemoveHT((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
98                     osi_QAddH((osi_queue_t **) &cm_data.aclLRUp,
99                               (osi_queue_t **) &cm_data.aclLRUEndp,
100                               &aclp->q);
101                 }
102                 retval = 0;     /* success */
103             }
104             break;
105         }
106     }
107
108     lock_ReleaseWrite(&cm_aclLock);
109     return retval;
110 }
111
112 /*
113  * This function returns a free (not in the LRU queue) acl cache entry.
114  * It must be called with the cm_aclLock lock held
115  */
116 static cm_aclent_t *GetFreeACLEnt(cm_scache_t * scp)
117 {
118     cm_aclent_t *aclp;
119     cm_scache_t *ascp = 0;
120
121     if (cm_data.aclLRUp == NULL)
122         osi_panic("empty aclent LRU", __FILE__, __LINE__);
123
124     if (cm_data.aclLRUEndp == NULL)
125         osi_panic("inconsistent aclent LRUEndp == NULL", __FILE__, __LINE__);
126
127     aclp = cm_data.aclLRUEndp;
128     osi_QRemoveHT((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
129
130     if (aclp->backp && scp != aclp->backp) {
131         ascp = aclp->backp;
132         lock_ReleaseWrite(&cm_aclLock);
133         lock_ObtainWrite(&ascp->rw);
134         lock_ObtainWrite(&cm_aclLock);
135     }
136     CleanupACLEnt(aclp);
137
138     if (ascp)
139         lock_ReleaseWrite(&ascp->rw);
140     return aclp;
141 }
142
143 time_t cm_TGTLifeTime(cm_user_t *userp, afs_uint32 cellID)
144 {
145     cm_cell_t *cellp = NULL;
146     cm_ucell_t * ucp = NULL;
147     time_t      expirationTime = 0;
148
149     lock_ObtainMutex(&userp->mx);
150     cellp = cm_FindCellByID(cellID, CM_FLAG_NOPROBE);
151     ucp = cm_GetUCell(userp, cellp);
152     if (ucp->ticketp)
153         expirationTime = ucp->expirationTime;
154     lock_ReleaseMutex(&userp->mx);
155
156     return expirationTime;
157 }
158
159
160 /*
161  * Add rights to an acl cache entry.  Do the right thing if not present,
162  * including digging up an entry from the LRU queue.
163  *
164  * The scp must be locked when this function is called.
165  */
166 long cm_AddACLCache(cm_scache_t *scp, cm_user_t *userp, afs_uint32 rights)
167 {
168     struct cm_aclent *aclp;
169     time_t tgtLifeTime;
170
171     tgtLifeTime = cm_TGTLifeTime(userp, scp->fid.cell);
172
173     lock_ObtainWrite(&cm_aclLock);
174     for (aclp = scp->randomACLp; aclp; aclp = aclp->nextp) {
175         if (aclp->userp == userp) {
176             aclp->randomAccess = rights;
177             if (aclp->tgtLifetime < tgtLifeTime)
178                 aclp->tgtLifetime = tgtLifeTime;
179             if (cm_data.aclLRUp != aclp) {
180                 /* move to the head of the LRU queue */
181                 osi_QRemoveHT((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
182                 osi_QAddH((osi_queue_t **) &cm_data.aclLRUp,
183                            (osi_queue_t **) &cm_data.aclLRUEndp,
184                            &aclp->q);
185             }
186             lock_ReleaseWrite(&cm_aclLock);
187             return 0;
188         }
189     }
190
191     /*
192      * Didn't find the dude we're looking for, so take someone from the LRUQ
193      * and  reuse. But first try the free list and see if there's already
194      * someone there.
195      */
196     aclp = GetFreeACLEnt(scp);           /* can't fail, panics instead */
197     osi_QAddH((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
198     aclp->backp = scp;
199     aclp->nextp = scp->randomACLp;
200     scp->randomACLp = aclp;
201     cm_HoldUser(userp);
202     aclp->userp = userp;
203     aclp->randomAccess = rights;
204     aclp->tgtLifetime = tgtLifeTime;
205     lock_ReleaseWrite(&cm_aclLock);
206
207     return 0;
208 }
209
210 long cm_ShutdownACLCache(void)
211 {
212     return 0;
213 }
214
215 long cm_ValidateACLCache(void)
216 {
217     long size = cm_data.stats * 2;
218     long count;
219     cm_aclent_t * aclp;
220
221     if ( cm_data.aclLRUp == NULL && cm_data.aclLRUEndp != NULL ||
222          cm_data.aclLRUp != NULL && cm_data.aclLRUEndp == NULL) {
223         afsi_log("cm_ValidateACLCache failure: inconsistent LRU pointers");
224         fprintf(stderr, "cm_ValidateACLCache failure: inconsistent LRU pointers\n");
225         return -9;
226     }
227
228     for ( aclp = cm_data.aclLRUp, count = 0; aclp;
229           aclp = (cm_aclent_t *) osi_QNext(&aclp->q), count++ ) {
230         if (aclp->magic != CM_ACLENT_MAGIC) {
231             afsi_log("cm_ValidateACLCache failure: acpl->magic != CM_ACLENT_MAGIC");
232             fprintf(stderr, "cm_ValidateACLCache failure: acpl->magic != CM_ACLENT_MAGIC\n");
233             return -1;
234         }
235         if (aclp->nextp && aclp->nextp->magic != CM_ACLENT_MAGIC) {
236             afsi_log("cm_ValidateACLCache failure: acpl->nextp->magic != CM_ACLENT_MAGIC");
237             fprintf(stderr,"cm_ValidateACLCache failure: acpl->nextp->magic != CM_ACLENT_MAGIC\n");
238             return -2;
239         }
240         if (aclp->backp && aclp->backp->magic != CM_SCACHE_MAGIC) {
241             afsi_log("cm_ValidateACLCache failure: acpl->backp->magic != CM_SCACHE_MAGIC");
242             fprintf(stderr,"cm_ValidateACLCache failure: acpl->backp->magic != CM_SCACHE_MAGIC\n");
243             return -3;
244         }
245         if (count != 0 && aclp == cm_data.aclLRUp || count > size) {
246             afsi_log("cm_ValidateACLCache failure: loop in cm_data.aclLRUp list");
247             fprintf(stderr, "cm_ValidateACLCache failure: loop in cm_data.aclLRUp list\n");
248             return -4;
249         }
250     }
251
252     for ( aclp = cm_data.aclLRUEndp, count = 0; aclp;
253           aclp = (cm_aclent_t *) osi_QPrev(&aclp->q), count++ ) {
254         if (aclp->magic != CM_ACLENT_MAGIC) {
255             afsi_log("cm_ValidateACLCache failure: aclp->magic != CM_ACLENT_MAGIC");
256             fprintf(stderr, "cm_ValidateACLCache failure: aclp->magic != CM_ACLENT_MAGIC\n");
257             return -5;
258         }
259         if (aclp->nextp && aclp->nextp->magic != CM_ACLENT_MAGIC) {
260             afsi_log("cm_ValidateACLCache failure: aclp->nextp->magic != CM_ACLENT_MAGIC");
261             fprintf(stderr, "cm_ValidateACLCache failure: aclp->nextp->magic != CM_ACLENT_MAGIC\n");
262             return -6;
263         }
264         if (aclp->backp && aclp->backp->magic != CM_SCACHE_MAGIC) {
265             afsi_log("cm_ValidateACLCache failure: aclp->backp->magic != CM_SCACHE_MAGIC");
266             fprintf(stderr, "cm_ValidateACLCache failure: aclp->backp->magic != CM_SCACHE_MAGIC\n");
267             return -7;
268         }
269
270         if (count != 0 && aclp == cm_data.aclLRUEndp || count > size) {
271             afsi_log("cm_ValidateACLCache failure: loop in cm_data.aclLRUEndp list");
272             fprintf(stderr, "cm_ValidateACLCache failure: loop in cm_data.aclLRUEndp list\n");
273             return -8;
274         }
275     }
276
277     return 0;
278 }
279
280 /*
281  * Initialize the cache to have an entries.  Called during system startup.
282  */
283 long cm_InitACLCache(int newFile, long size)
284 {
285     cm_aclent_t *aclp;
286     long i;
287     static osi_once_t once;
288
289     if (osi_Once(&once)) {
290         lock_InitializeRWLock(&cm_aclLock, "cm_aclLock", LOCK_HIERARCHY_ACL_GLOBAL);
291         osi_EndOnce(&once);
292     }
293
294     lock_ObtainWrite(&cm_aclLock);
295     if ( newFile ) {
296         cm_data.aclLRUp = cm_data.aclLRUEndp = NULL;
297         aclp = (cm_aclent_t *) cm_data.aclBaseAddress;
298         memset(aclp, 0, size * sizeof(cm_aclent_t));
299
300         /*
301          * Put all of these guys on the LRU queue
302          */
303         for (i = 0; i < size; i++) {
304             aclp->magic = CM_ACLENT_MAGIC;
305             osi_QAddH((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
306             aclp++;
307         }
308     } else {
309         aclp = (cm_aclent_t *) cm_data.aclBaseAddress;
310         for (i = 0; i < size; i++) {
311             aclp->userp = NULL;
312             aclp->tgtLifetime = 0;
313             aclp++;
314         }
315     }
316     lock_ReleaseWrite(&cm_aclLock);
317     return 0;
318 }
319
320
321 /*
322  * Free all associated acl entries.  We actually just clear the back pointer
323  * since the acl entries are already in the free list.  The scp must be locked
324  * or completely unreferenced (such as when called while recycling the scp).
325  */
326 void cm_FreeAllACLEnts(cm_scache_t *scp)
327 {
328     cm_aclent_t *aclp;
329     cm_aclent_t *taclp;
330
331     lock_ObtainWrite(&cm_aclLock);
332     for (aclp = scp->randomACLp; aclp; aclp = taclp) {
333         taclp = aclp->nextp;
334         if (aclp->userp) {
335             cm_ReleaseUser(aclp->userp);
336             aclp->userp = NULL;
337         }
338         aclp->backp = (struct cm_scache *) 0;
339     }
340
341     scp->randomACLp = (struct cm_aclent *) 0;
342     scp->anyAccess = 0;         /* reset this, too */
343     lock_ReleaseWrite(&cm_aclLock);
344 }
345
346
347 /*
348  * Invalidate all ACL entries for particular user on this particular vnode.
349  *
350  * The scp must not be locked.
351  */
352 void cm_InvalidateACLUser(cm_scache_t *scp, cm_user_t *userp)
353 {
354     cm_aclent_t *aclp;
355     cm_aclent_t **laclpp;
356     int found = 0;
357     int callback = 0;
358
359     lock_ObtainWrite(&scp->rw);
360     lock_ObtainWrite(&cm_aclLock);
361     laclpp = &scp->randomACLp;
362     for (aclp = *laclpp; aclp; laclpp = &aclp->nextp, aclp = *laclpp) {
363         if (userp == aclp->userp) {     /* One for a given user/scache */
364             *laclpp = aclp->nextp;
365             cm_ReleaseUser(aclp->userp);
366             aclp->userp = NULL;
367             aclp->backp = (struct cm_scache *) 0;
368             found = 1;
369             break;
370         }
371     }
372     lock_ReleaseWrite(&cm_aclLock);
373     if (found)
374         callback = cm_HaveCallback(scp);
375     lock_ReleaseWrite(&scp->rw);
376
377     if (found && callback && RDR_Initialized)
378         RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
379                              scp->fid.hash, scp->fileType, AFS_INVALIDATE_CREDS);
380 }
381
382 /*
383  * Invalidate ACL info for a user that has just obtained or lost tokens.
384  */
385 void
386 cm_ResetACLCache(cm_cell_t *cellp, cm_user_t *userp)
387 {
388     cm_volume_t *volp, *nextVolp;
389     cm_scache_t *scp, *nextScp;
390     afs_uint32 hash;
391
392     lock_ObtainRead(&cm_scacheLock);
393     for (hash=0; hash < cm_data.scacheHashTableSize; hash++) {
394         for (scp=cm_data.scacheHashTablep[hash]; scp; scp=nextScp) {
395             nextScp = scp->nextp;
396             if (cellp == NULL ||
397                 scp->fid.cell == cellp->cellID) {
398                 cm_HoldSCacheNoLock(scp);
399                 lock_ReleaseRead(&cm_scacheLock);
400                 cm_InvalidateACLUser(scp, userp);
401                 lock_ObtainRead(&cm_scacheLock);
402                 cm_ReleaseSCacheNoLock(scp);
403             }
404         }
405     }
406     lock_ReleaseRead(&cm_scacheLock);
407
408     cm_EAccesClearUserEntries(userp, cellp->cellID);
409
410     if (RDR_Initialized) {
411         lock_ObtainRead(&cm_volumeLock);
412         for (hash = 0; hash < cm_data.volumeHashTableSize; hash++) {
413             for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = nextVolp) {
414                 nextVolp = volp->vol[RWVOL].nextp;
415                 if ((cellp == NULL || cellp->cellID == volp->cellp->cellID) &&
416                     volp->vol[RWVOL].ID) {
417                     lock_ReleaseRead(&cm_volumeLock);
418                     RDR_InvalidateVolume(volp->cellp->cellID, volp->vol[RWVOL].ID, AFS_INVALIDATE_CREDS);
419                     lock_ObtainRead(&cm_volumeLock);
420                 }
421             }
422             for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = nextVolp) {
423                 nextVolp = volp->vol[ROVOL].nextp;
424                 if ((cellp == NULL || cellp->cellID == volp->cellp->cellID) &&
425                     volp->vol[ROVOL].ID) {
426                     lock_ReleaseRead(&cm_volumeLock);
427                     RDR_InvalidateVolume(volp->cellp->cellID, volp->vol[ROVOL].ID, AFS_INVALIDATE_CREDS);
428                     lock_ObtainRead(&cm_volumeLock);
429                 }
430             }
431             for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = nextVolp) {
432                 nextVolp = volp->vol[BACKVOL].nextp;
433                 if ((cellp == NULL || cellp->cellID == volp->cellp->cellID) &&
434                     volp->vol[BACKVOL].ID) {
435                     lock_ReleaseRead(&cm_volumeLock);
436                     RDR_InvalidateVolume(volp->cellp->cellID, volp->vol[BACKVOL].ID, AFS_INVALIDATE_CREDS);
437                     lock_ObtainRead(&cm_volumeLock);
438                 }
439             }
440         }
441         lock_ReleaseRead(&cm_volumeLock);
442     }
443 }
444
445