Import Upstream version 1.8.5
[hcoop/debian/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 #ifdef AFS_SGI65_ENV
39 /* fixup_pags returns error code if relevant or 0 on no error.
40 * Sets up the cred for the call to estgroups. This is pretty convoluted
41 * in order to avoid including the private proc.h header file.
42 */
43 int
44 fixup_pags(int **credpp, int ngroups, gid_t * gidset, int old_afs_pag,
45 int old_dfs_pag)
46 {
47 int new_afs_pag = 0;
48 int new_dfs_pag = 0;
49 int new, old;
50 int changed = 0;
51 cred_t *cr;
52 gid_t groups[NGROUPS_UMAX];
53 int code;
54
55 if (ngroups < 0 || ngroups > ngroups_max)
56 return EINVAL;
57
58 if (ngroups) {
59 AFS_COPYIN(gidset, groups, ngroups * sizeof(gid_t), code);
60 if (code)
61 return EFAULT;
62 }
63
64 if (ngroups >= 2) { /* possibly an AFS PAG */
65 new_afs_pag =
66 (afs_get_pag_from_groups(groups[0], groups[1]) != NOPAG);
67 }
68 if (ngroups >= 1) { /* possibly a DFS PAG */
69 new_dfs_pag = (int)groups[ngroups - 1];
70 if (((new_dfs_pag >> 24) & 0xff) == 'A')
71 new_dfs_pag = (int)groups[ngroups - 1];
72 else
73 new_dfs_pag = 0;
74 }
75
76 /* Now compute the number of groups we will need. */
77 new = ngroups;
78 if (old_afs_pag && !new_afs_pag) /* prepend old AFS pag */
79 new += 2;
80 if (old_dfs_pag && !new_dfs_pag) /* append old DFS pag */
81 new++;
82
83 if (new > ngroups_max)
84 return EINVAL; /* sorry */
85
86 cr = crdup(OSI_GET_CURRENT_CRED()); /* we will replace all the groups. */
87 memset(&cr->cr_groups, 0, ngroups_max * sizeof(gid_t));
88
89 /* Now cobble the new groups list together. */
90 new = 0;
91 old = 0;
92 if (old_afs_pag && !new_afs_pag) { /* prepend old AFS pag */
93 gid_t g0, g1;
94 changed = 1;
95 afs_get_groups_from_pag(old_afs_pag, &g0, &g1);
96 cr->cr_groups[new++] = g0;
97 cr->cr_groups[new++] = g1;
98 }
99
100 for (old = 0; old < ngroups; old++)
101 cr->cr_groups[new++] = groups[old];
102
103 if (old_dfs_pag && !new_dfs_pag) { /* append old DFS pag */
104 changed = 1;
105 cr->cr_groups[new++] = old_dfs_pag;
106 }
107
108 /* Now, did we do anything? */
109 if (changed) {
110 cr->cr_ngroups = new;
111 *credpp = cr;
112 } else {
113 crfree(cr);
114 *credpp = NULL;
115 }
116 return 0;
117 }
118 #else
119 /*
120 * Generic routine to set the PAG in the cred for AFS and DFS.
121 * If flag = 0 this is a DFS pag held in one group.
122 * If flag = 1 this is a AFS pag held in two group entries
123 */
124 static int
125 afsDFS_SetPagInCred(struct ucred *credp, int pag, int flag)
126 {
127 int *gidset;
128 int i, ngrps;
129 gid_t g0, g1;
130 int n = 0;
131 struct ucred *newcredp;
132 int groups_taken = (flag ? 2 : 1);
133
134 ngrps = credp->cr_ngroups + groups_taken;
135 if (ngrps >= ngroups_max)
136 return E2BIG;
137
138
139 if (flag) {
140 /* Break out the AFS pag into two groups */
141 afs_get_groups_from_pag(pag, &g0, &g1);
142 }
143
144 newcredp = crdup(credp);
145 newcredp->cr_ngroups = ngrps;
146
147 if (flag) {
148 /* AFS case */
149 newcredp->cr_groups[0] = g0;
150 newcredp->cr_groups[1] = g1;
151 } else {
152 /* DFS case */
153 if (PagInCred(newcredp) != NOPAG) {
154 /* found an AFS PAG is set in this cred */
155 n = 2;
156 }
157 newcredp->cr_groups[n] = pag;
158 }
159 for (i = n; i < credp->cr_ngroups; i++)
160 newcredp->cr_groups[i + groups_taken] = credp->cr_groups[i];
161
162 /* estgroups sets current threads cred from newcredp and crfree's credp */
163 estgroups(credp, newcredp);
164
165 return 0;
166 }
167 #endif /* AFS_SGI65_ENV */
168
169 /* SGI's osi_GetPagFromCred - They return a long. */
170 int
171 osi_DFSGetPagFromCred(struct ucred *credp)
172 {
173 int pag;
174 int ngroups;
175
176 /*
177 * For IRIX, the PAG is stored in the first entry
178 * of the gruop list in the cred structure. gid_t's
179 * are 32 bits on 64 bit and 32 bit hardware types.
180 * As of Irix 6.5, the DFS pag is the last group in the list.
181 */
182 ngroups = credp->cr_ngroups;
183 if (ngroups < 1)
184 return NOPAG;
185 /*
186 * Keep in mind that we might be living with AFS here.
187 * This means we don't really know if our DFS PAG is in
188 * the first or third group entry.
189 */
190 #ifdef AFS_SGI65_ENV
191 pag = credp->cr_groups[ngroups - 1];
192 #else
193 pag = credp->cr_groups[0];
194 if (PagInCred(credp) != NOPAG) {
195 /* AFS has a PAG value in the first two group entries */
196 if (ngroups < 3)
197 return NOPAG;
198 pag = credp->cr_groups[2];
199 }
200 #endif
201 if (((pag >> 24) & 0xff) == 'A')
202 return pag;
203 else
204 return NOPAG;
205 }
206
207 int
208 Afs_xsetgroups(int ngroups, gid_t * gidset)
209 {
210 int old_afs_pag = NOPAG;
211 int old_dfs_pag = NOPAG;
212 int code = 0;
213 struct ucred *credp = OSI_GET_CURRENT_CRED();
214 struct ucred *modcredp;
215
216
217 credp = OSI_GET_CURRENT_CRED();
218 /* First get any old PAG's */
219 old_afs_pag = PagInCred(credp);
220 old_dfs_pag = osi_DFSGetPagFromCred(credp);
221
222 /* Set the passed in group list. */
223 if (code = setgroups(ngroups, gidset))
224 return code;
225
226 #ifdef AFS_SGI65_ENV
227 if (old_afs_pag == NOPAG && old_dfs_pag == NOPAG)
228 return 0;
229
230 /* Well, we could get the cred, except it's in the proc struct which
231 * is not a publicly available header. And the cred won't be valid on
232 * the uthread until we return to user space. So, we examine the passed
233 * in groups in fixup_pags.
234 */
235 code =
236 fixup_pags(&modcredp, ngroups, gidset,
237 (old_afs_pag == NOPAG) ? 0 : old_afs_pag,
238 (old_dfs_pag == NOPAG) ? 0 : old_dfs_pag);
239 if (!code && modcredp)
240 estgroups(OSI_GET_CURRENT_PROCP(), modcredp);
241 #else
242
243 /*
244 * The setgroups gave our curent thread a new cred pointer
245 * Get the value again
246 */
247 credp = OSI_GET_CURRENT_CRED();
248 if ((PagInCred(credp) == NOPAG) && (old_afs_pag != NOPAG)) {
249 /* reset the AFS PAG */
250 code = afsDFS_SetPagInCred(credp, old_afs_pag, 1);
251 }
252 /*
253 * Once again get the credp because the afsDFS_SetPagInCred might have
254 * assigned a new one.
255 */
256 credp = OSI_GET_CURRENT_CRED();
257 if ((osi_DFSGetPagFromCred(credp) == NOPAG)
258 && (old_dfs_pag != NOPAG)) {
259 code = afsDFS_SetPagInCred(credp, old_dfs_pag, 0);
260 }
261 #endif /* AFS_SGI65_ENV */
262 return code;
263 }
264
265
266 int
267 setpag(cred, pagvalue, newpag, change_parent)
268 struct ucred **cred;
269 afs_uint32 pagvalue;
270 afs_uint32 *newpag;
271 afs_uint32 change_parent;
272 {
273 gid_t gidset[NGROUPS];
274 int ngroups, code;
275 int j;
276
277 AFS_STATCNT(setpag);
278
279 ngroups = afs_getgroups(*cred, NGROUPS, gidset);
280 if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
281 /* We will have to shift grouplist to make room for pag */
282 if (ngroups + 2 > NGROUPS) {
283 #if defined(KERNEL_HAVE_UERROR)
284 return (setuerror(E2BIG), E2BIG);
285 #else
286 return (E2BIG);
287 #endif
288 }
289 for (j = ngroups - 1; j >= 0; j--) {
290 gidset[j + 2] = gidset[j];
291 }
292 ngroups += 2;
293 }
294 *newpag = (pagvalue == -1 ? genpag() : pagvalue);
295 afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
296 if (code = afs_setgroups(cred, ngroups, gidset, change_parent)) {
297 #if defined(KERNEL_HAVE_UERROR)
298 return (setuerror(code), code);
299 #else
300 return code;
301 #endif
302 }
303 return code;
304 }
305
306
307 static int
308 afs_getgroups(struct ucred *cred, int ngroups, gid_t * gidset)
309 {
310 int ngrps, savengrps;
311 gid_t *gp;
312
313 gidset[0] = gidset[1] = 0;
314 AFS_STATCNT(afs_getgroups);
315 savengrps = ngrps = MIN(ngroups, cred->cr_ngroups);
316 gp = cred->cr_groups;
317 while (ngrps--)
318 *gidset++ = *gp++;
319 return savengrps;
320 }
321
322
323
324 static int
325 afs_setgroups(struct ucred **cred, int ngroups, gid_t * gidset,
326 int change_parent)
327 {
328 gid_t *gp;
329 cred_t *cr, *newcr;
330
331 AFS_STATCNT(afs_setgroups);
332
333 if (ngroups > ngroups_max)
334 return EINVAL;
335 cr = *cred;
336 if (!change_parent)
337 newcr = crdup(cr);
338 else
339 newcr = cr;
340 newcr->cr_ngroups = ngroups;
341 gp = newcr->cr_groups;
342 while (ngroups--)
343 *gp++ = *gidset++;
344 if (!change_parent) {
345 #ifdef AFS_SGI65_ENV
346 estgroups(OSI_GET_CURRENT_PROCP(), newcr);
347 #else
348 estgroups(cr, newcr);
349 #endif
350 }
351 *cred = newcr;
352 return (0);
353 }