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