for-rodney-20040404
[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 cm_aclent_t *cm_aclLRUp;                /* LRUQ for dudes in vnodes' lists */
32 cm_aclent_t *cm_aclLRUEndp;             /* ditto */
33
34 /* 
35  * Get an acl cache entry for a particular user and file, or return that it doesn't exist.
36  * Called with the scp locked.
37  */
38 long cm_FindACLCache(cm_scache_t *scp, cm_user_t *userp, long *rightsp)
39 {
40         cm_aclent_t *aclp;
41
42         lock_ObtainWrite(&cm_aclLock);
43         for (aclp = scp->randomACLp; aclp; aclp = aclp->nextp) {
44                 if (aclp->userp == userp) {
45                         if (aclp->tgtLifetime && aclp->tgtLifetime <= (long) osi_Time()) {
46                                 /* ticket expired */
47                                 aclp->tgtLifetime = 0;
48                                 *rightsp = 0;
49                                 break; /* get a new acl from server */
50                         }
51                         else {
52                                 *rightsp = aclp->randomAccess;
53                                 if (cm_aclLRUEndp == aclp)
54                                         cm_aclLRUEndp = (cm_aclent_t *) osi_QPrev(&aclp->q);
55
56                                 /* move to the head of the LRU queue */
57                                 osi_QRemove((osi_queue_t **) &cm_aclLRUp, &aclp->q);
58                                 osi_QAddH((osi_queue_t **) &cm_aclLRUp,
59                                         (osi_queue_t **) &cm_aclLRUEndp,
60                                         &aclp->q);
61                         }
62                         lock_ReleaseWrite(&cm_aclLock);
63                         return 0;
64                 }
65         }
66
67         /* 
68          * If we make it here, this entry isn't present, so we're going to fail. 
69          */
70         lock_ReleaseWrite(&cm_aclLock);
71         return -1;
72 }
73
74
75 /* 
76  * This function returns a free (not in the LRU queue) acl cache entry.
77  * It must be called with the cm_aclLock lock held.
78  */
79 static cm_aclent_t *GetFreeACLEnt(void)
80 {
81         cm_aclent_t *aclp;
82         cm_aclent_t *taclp;
83         cm_aclent_t **laclpp;
84
85         if (cm_aclLRUp == NULL)
86                 osi_panic("empty aclent LRU", __FILE__, __LINE__);
87
88         aclp = cm_aclLRUEndp;
89         if (aclp == cm_aclLRUEndp)
90                 cm_aclLRUEndp = (cm_aclent_t *) osi_QPrev(&aclp->q);
91         osi_QRemove((osi_queue_t **) &cm_aclLRUp, &aclp->q);
92         if (aclp->backp) {
93                 /* 
94                  * Remove the entry from the vnode's list 
95                  */
96                 laclpp = &aclp->backp->randomACLp;
97                 for (taclp = *laclpp; taclp; laclpp = &taclp->nextp, taclp = *laclpp) {
98                         if (taclp == aclp) 
99                                 break;
100                 }
101                 if (!taclp) 
102                         osi_panic("GetFreeACLEnt race", __FILE__, __LINE__);
103                 *laclpp = aclp->nextp;                  /* remove from vnode list */
104                 aclp->backp = NULL;
105         }
106         
107     /* release the old user */
108     if (aclp->userp) {
109                 cm_ReleaseUser(aclp->userp);
110         aclp->userp = NULL;
111         }
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_aclLRUp, (osi_queue_t **) &cm_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 /* 
157  * Initialize the cache to have an entries.  Called during system startup.
158  */
159 long cm_InitACLCache(long size)
160 {
161         cm_aclent_t *aclp;
162         long i;
163         static osi_once_t once;
164
165         
166         if (osi_Once(&once)) {
167                 lock_InitializeRWLock(&cm_aclLock, "cm_aclLock");
168                 osi_EndOnce(&once);
169         }
170
171         lock_ObtainWrite(&cm_aclLock);
172         cm_aclLRUp = cm_aclLRUEndp = NULL;
173         aclp = (cm_aclent_t *) malloc(size * sizeof(cm_aclent_t));
174         memset(aclp, 0, size * sizeof(cm_aclent_t));
175
176         /* 
177          * Put all of these guys on the LRU queue 
178          */
179         for (i = 0; i < size; i++) {
180                 osi_QAddH((osi_queue_t **) &cm_aclLRUp, (osi_queue_t **) &cm_aclLRUEndp,
181                         &aclp->q);
182                 aclp++;
183         }
184
185         lock_ReleaseWrite(&cm_aclLock);
186         return 0;
187 }
188
189
190 /* 
191  * Free all associated acl entries.  We actually just clear the back pointer
192  * since the acl entries are already in the free list.  The scp must be locked
193  * or completely unreferenced (such as when called while recycling the scp).
194  */
195 void cm_FreeAllACLEnts(cm_scache_t *scp)
196 {
197         cm_aclent_t *aclp;
198         cm_aclent_t *taclp;
199
200         lock_ObtainWrite(&cm_aclLock);
201         for (aclp = scp->randomACLp; aclp; aclp = taclp) {
202                 taclp = aclp->nextp;
203                 if (aclp->userp) {
204                         cm_ReleaseUser(aclp->userp);
205                         aclp->userp = NULL;
206                 }
207                 aclp->backp = (struct cm_scache *) 0;
208         }
209
210         scp->randomACLp = (struct cm_aclent *) 0;
211         scp->anyAccess = 0;             /* reset this, too */
212         lock_ReleaseWrite(&cm_aclLock);
213 }
214
215
216 /* 
217  * Invalidate all ACL entries for particular user on this particular vnode.
218  *
219  * The scp must be locked.
220  */
221 void cm_InvalidateACLUser(cm_scache_t *scp, cm_user_t *userp)
222 {
223         cm_aclent_t *aclp;
224         cm_aclent_t **laclpp;
225
226         lock_ObtainWrite(&cm_aclLock);
227         laclpp = &scp->randomACLp;
228         for (aclp = *laclpp; aclp; laclpp = &aclp->nextp, aclp = *laclpp) {
229                 if (userp == aclp->userp) {     /* One for a given user/scache */
230                         *laclpp = aclp->nextp;
231             cm_ReleaseUser(aclp->userp);
232             aclp->userp = NULL;
233                         aclp->backp = (struct cm_scache *) 0;
234                         break;
235                 }
236         }
237         lock_ReleaseWrite(&cm_aclLock);
238 }