struct group_info init_groups = { .usage = ATOMIC_INIT(2) };
struct group_info *groups_alloc(int gidsetsize){ struct group_info *group_info;
int nblocks; int i;
nblocks = (gidsetsize + NGROUPS_PER_BLOCK - 1) / NGROUPS_PER_BLOCK;
/* Make sure we always allocate at least one indirect block pointer */
nblocks = nblocks ? : 1;
group_info = kmalloc(sizeof(*group_info) + nblocks*sizeof(gid_t *), GFP_USER);
if (!group_info) return NULL; group_info->ngroups = gidsetsize;
group_info->nblocks = nblocks; atomic_set(&group_info->usage, 1);
if (gidsetsize <= NGROUPS_SMALL)
group_info->blocks[0] = group_info->small_block; else {
for (i = 0; i < nblocks; i++) { gid_t *b;
b = (void *)__get_free_page(GFP_USER); if (!b)
goto out_undo_partial_alloc; group_info->blocks[i] = b; } }
return group_info;out_undo_partial_alloc: while (--i >= 0) {
free_page((unsigned long)group_info->blocks[i]); } kfree(group_info);
return NULL;}EXPORT_SYMBOL(groups_alloc);
void groups_free(struct group_info *group_info){
if (group_info->blocks[0] != group_info->small_block) { int i;
for (i = 0; i < group_info->nblocks; i++)
free_page((unsigned long)group_info->blocks[i]); } kfree(group_info);}
EXPORT_SYMBOL(groups_free);/* export the group_info to a user-space array */
static int groups_to_user(gid_t __user *grouplist,
const struct group_info *group_info){ int i;
unsigned int count = group_info->ngroups;
for (i = 0; i < group_info->nblocks; i++) {
unsigned int cp_count = min(NGROUPS_PER_BLOCK, count);
unsigned int len = cp_count * sizeof(*grouplist);
if (copy_to_user(grouplist, group_info->blocks[i], len)) return -EFAULT;
grouplist += NGROUPS_PER_BLOCK; count -= cp_count; } return 0;}
/* fill a group_info from a user-space array - it must be allocated already */
static int groups_from_user(struct group_info *group_info,
gid_t __user *grouplist){ int i;
unsigned int count = group_info->ngroups;
for (i = 0; i < group_info->nblocks; i++) {
unsigned int cp_count = min(NGROUPS_PER_BLOCK, count);
unsigned int len = cp_count * sizeof(*grouplist);
if (copy_from_user(group_info->blocks[i], grouplist, len))
return -EFAULT; grouplist += NGROUPS_PER_BLOCK; count -= cp_count; }
return 0;}/* a simple Shell sort */
static void groups_sort(struct group_info *group_info){ int base, max, stride;
int gidsetsize = group_info->ngroups;
for (stride = 1; stride < gidsetsize; stride = 3 * stride + 1)
; /* nothing */ stride /= 3; while (stride) { max = gidsetsize - stride;
for (base = 0; base < max; base++) { int left = base;
int right = left + stride; gid_t tmp = GROUP_AT(group_info, right);
while (left >= 0 && GROUP_AT(group_info, left) > tmp) {
GROUP_AT(group_info, right) = GROUP_AT(group_info, left);
right = left; left -= stride; } GROUP_AT(group_info, right) = tmp; }
stride /= 3; }}/* a simple bsearch */
int groups_search(const struct group_info *group_info, gid_t grp){
unsigned int left, right; if (!group_info) return 0; left = 0;
right = group_info->ngroups; while (left < right) {
unsigned int mid = (left+right)/2; if (grp > GROUP_AT(group_info, mid))
left = mid + 1; else if (grp < GROUP_AT(group_info, mid)) right = mid;
else return 1; } return 0;}/**
* set_groups - Change a group subion in a set of credentials
* @new: The newly prepared set of credentials to alter
* @group_info: The group list to install *
* Validate a group subion and, if valid, insert it into a set
* of credentials. */
int set_groups(struct cred *new, struct group_info *group_info){
put_group_info(new->group_info); groups_sort(group_info);
get_group_info(group_info); new->group_info = group_info; return 0;}
EXPORT_SYMBOL(set_groups);/**
* set_current_groups - Change current's group subion
* @group_info: The group list to impose *
* Validate a group subion and, if valid, impose it upon current's task
* security record. */int set_current_groups(struct group_info *group_info){
struct cred *new; int ret; new = prepare_creds(); if (!new) return -ENOMEM;
ret = set_groups(new, group_info); if (ret < 0) { abort_creds(new);
return ret; } return commit_creds(new);}EXPORT_SYMBOL(set_current_groups);
SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist){
const struct cred *cred = current_cred(); int i; if (gidsetsize < 0)
return -EINVAL; /* no need to grab task_lock here; it cannot change */
i = cred->group_info->ngroups; if (gidsetsize) {
if (i > gidsetsize) { i = -EINVAL; goto out; }
if (groups_to_user(grouplist, cred->group_info)) { i = -EFAULT;
goto out; } }out: return i;}/*
* SMP: Our groups are copy-on-write. We can set them safely
* without another task interfering. */
SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist){
struct group_info *group_info; int retval; if (!nsown_capable(CAP_SETGID))
return -EPERM; if ((unsigned)gidsetsize > NGROUPS_MAX) return -EINVAL;
group_info = groups_alloc(gidsetsize); if (!group_info) return -ENOMEM;
retval = groups_from_user(group_info, grouplist); if (retval) {
put_group_info(group_info); return retval; }
retval = set_current_groups(group_info); put_group_info(group_info);
return retval;}/*
* Check whether we're fsgid/egid or in the supplemental group.. */
int in_group_p(gid_t grp){ const struct cred *cred = current_cred();
int retval = 1; if (grp != cred->fsgid)
retval = groups_search(cred->group_info, grp); return retval;}
EXPORT_SYMBOL(in_group_p);int in_egroup_p(gid_t grp){
const struct cred *cred = current_cred(); int retval = 1;
if (grp != cred->egid) retval = groups_search(cred->group_info, grp);
return retval;}
이거 어디다 쓰는거죠?
그냥 막 쳐져있는건가요