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