[size=medium]struct group_info init_groups = { .usage = ATOMIC_INIT(2) };[/size]
[size=medium]struct group_info *groups_alloc(int gidsetsize){[/size]
[size=medium] struct group_info *group_info;[/size]
[size=medium] int nblocks;[/size]
[size=medium] int i;[/size]
[size=medium] nblocks = (gidsetsize + NGROUPS_PER_BLOCK - 1) / NGROUPS_PER_BLOCK;[/size]
[size=medium] /* Make sure we always allocate at least one indirect block pointer */[/size]
[size=medium] nblocks = nblocks ? : 1;[/size]
[size=medium] group_info = kmalloc(sizeof(*group_info) + nblocks*sizeof(gid_t *), GFP_USER);[/size]
[size=medium] if (!group_info)[/size]
[size=medium] return NULL;[/size]
[size=medium] group_info->ngroups = gidsetsize;[/size]
[size=medium] group_info->nblocks = nblocks;[/size]
[size=medium] atomic_set(&group_info->usage, 1);[/size]
[size=medium] if (gidsetsize blocks[0] = group_info->small_block;[/size]
[size=medium] else {[/size]
[size=medium] for (i = 0; i blocks = b;[/size]
[size=medium] }[/size]
[size=medium] }[/size]
[size=medium] return group_info;[/size]
[size=medium]out_undo_partial_alloc:[/size]
[size=medium] while (–i >= 0) {[/size]
[size=medium] free_page((unsigned long)group_info->blocks);[/size]
[size=medium] }[/size]
[size=medium] kfree(group_info);[/size]
[size=medium] return NULL;[/size]
[size=medium]}[/size]
[size=medium]EXPORT_SYMBOL(groups_alloc);[/size]
[size=medium]void groups_free(struct group_info *group_info)[/size]
[size=medium]{[/size]
[size=medium] if (group_info->blocks[0] != group_info->small_block) {[/size]
[size=medium] int i;[/size]
[size=medium] for (i = 0; i nblocks; i++)[/size]
[size=medium] free_page((unsigned long)group_info->blocks);[/size]
[size=medium] }[/size]
[size=medium] kfree(group_info);[/size]
[size=medium]}[/size]
[size=medium]EXPORT_SYMBOL(groups_free);[/size]
[size=medium]/* export the group_info to a user-space array */[/size]
[size=medium]static int groups_to_user(gid_t __user *grouplist,[/size]
[size=medium] const struct group_info *group_info)[/size]
[size=medium]{[/size]
[size=medium] int i;[/size]
[size=medium] unsigned int count = group_info->ngroups;[/size]
[size=medium] for (i = 0; i nblocks; i++) {[/size]
[size=medium] unsigned int cp_count = min(NGROUPS_PER_BLOCK, count);[/size]
[size=medium] unsigned int len = cp_count * sizeof(*grouplist);[/size]
[size=medium] if (copy_to_user(grouplist, group_info->blocks, len))[/size]
[size=medium] return -EFAULT;[/size]
[size=medium] grouplist += NGROUPS_PER_BLOCK;[/size]
[size=medium] count -= cp_count;[/size]
[size=medium] }[/size]
[size=medium] return 0;[/size]
[size=medium]}[/size]
[size=medium]/* fill a group_info from a user-space array - it must be allocated already */[/size]
[size=medium]static int groups_from_user(struct group_info *group_info,[/size]
[size=medium] gid_t __user *grouplist)[/size]
[size=medium]{[/size]
[size=medium] int i;[/size]
[size=medium] unsigned int count = group_info->ngroups;[/size]
[size=medium] for (i = 0; i nblocks; i++) {[/size]
[size=medium] unsigned int cp_count = min(NGROUPS_PER_BLOCK, count);[/size]
[size=medium] unsigned int len = cp_count * sizeof(*grouplist);[/size]
[size=medium] if (copy_from_user(group_info->blocks, grouplist, len))[/size]
[size=medium] return -EFAULT;[/size]
[size=medium] grouplist += NGROUPS_PER_BLOCK;[/size]
[size=medium] count -= cp_count;[/size]
[size=medium] }[/size]
[size=medium] return 0;[/size]
[size=medium]}[/size]
[size=medium]/* a simple Shell sort */[/size]
[size=medium]static void groups_sort(struct group_info *group_info)[/size]
[size=medium]{[/size]
[size=medium] int base, max, stride;[/size]
[size=medium] in[/size]