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