ukernel: Fix AFS_GUNLOCK panic in rx_ServerProc
[openafs.git] / src / afs / IRIX / osi_groups.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 /*
11  * Implements:
12  * afsDFS_SetPagInCred (shared with SGI)
13  * osi_DFSGetPagFromCred (shared with SGI)
14  * Afs_xsetgroups (syscall)
15  * setpag
16  *
17  */
18 #include <afsconfig.h>
19 #include "afs/param.h"
20
21
22 #include "afs/sysincludes.h"
23 #include "afsincludes.h"
24 #include "afs/afs_stats.h"      /* statistics */
25
26
27 static int
28   afs_getgroups(struct ucred *cred, int ngroups, gid_t * gidset);
29
30 static int
31   afs_setgroups(struct ucred **cred, int ngroups, gid_t * gidset,
32                 int change_parent);
33
34
35 /* This is common code between SGI's DFS and our AFS. Do *not* alter it's
36  * interface or semantics without notifying SGI.
37  */
38 /* fixup_pags returns error code if relevant or 0 on no error.
39  * Sets up the cred for the call to estgroups. This is pretty convoluted
40  * in order to avoid including the private proc.h header file.
41  */
42 int
43 fixup_pags(int **credpp, int ngroups, gid_t * gidset, int old_afs_pag,
44            int old_dfs_pag)
45 {
46     int new_afs_pag = 0;
47     int new_dfs_pag = 0;
48     int new, old;
49     int changed = 0;
50     cred_t *cr;
51     gid_t groups[NGROUPS_UMAX];
52     int code;
53
54     if (ngroups < 0 || ngroups > ngroups_max)
55         return EINVAL;
56
57     if (ngroups) {
58         AFS_COPYIN(gidset, groups, ngroups * sizeof(gid_t), code);
59         if (code)
60             return EFAULT;
61     }
62
63     if (ngroups >= 2) {         /* possibly an AFS PAG */
64         new_afs_pag =
65             (afs_get_pag_from_groups(groups[0], groups[1]) != NOPAG);
66     }
67     if (ngroups >= 1) {         /* possibly a DFS PAG */
68         new_dfs_pag = (int)groups[ngroups - 1];
69         if (((new_dfs_pag >> 24) & 0xff) == 'A')
70             new_dfs_pag = (int)groups[ngroups - 1];
71         else
72             new_dfs_pag = 0;
73     }
74
75     /* Now compute the number of groups we will need. */
76     new = ngroups;
77     if (old_afs_pag && !new_afs_pag)    /* prepend old AFS pag */
78         new += 2;
79     if (old_dfs_pag && !new_dfs_pag)    /* append old DFS pag */
80         new++;
81
82     if (new > ngroups_max)
83         return EINVAL;          /* sorry */
84
85     cr = crdup(OSI_GET_CURRENT_CRED()); /* we will replace all the groups. */
86     memset(&cr->cr_groups, 0, ngroups_max * sizeof(gid_t));
87
88     /* Now cobble the new groups list together. */
89     new = 0;
90     old = 0;
91     if (old_afs_pag && !new_afs_pag) {  /* prepend old AFS pag */
92         gid_t g0, g1;
93         changed = 1;
94         afs_get_groups_from_pag(old_afs_pag, &g0, &g1);
95         cr->cr_groups[new++] = g0;
96         cr->cr_groups[new++] = g1;
97     }
98
99     for (old = 0; old < ngroups; old++)
100         cr->cr_groups[new++] = groups[old];
101
102     if (old_dfs_pag && !new_dfs_pag) {  /* append old DFS pag */
103         changed = 1;
104         cr->cr_groups[new++] = old_dfs_pag;
105     }
106
107     /* Now, did we do anything? */
108     if (changed) {
109         cr->cr_ngroups = new;
110         *credpp = cr;
111     } else {
112         crfree(cr);
113         *credpp = NULL;
114     }
115     return 0;
116 }
117
118 /* SGI's osi_GetPagFromCred - They return a long. */
119 int
120 osi_DFSGetPagFromCred(struct ucred *credp)
121 {
122     int pag;
123     int ngroups;
124
125     /*
126      *  For IRIX, the PAG is stored in the first entry
127      *  of the gruop list in the cred structure.  gid_t's
128      *  are 32 bits on 64 bit and 32 bit hardware types.
129      *  As of Irix 6.5, the DFS pag is the last group in the list.
130      */
131     ngroups = credp->cr_ngroups;
132     if (ngroups < 1)
133         return NOPAG;
134     /*
135      *  Keep in mind that we might be living with AFS here.
136      *  This means we don't really know if our DFS PAG is in
137      *  the first or third group entry.
138      */
139     pag = credp->cr_groups[ngroups - 1];
140     if (((pag >> 24) & 0xff) == 'A')
141         return pag;
142     else
143         return NOPAG;
144 }
145
146 int
147 Afs_xsetgroups(int ngroups, gid_t * gidset)
148 {
149     int old_afs_pag = NOPAG;
150     int old_dfs_pag = NOPAG;
151     int code = 0;
152     struct ucred *credp = OSI_GET_CURRENT_CRED();
153     struct ucred *modcredp;
154
155
156     credp = OSI_GET_CURRENT_CRED();
157     /* First get any old PAG's */
158     old_afs_pag = PagInCred(credp);
159     old_dfs_pag = osi_DFSGetPagFromCred(credp);
160
161     /* Set the passed in group list. */
162     if (code = setgroups(ngroups, gidset))
163         return code;
164
165     if (old_afs_pag == NOPAG && old_dfs_pag == NOPAG)
166         return 0;
167
168     /* Well, we could get the cred, except it's in the proc struct which
169      * is not a publicly available header. And the cred won't be valid on
170      * the uthread until we return to user space. So, we examine the passed
171      * in groups in fixup_pags.
172      */
173     code =
174         fixup_pags(&modcredp, ngroups, gidset,
175                    (old_afs_pag == NOPAG) ? 0 : old_afs_pag,
176                    (old_dfs_pag == NOPAG) ? 0 : old_dfs_pag);
177     if (!code && modcredp)
178         estgroups(OSI_GET_CURRENT_PROCP(), modcredp);
179     return code;
180 }
181
182
183 int
184 setpag(cred, pagvalue, newpag, change_parent)
185      struct ucred **cred;
186      afs_uint32 pagvalue;
187      afs_uint32 *newpag;
188      afs_uint32 change_parent;
189 {
190     gid_t gidset[NGROUPS];
191     int ngroups, code;
192     int j;
193
194     AFS_STATCNT(setpag);
195
196     ngroups = afs_getgroups(*cred, NGROUPS, gidset);
197     if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
198         /* We will have to shift grouplist to make room for pag */
199         if (ngroups + 2 > NGROUPS) {
200 #if defined(KERNEL_HAVE_UERROR)
201             return (setuerror(E2BIG), E2BIG);
202 #else
203             return (E2BIG);
204 #endif
205         }
206         for (j = ngroups - 1; j >= 0; j--) {
207             gidset[j + 2] = gidset[j];
208         }
209         ngroups += 2;
210     }
211     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
212     afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
213     if (code = afs_setgroups(cred, ngroups, gidset, change_parent)) {
214 #if defined(KERNEL_HAVE_UERROR)
215         return (setuerror(code), code);
216 #else
217         return code;
218 #endif
219     }
220     return code;
221 }
222
223
224 static int
225 afs_getgroups(struct ucred *cred, int ngroups, gid_t * gidset)
226 {
227     int ngrps, savengrps;
228     gid_t *gp;
229
230     gidset[0] = gidset[1] = 0;
231     AFS_STATCNT(afs_getgroups);
232     savengrps = ngrps = MIN(ngroups, cred->cr_ngroups);
233     gp = cred->cr_groups;
234     while (ngrps--)
235         *gidset++ = *gp++;
236     return savengrps;
237 }
238
239
240
241 static int
242 afs_setgroups(struct ucred **cred, int ngroups, gid_t * gidset,
243               int change_parent)
244 {
245     gid_t *gp;
246     cred_t *cr, *newcr;
247
248     AFS_STATCNT(afs_setgroups);
249
250     if (ngroups > ngroups_max)
251         return EINVAL;
252     cr = *cred;
253     if (!change_parent)
254         newcr = crdup(cr);
255     else
256         newcr = cr;
257     newcr->cr_ngroups = ngroups;
258     gp = newcr->cr_groups;
259     while (ngroups--)
260         *gp++ = *gidset++;
261     if (!change_parent) {
262         estgroups(OSI_GET_CURRENT_PROCP(), newcr);
263     }
264     *cred = newcr;
265     return (0);
266 }