linux24-setgroups32-support-20010329
[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 "../afs/param.h"
17 #include "../afs/sysincludes.h"
18 #include "../afs/afsincludes.h"
19 #include "../afs/afs_stats.h"  /* statistics */
20 #ifdef AFS_LINUX22_ENV
21 #include "../h/smp_lock.h"
22 #endif
23
24 static int afs_getgroups(cred_t *cr, gid_t *groups);
25 static int afs_setgroups(cred_t **cr, int ngroups, gid_t *gidset, int change_parent);
26
27 /* Only propogate the PAG to the parent process. Unix's propogate to 
28  * all processes sharing the cred.
29  */
30 int set_pag_in_parent(int pag, int g0, int g1)
31 {
32     gid_t *gp = current->p_pptr->groups;
33     int ngroups;
34     int i;
35
36     
37     ngroups = current->p_pptr->ngroups;
38     gp = current->p_pptr->groups;
39
40
41     if (afs_get_pag_from_groups(gp[0], gp[1]) == NOPAG) {
42         /* We will have to shift grouplist to make room for pag */
43         if (ngroups + 2 > NGROUPS) {
44             return EINVAL;
45         }
46         for (i = ngroups-1; i >= 0; i--) {
47             gp[i+2] = gp[i];
48         }
49         ngroups += 2;
50     }
51     gp[0] = g0;
52     gp[1] = g1;
53     if (ngroups < NGROUPS)
54         gp[ngroups] = NOGROUP;
55
56     current->p_pptr->ngroups = ngroups;
57     return 0;
58 }
59
60 int setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag, int change_parent)
61 {
62     gid_t *gidset;
63     afs_int32 ngroups, code = 0;
64     int j;
65
66     AFS_STATCNT(setpag);
67
68     gidset = (gid_t *) osi_Alloc(NGROUPS*sizeof(gidset[0]));
69     ngroups = afs_getgroups(*cr, gidset);
70
71     if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
72         /* We will have to shift grouplist to make room for pag */
73         if (ngroups + 2 > NGROUPS) {
74             osi_Free((char *)gidset, NGROUPS*sizeof(int));
75             return EINVAL;
76         }
77         for (j = ngroups - 1; j >= 0; j--) {
78             gidset[j+2] = gidset[j];
79         }
80         ngroups += 2;
81     }
82     *newpag = (pagvalue == -1 ? genpag(): pagvalue);
83     afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
84     code = afs_setgroups(cr, ngroups, gidset, change_parent);
85
86     /* If change_parent is set, then we should set the pag in the parent as
87      * well.
88      */
89     if (change_parent && !code) {
90         code = set_pag_in_parent(*newpag, gidset[0], gidset[1]);
91     }
92
93     osi_Free((char *)gidset, NGROUPS*sizeof(int));
94     return code;
95 }
96
97
98 /* Intercept the standard system call. */
99 extern int (*sys_setgroupsp)(int gidsetsize, gid_t *grouplist);
100 asmlinkage int afs_xsetgroups(int gidsetsize, gid_t *grouplist)
101 {
102     int code;
103     cred_t *cr = crref();
104     afs_uint32 junk;
105     int old_pag;
106
107     lock_kernel();
108     old_pag = PagInCred(cr);
109     crfree(cr);
110     unlock_kernel();
111
112     code = (*sys_setgroupsp)(gidsetsize, grouplist);
113     if (code) {
114         return code;
115     }
116
117     lock_kernel();
118     cr = crref();
119     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
120         /* re-install old pag if there's room. */
121         code = setpag(&cr, old_pag, &junk, 0);
122     }
123     crfree(cr);
124     unlock_kernel();
125
126     return code;
127 }
128
129 #if defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_LINUX24_ENV)
130 #if defined(AFS_SPARC64_LINUX20_ENV)
131 asmlinkage int afs_xsetgroups32(int gidsetsize, __kernel_gid_t32 *grouplist)
132 #else
133 asmlinkage int afs_xsetgroups32(int gidsetsize, __kernel_gid32_t *grouplist)
134 #endif
135 {
136     gid_t gl[NGROUPS];
137     int ret, i;
138     mm_segment_t old_fs = get_fs ();
139
140     if ((unsigned) gidsetsize > NGROUPS)
141         return -EINVAL;
142     for (i = 0; i < gidsetsize; i++, grouplist++)
143         if (__get_user (gl[i], grouplist))
144             return -EFAULT;
145     set_fs (KERNEL_DS);
146     ret = afs_xsetgroups(gidsetsize, gl);
147     set_fs (old_fs);
148     return ret;
149 }
150 #endif
151
152 static int afs_setgroups(cred_t **cr, int ngroups, gid_t *gidset, int change_parent)
153 {
154     int ngrps;
155     int i;
156     gid_t *gp;
157
158     AFS_STATCNT(afs_setgroups);
159
160     if (ngroups > NGROUPS)
161         return EINVAL;
162
163     gp = (*cr)->cr_groups;
164     if (ngroups < NGROUPS)
165         gp[ngroups] = (gid_t)NOGROUP;
166
167     for (i = ngroups; i > 0; i--) {
168         *gp++ = *gidset++;
169     }
170
171     (*cr)->cr_ngroups = ngroups;
172     crset(*cr);
173     return (0);
174 }
175
176 /* Returns number of groups. And we trust groups to be large enough to
177  * hold all the groups.
178  */
179 static int afs_getgroups(cred_t *cr, gid_t *groups)
180 {
181     int i;
182     gid_t *gp = cr->cr_groups;
183     int n = cr->cr_ngroups;
184     AFS_STATCNT(afs_getgroups);
185
186     for (i = 0; (i < n) && (*gp != (gid_t)NOGROUP); i++) {
187         *groups++ = *gp++;
188     }
189     return i;
190 }
191