linux-group-syscalls-return-long-20030521
[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("$Header$");
20
21 #include "afs/sysincludes.h"
22 #include "afsincludes.h"
23 #include "afs/afs_stats.h"  /* statistics */
24 #ifdef AFS_LINUX22_ENV
25 #include "h/smp_lock.h"
26 #endif
27
28 static int afs_getgroups(cred_t *cr, gid_t *groups);
29 static int afs_setgroups(cred_t **cr, int ngroups, gid_t *gidset, int change_parent);
30
31 /* Only propogate the PAG to the parent process. Unix's propogate to 
32  * all processes sharing the cred.
33  */
34 int set_pag_in_parent(int pag, int g0, int g1)
35 {
36 #ifdef STRUCT_TASK_STRUCT_HAS_PARENT
37     gid_t *gp = current->parent->groups;
38 #else
39     gid_t *gp = current->p_pptr->groups;
40 #endif
41     int ngroups;
42     int i;
43
44     
45 #ifdef STRUCT_TASK_STRUCT_HAS_PARENT
46     ngroups = current->parent->ngroups;
47 #else
48     ngroups = current->p_pptr->ngroups;
49 #endif
50
51     if ((ngroups < 2) || (afs_get_pag_from_groups(gp[0], gp[1]) == NOPAG)) {
52         /* We will have to shift grouplist to make room for pag */
53         if (ngroups + 2 > NGROUPS) {
54             return EINVAL;
55         }
56         for (i = ngroups-1; i >= 0; i--) {
57             gp[i+2] = gp[i];
58         }
59         ngroups += 2;
60     }
61     gp[0] = g0;
62     gp[1] = g1;
63     if (ngroups < NGROUPS)
64         gp[ngroups] = NOGROUP;
65
66 #ifdef STRUCT_TASK_STRUCT_HAS_PARENT
67     current->parent->ngroups = ngroups;
68 #else
69     current->p_pptr->ngroups = ngroups;
70 #endif
71     return 0;
72 }
73
74 int setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag, int change_parent)
75 {
76     gid_t *gidset;
77     afs_int32 ngroups, code = 0;
78     int j;
79
80     AFS_STATCNT(setpag);
81
82     gidset = (gid_t *) osi_Alloc(NGROUPS*sizeof(gidset[0]));
83     ngroups = afs_getgroups(*cr, gidset);
84
85     if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
86         /* We will have to shift grouplist to make room for pag */
87         if (ngroups + 2 > NGROUPS) {
88             osi_Free((char *)gidset, NGROUPS*sizeof(int));
89             return EINVAL;
90         }
91         for (j = ngroups - 1; j >= 0; j--) {
92             gidset[j+2] = gidset[j];
93         }
94         ngroups += 2;
95     }
96     *newpag = (pagvalue == -1 ? genpag(): pagvalue);
97     afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
98     code = afs_setgroups(cr, ngroups, gidset, change_parent);
99
100     /* If change_parent is set, then we should set the pag in the parent as
101      * well.
102      */
103     if (change_parent && !code) {
104         code = set_pag_in_parent(*newpag, gidset[0], gidset[1]);
105     }
106
107     osi_Free((char *)gidset, NGROUPS*sizeof(int));
108     return code;
109 }
110
111
112 /* Intercept the standard system call. XXX take old_gid_t in new kernels */
113 extern long (*sys_setgroupsp)(int gidsetsize, gid_t *grouplist);
114 asmlinkage long afs_xsetgroups(int gidsetsize, gid_t *grouplist)
115 {
116     long code;
117     cred_t *cr = crref();
118     afs_uint32 junk;
119     int old_pag;
120
121     lock_kernel();
122     old_pag = PagInCred(cr);
123     crfree(cr);
124     unlock_kernel();
125
126     code = (*sys_setgroupsp)(gidsetsize, grouplist);
127     if (code) {
128         return code;
129     }
130
131     lock_kernel();
132     cr = crref();
133     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
134         /* re-install old pag if there's room. */
135         code = setpag(&cr, old_pag, &junk, 0);
136     }
137     crfree(cr);
138     unlock_kernel();
139
140     /* Linux syscall ABI returns errno as negative */
141     return (- code);
142 }
143
144 #if defined(AFS_LINUX24_ENV)
145 /* Intercept the standard uid32 system call. */
146 extern long (*sys_setgroups32p)(int gidsetsize, gid_t *grouplist);
147 asmlinkage long afs_xsetgroups32(int gidsetsize, gid_t *grouplist)
148 {
149     long code;
150     cred_t *cr = crref();
151     afs_uint32 junk;
152     int old_pag;
153     
154     lock_kernel();
155     old_pag = PagInCred(cr);
156     crfree(cr);
157     unlock_kernel();
158
159     code = (*sys_setgroups32p)(gidsetsize, grouplist);
160     if (code) {
161         return code;
162     }
163
164     lock_kernel();
165     cr = crref();
166     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
167         /* re-install old pag if there's room. */
168         code = setpag(&cr, old_pag, &junk, 0);
169     }
170     crfree(cr);
171     unlock_kernel();
172
173     /* Linux syscall ABI returns errno as negative */
174     return (- code);
175 }
176 #endif
177
178 #if defined(AFS_SPARC64_LINUX20_ENV)
179 /* Intercept the uid16 system call as used by 32bit programs. */
180 extern int (*sys32_setgroupsp)(int gidsetsize, __kernel_gid_t32 *grouplist);
181 asmlinkage int afs32_xsetgroups(int gidsetsize, __kernel_gid_t32 *grouplist)
182 {
183     int code;
184     cred_t *cr = crref();
185     afs_uint32 junk;
186     int old_pag;
187
188     lock_kernel();
189     old_pag = PagInCred(cr);
190     crfree(cr);
191     unlock_kernel();
192
193     code = (*sys32_setgroupsp)(gidsetsize, grouplist);
194     if (code) {
195         return code;
196     }
197
198     lock_kernel();
199     cr = crref();
200     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
201         /* re-install old pag if there's room. */
202         code = setpag(&cr, old_pag, &junk, 0);
203     }
204     crfree(cr);
205     unlock_kernel();
206
207     /* Linux syscall ABI returns errno as negative */
208     return (- code);
209 }
210 #ifdef AFS_LINUX24_ENV
211 /* Intercept the uid32 system call as used by 32bit programs. */
212 extern int (*sys32_setgroups32p)(int gidsetsize, __kernel_gid_t32 *grouplist);
213 asmlinkage int afs32_xsetgroups32(int gidsetsize, __kernel_gid_t32 *grouplist)
214 {
215     int code;
216     cred_t *cr = crref();
217     afs_uint32 junk;
218     int old_pag;
219
220     lock_kernel();
221     old_pag = PagInCred(cr);
222     crfree(cr);
223     unlock_kernel();
224
225     code = (*sys32_setgroups32p)(gidsetsize, grouplist);
226     if (code) {
227         return code;
228     }
229
230     lock_kernel();
231     cr = crref();
232     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
233         /* re-install old pag if there's room. */
234         code = setpag(&cr, old_pag, &junk, 0);
235     }
236     crfree(cr);
237     unlock_kernel();
238
239     /* Linux syscall ABI returns errno as negative */
240     return (- code);
241 }
242 #endif
243 #endif
244
245 static int afs_setgroups(cred_t **cr, int ngroups, gid_t *gidset, int change_parent)
246 {
247     int ngrps;
248     int i;
249     gid_t *gp;
250
251     AFS_STATCNT(afs_setgroups);
252
253     if (ngroups > NGROUPS)
254         return EINVAL;
255
256     gp = (*cr)->cr_groups;
257     if (ngroups < NGROUPS)
258         gp[ngroups] = (gid_t)NOGROUP;
259
260     for (i = ngroups; i > 0; i--) {
261         *gp++ = *gidset++;
262     }
263
264     (*cr)->cr_ngroups = ngroups;
265     crset(*cr);
266     return (0);
267 }
268
269 /* Returns number of groups. And we trust groups to be large enough to
270  * hold all the groups.
271  */
272 static int afs_getgroups(cred_t *cr, gid_t *groups)
273 {
274     int i;
275     gid_t *gp = cr->cr_groups;
276     int n = cr->cr_ngroups;
277     AFS_STATCNT(afs_getgroups);
278
279     for (i = 0; (i < n) && (*gp != (gid_t)NOGROUP); i++) {
280         *groups++ = *gp++;
281     }
282     return i;
283 }
284