linux-updates-20060309
[openafs.git] / src / afs / LINUX / 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  * setgroups (syscall)
13  * setpag
14  *
15  */
16 #include <afsconfig.h>
17 #include "afs/param.h"
18
19 RCSID
20     ("$Header$");
21
22 #include "afs/sysincludes.h"
23 #include "afsincludes.h"
24 #include "afs/afs_stats.h"      /* statistics */
25 #ifdef AFS_LINUX22_ENV
26 #include "h/smp_lock.h"
27 #endif
28
29 #if defined(AFS_LINUX26_ENV)
30 static int
31 afs_setgroups(cred_t **cr, struct group_info *group_info, int change_parent)
32 {
33     struct group_info *old_info;
34
35     AFS_STATCNT(afs_setgroups);
36
37     old_info = (*cr)->cr_group_info;
38     get_group_info(group_info);
39     (*cr)->cr_group_info = group_info;
40     put_group_info(old_info);
41
42     crset(*cr);
43
44     if (change_parent) {
45         old_info = current->parent->group_info;
46         get_group_info(group_info);
47         current->parent->group_info = group_info;
48         put_group_info(old_info);
49     }
50
51     return (0);
52 }
53 #else
54 static int
55 afs_setgroups(cred_t **cr, int ngroups, gid_t * gidset, int change_parent)
56 {
57     int ngrps;
58     int i;
59     gid_t *gp;
60
61     AFS_STATCNT(afs_setgroups);
62
63     if (ngroups > NGROUPS)
64         return EINVAL;
65
66     gp = (*cr)->cr_groups;
67     if (ngroups < NGROUPS)
68         gp[ngroups] = (gid_t) NOGROUP;
69
70     for (i = ngroups; i > 0; i--) {
71         *gp++ = *gidset++;
72     }
73
74     (*cr)->cr_ngroups = ngroups;
75     crset(*cr);
76     return (0);
77 }
78 #endif
79
80 #if defined(AFS_LINUX26_ENV)
81 static struct group_info *
82 afs_getgroups(cred_t * cr)
83 {
84     AFS_STATCNT(afs_getgroups);
85
86     get_group_info(cr->cr_group_info);
87     return cr->cr_group_info;
88 }
89 #else
90 /* Returns number of groups. And we trust groups to be large enough to
91  * hold all the groups.
92  */
93 static int
94 afs_getgroups(cred_t *cr, gid_t *groups)
95 {
96     int i;
97     int n;
98     gid_t *gp;
99
100     AFS_STATCNT(afs_getgroups);
101
102     gp = cr->cr_groups;
103     n = cr->cr_ngroups;
104
105     for (i = 0; (i < n) && (*gp != (gid_t) NOGROUP); i++)
106         *groups++ = *gp++;
107     return i;
108 }
109 #endif
110
111 #if !defined(AFS_LINUX26_ENV)
112 /* Only propogate the PAG to the parent process. Unix's propogate to 
113  * all processes sharing the cred.
114  */
115 int
116 set_pag_in_parent(int pag, int g0, int g1)
117 {
118     int i;
119 #ifdef STRUCT_TASK_STRUCT_HAS_PARENT
120     gid_t *gp = current->parent->groups;
121     int ngroups = current->parent->ngroups;
122 #else
123     gid_t *gp = current->p_pptr->groups;
124     int ngroups = current->p_pptr->ngroups;
125 #endif
126
127     if ((ngroups < 2) || (afs_get_pag_from_groups(gp[0], gp[1]) == NOPAG)) {
128         /* We will have to shift grouplist to make room for pag */
129         if (ngroups + 2 > NGROUPS) {
130             return EINVAL;
131         }
132         for (i = ngroups - 1; i >= 0; i--) {
133             gp[i + 2] = gp[i];
134         }
135         ngroups += 2;
136     }
137     gp[0] = g0;
138     gp[1] = g1;
139     if (ngroups < NGROUPS)
140         gp[ngroups] = NOGROUP;
141
142 #ifdef STRUCT_TASK_STRUCT_HAS_PARENT
143     current->parent->ngroups = ngroups;
144 #else
145     current->p_pptr->ngroups = ngroups;
146 #endif
147     return 0;
148 }
149 #endif
150
151 int
152 setpag(cred_t ** cr, afs_uint32 pagvalue, afs_uint32 * newpag,
153        int change_parent)
154 {
155 #if defined(AFS_LINUX26_ENV)
156     struct group_info *group_info;
157     gid_t g0, g1;
158     struct group_info *tmp;
159     int i;
160     int need_space = 0;
161
162     AFS_STATCNT(setpag);
163
164     group_info = afs_getgroups(*cr);
165     if (group_info->ngroups < 2
166         ||  afs_get_pag_from_groups(GROUP_AT(group_info, 0),
167                                     GROUP_AT(group_info, 1)) == NOPAG) 
168         /* We will have to make sure group_info is big enough for pag */
169       need_space = 2;
170
171     tmp = groups_alloc(group_info->ngroups + need_space);
172     
173     for (i = 0; i < group_info->ngroups; ++i)
174       GROUP_AT(tmp, i + need_space) = GROUP_AT(group_info, i);
175     put_group_info(group_info);
176     group_info = tmp;
177
178     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
179     afs_get_groups_from_pag(*newpag, &g0, &g1);
180     GROUP_AT(group_info, 0) = g0;
181     GROUP_AT(group_info, 1) = g1;
182
183     afs_setgroups(cr, group_info, change_parent);
184
185     put_group_info(group_info);
186
187     return 0;
188 #else
189     gid_t *gidset;
190     afs_int32 ngroups, code = 0;
191     int j;
192
193     AFS_STATCNT(setpag);
194
195     gidset = (gid_t *) osi_Alloc(NGROUPS * sizeof(gidset[0]));
196     ngroups = afs_getgroups(*cr, gidset);
197
198     if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
199         /* We will have to shift grouplist to make room for pag */
200         if (ngroups + 2 > NGROUPS) {
201             osi_Free((char *)gidset, NGROUPS * sizeof(int));
202             return EINVAL;
203         }
204         for (j = ngroups - 1; j >= 0; j--) {
205             gidset[j + 2] = gidset[j];
206         }
207         ngroups += 2;
208     }
209     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
210     afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
211     code = afs_setgroups(cr, ngroups, gidset, change_parent);
212
213     /* If change_parent is set, then we should set the pag in the parent as
214      * well.
215      */
216     if (change_parent && !code) {
217         code = set_pag_in_parent(*newpag, gidset[0], gidset[1]);
218     }
219
220     osi_Free((char *)gidset, NGROUPS * sizeof(int));
221     return code;
222 #endif
223 }
224
225
226 /* Intercept the standard system call. */
227 extern asmlinkage long (*sys_setgroupsp) (int gidsetsize, gid_t * grouplist);
228 asmlinkage long
229 afs_xsetgroups(int gidsetsize, gid_t * grouplist)
230 {
231     long code;
232     cred_t *cr = crref();
233     afs_uint32 junk;
234     int old_pag;
235
236     lock_kernel();
237     old_pag = PagInCred(cr);
238     crfree(cr);
239     unlock_kernel();
240
241     code = (*sys_setgroupsp) (gidsetsize, grouplist);
242     if (code) {
243         return code;
244     }
245
246     lock_kernel();
247     cr = crref();
248     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
249         /* re-install old pag if there's room. */
250         code = setpag(&cr, old_pag, &junk, 0);
251     }
252     crfree(cr);
253     unlock_kernel();
254
255     /* Linux syscall ABI returns errno as negative */
256     return (-code);
257 }
258
259 #if defined(AFS_LINUX24_ENV)
260 /* Intercept the standard uid32 system call. */
261 extern asmlinkage long (*sys_setgroups32p) (int gidsetsize, gid_t * grouplist);
262 asmlinkage long
263 afs_xsetgroups32(int gidsetsize, gid_t * grouplist)
264 {
265     long code;
266     cred_t *cr = crref();
267     afs_uint32 junk;
268     int old_pag;
269
270     lock_kernel();
271     old_pag = PagInCred(cr);
272     crfree(cr);
273     unlock_kernel();
274
275     code = (*sys_setgroups32p) (gidsetsize, grouplist);
276
277     if (code) {
278         return code;
279     }
280
281     lock_kernel();
282     cr = crref();
283     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
284         /* re-install old pag if there's room. */
285         code = setpag(&cr, old_pag, &junk, 0);
286     }
287     crfree(cr);
288     unlock_kernel();
289
290     /* Linux syscall ABI returns errno as negative */
291     return (-code);
292 }
293 #endif
294
295 #if defined(AFS_PPC64_LINUX20_ENV)
296 /* Intercept the uid16 system call as used by 32bit programs. */
297 extern long (*sys32_setgroupsp)(int gidsetsize, gid_t *grouplist);
298 asmlinkage long afs32_xsetgroups(int gidsetsize, gid_t *grouplist)
299 {
300     long code;
301     cred_t *cr = crref();
302     afs_uint32 junk;
303     int old_pag;
304     
305     lock_kernel();
306     old_pag = PagInCred(cr);
307     crfree(cr);
308     unlock_kernel();
309     
310     code = (*sys32_setgroupsp)(gidsetsize, grouplist);
311     if (code) {
312         return code;
313     }
314     
315     lock_kernel();
316     cr = crref();
317     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
318         /* re-install old pag if there's room. */
319         code = setpag(&cr, old_pag, &junk, 0);
320     }
321     crfree(cr);
322     unlock_kernel();
323     
324     /* Linux syscall ABI returns errno as negative */
325     return (-code);
326 }
327 #endif
328
329 #if defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_AMD64_LINUX20_ENV)
330 /* Intercept the uid16 system call as used by 32bit programs. */
331 extern long (*sys32_setgroupsp) (int gidsetsize, u16 * grouplist);
332 asmlinkage long
333 afs32_xsetgroups(int gidsetsize, u16 * grouplist)
334 {
335     long code;
336     cred_t *cr = crref();
337     afs_uint32 junk;
338     int old_pag;
339     
340     lock_kernel();
341     old_pag = PagInCred(cr);
342     crfree(cr);
343     unlock_kernel();
344     
345     code = (*sys32_setgroupsp) (gidsetsize, grouplist);
346     if (code) {
347         return code;
348     }
349     
350     lock_kernel();
351     cr = crref();
352     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
353         /* re-install old pag if there's room. */
354         code = setpag(&cr, old_pag, &junk, 0);
355     }
356     crfree(cr);
357     unlock_kernel();
358     
359     /* Linux syscall ABI returns errno as negative */
360     return (-code);
361 }
362
363 #ifdef AFS_LINUX24_ENV
364 /* Intercept the uid32 system call as used by 32bit programs. */
365 extern long (*sys32_setgroups32p) (int gidsetsize, gid_t * grouplist);
366 asmlinkage long
367 afs32_xsetgroups32(int gidsetsize, gid_t * grouplist)
368 {
369     long code;
370     cred_t *cr = crref();
371     afs_uint32 junk;
372     int old_pag;
373
374     lock_kernel();
375     old_pag = PagInCred(cr);
376     crfree(cr);
377     unlock_kernel();
378
379     code = (*sys32_setgroups32p) (gidsetsize, grouplist);
380     if (code) {
381         return code;
382     }
383
384     lock_kernel();
385     cr = crref();
386     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
387         /* re-install old pag if there's room. */
388         code = setpag(&cr, old_pag, &junk, 0);
389     }
390     crfree(cr);
391     unlock_kernel();
392
393     /* Linux syscall ABI returns errno as negative */
394     return (-code);
395 }
396 #endif
397 #endif
398