windows-pcache-20050310
[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 #ifndef DJGPP
14 #include <windows.h>
15 #endif
16 #include <stdlib.h>
17 #include <string.h>
18 #include <malloc.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         /* 
41          * Remove the entry from the vnode's list 
42          */
43         laclpp = &aclp->backp->randomACLp;
44         for (taclp = *laclpp; taclp; laclpp = &taclp->nextp, taclp = *laclpp) {
45             if (taclp == aclp) 
46                 break;
47         }
48         if (!taclp) 
49             osi_panic("CleanupACLEnt race", __FILE__, __LINE__);
50         *laclpp = aclp->nextp;                  /* remove from vnode list */
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, long *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 <= osi_Time()) {
81                 /* ticket expired */
82                 osi_QRemove((osi_queue_t **) &cm_data.aclLRUp, &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.aclLRUEndp == aclp)
92                     cm_data.aclLRUEndp = (cm_aclent_t *) osi_QPrev(&aclp->q);
93
94                 /* move to the head of the LRU queue */
95                 osi_QRemove((osi_queue_t **) &cm_data.aclLRUp, &aclp->q);
96                 osi_QAddH((osi_queue_t **) &cm_data.aclLRUp,
97                            (osi_queue_t **) &cm_data.aclLRUEndp,
98                            &aclp->q);
99                 retval = 0;     /* success */
100             }               
101             break;
102         }
103     }
104
105     lock_ReleaseWrite(&cm_aclLock);
106     return retval;
107 }       
108
109 /* 
110  * This function returns a free (not in the LRU queue) acl cache entry.
111  * It must be called with the cm_aclLock lock held
112  */
113 static cm_aclent_t *GetFreeACLEnt(void)
114 {
115     cm_aclent_t *aclp;
116
117     if (cm_data.aclLRUp == NULL)
118         osi_panic("empty aclent LRU", __FILE__, __LINE__);
119
120     aclp = cm_data.aclLRUEndp;
121     cm_data.aclLRUEndp = (cm_aclent_t *) osi_QPrev(&aclp->q);
122     osi_QRemove((osi_queue_t **) &cm_data.aclLRUp, &aclp->q);
123
124     CleanupACLEnt(aclp);
125
126     return aclp;
127 }
128
129
130 /* 
131  * Add rights to an acl cache entry.  Do the right thing if not present, 
132  * including digging up an entry from the LRU queue.
133  *
134  * The scp must be locked when this function is called.
135  */
136 long cm_AddACLCache(cm_scache_t *scp, cm_user_t *userp, long rights)
137 {
138     register struct cm_aclent *aclp;
139
140     lock_ObtainWrite(&cm_aclLock);
141     for (aclp = scp->randomACLp; aclp; aclp = aclp->nextp) {
142         if (aclp->userp == userp) {
143             aclp->randomAccess = rights;
144             if (aclp->tgtLifetime == 0) 
145                 aclp->tgtLifetime = cm_TGTLifeTime(pag);
146             lock_ReleaseWrite(&cm_aclLock);
147             return 0;
148         }
149     }
150
151     /* 
152      * Didn't find the dude we're looking for, so take someone from the LRUQ 
153      * and  reuse. But first try the free list and see if there's already 
154      * someone there.
155      */
156     aclp = GetFreeACLEnt();              /* can't fail, panics instead */
157     osi_QAddH((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
158     aclp->backp = scp;
159     aclp->nextp = scp->randomACLp;
160     scp->randomACLp = aclp;
161     cm_HoldUser(userp);
162     aclp->userp = userp;
163     aclp->randomAccess = rights;
164     aclp->tgtLifetime = cm_TGTLifeTime(userp);
165     lock_ReleaseWrite(&cm_aclLock);
166
167     return 0;
168 }
169
170 long cm_ShutdownACLCache(void)
171 {
172     return 0;
173 }
174
175 long cm_ValidateACLCache(void)
176 {
177     long size = cm_data.stats * 2;
178     long count;
179     cm_aclent_t * aclp;
180
181     for ( aclp = cm_data.aclLRUp, count = 0; aclp;
182           aclp = (cm_aclent_t *) osi_QNext(&aclp->q), count++ ) {
183         if (aclp->magic != CM_ACLENT_MAGIC) {
184             afsi_log("cm_ValidateACLCache failure: acpl->magic != CM_ACLENT_MAGIC");
185             fprintf(stderr, "cm_ValidateACLCache failure: acpl->magic != CM_ACLENT_MAGIC\n");
186             return -1;
187         }
188         if (aclp->nextp && aclp->nextp->magic != CM_ACLENT_MAGIC) {
189             afsi_log("cm_ValidateACLCache failure: acpl->nextp->magic != CM_ACLENT_MAGIC");
190             fprintf(stderr,"cm_ValidateACLCache failure: acpl->nextp->magic != CM_ACLENT_MAGIC\n");
191             return -2;
192         }
193         if (aclp->backp && aclp->backp->magic != CM_SCACHE_MAGIC) {
194             afsi_log("cm_ValidateACLCache failure: acpl->backp->magic != CM_SCACHE_MAGIC");
195             fprintf(stderr,"cm_ValidateACLCache failure: acpl->backp->magic != CM_SCACHE_MAGIC\n");
196             return -3;
197         }
198         if (count != 0 && aclp == cm_data.aclLRUp || count > size) {
199             afsi_log("cm_ValidateACLCache failure: loop in cm_data.aclLRUp list");
200             fprintf(stderr, "cm_ValidateACLCache failure: loop in cm_data.aclLRUp list\n");
201             return -4;
202         }
203     }
204
205     for ( aclp = cm_data.aclLRUEndp, count = 0; aclp;
206           aclp = (cm_aclent_t *) osi_QPrev(&aclp->q), count++ ) {
207         if (aclp->magic != CM_ACLENT_MAGIC) {
208             afsi_log("cm_ValidateACLCache failure: aclp->magic != CM_ACLENT_MAGIC");
209             fprintf(stderr, "cm_ValidateACLCache failure: aclp->magic != CM_ACLENT_MAGIC\n");
210             return -5;
211         }
212         if (aclp->nextp && aclp->nextp->magic != CM_ACLENT_MAGIC) {
213             afsi_log("cm_ValidateACLCache failure: aclp->nextp->magic != CM_ACLENT_MAGIC");
214             fprintf(stderr, "cm_ValidateACLCache failure: aclp->nextp->magic != CM_ACLENT_MAGIC\n");
215             return -6;
216         }
217         if (aclp->backp && aclp->backp->magic != CM_SCACHE_MAGIC) {
218             afsi_log("cm_ValidateACLCache failure: aclp->backp->magic != CM_SCACHE_MAGIC");
219             fprintf(stderr, "cm_ValidateACLCache failure: aclp->backp->magic != CM_SCACHE_MAGIC\n");
220             return -7;
221         }
222
223         if (count != 0 && aclp == cm_data.aclLRUEndp || count > size) {
224             afsi_log("cm_ValidateACLCache failure: loop in cm_data.aclLRUEndp list");
225             fprintf(stderr, "cm_ValidateACLCache failure: loop in cm_data.aclLRUEndp list\n");
226             return -8;
227         }
228     }
229
230     return 0;
231 }
232
233 /* 
234  * Initialize the cache to have an entries.  Called during system startup.
235  */
236 long cm_InitACLCache(int newFile, long size)
237 {
238     cm_aclent_t *aclp;
239     long i;
240     static osi_once_t once;
241
242     if (osi_Once(&once)) {
243         lock_InitializeRWLock(&cm_aclLock, "cm_aclLock");
244         osi_EndOnce(&once);
245     }
246
247     lock_ObtainWrite(&cm_aclLock);
248     if ( newFile ) {
249         cm_data.aclLRUp = cm_data.aclLRUEndp = NULL;
250         aclp = (cm_aclent_t *) cm_data.aclBaseAddress;
251         memset(aclp, 0, size * sizeof(cm_aclent_t));
252
253         /* 
254          * Put all of these guys on the LRU queue 
255          */
256         for (i = 0; i < size; i++) {
257             aclp->magic = CM_ACLENT_MAGIC;
258             osi_QAddH((osi_queue_t **) &cm_data.aclLRUp, (osi_queue_t **) &cm_data.aclLRUEndp, &aclp->q);
259             aclp++;
260         }
261     } else {
262         aclp = (cm_aclent_t *) cm_data.aclBaseAddress;
263         for (i = 0; i < size; i++) {
264             aclp->userp = NULL;
265             aclp->tgtLifetime = 0;
266             aclp++;
267         }
268     }
269     lock_ReleaseWrite(&cm_aclLock);
270     return 0;
271 }
272
273
274 /* 
275  * Free all associated acl entries.  We actually just clear the back pointer
276  * since the acl entries are already in the free list.  The scp must be locked
277  * or completely unreferenced (such as when called while recycling the scp).
278  */
279 void cm_FreeAllACLEnts(cm_scache_t *scp)
280 {
281     cm_aclent_t *aclp;
282     cm_aclent_t *taclp;
283
284     lock_ObtainWrite(&cm_aclLock);
285     for (aclp = scp->randomACLp; aclp; aclp = taclp) {
286         taclp = aclp->nextp;
287         if (aclp->userp) {
288             cm_ReleaseUser(aclp->userp);
289             aclp->userp = NULL;
290         }
291         aclp->backp = (struct cm_scache *) 0;
292     }
293
294     scp->randomACLp = (struct cm_aclent *) 0;
295     scp->anyAccess = 0;         /* reset this, too */
296     lock_ReleaseWrite(&cm_aclLock);
297 }
298
299
300 /* 
301  * Invalidate all ACL entries for particular user on this particular vnode.
302  *
303  * The scp must be locked.
304  */
305 void cm_InvalidateACLUser(cm_scache_t *scp, cm_user_t *userp)
306 {
307     cm_aclent_t *aclp;
308     cm_aclent_t **laclpp;
309
310     lock_ObtainWrite(&cm_aclLock);
311     laclpp = &scp->randomACLp;
312     for (aclp = *laclpp; aclp; laclpp = &aclp->nextp, aclp = *laclpp) {
313         if (userp == aclp->userp) {     /* One for a given user/scache */
314             *laclpp = aclp->nextp;
315             cm_ReleaseUser(aclp->userp);
316             aclp->userp = NULL;
317             aclp->backp = (struct cm_scache *) 0;
318             break;
319         }
320     }
321     lock_ReleaseWrite(&cm_aclLock);
322 }