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