無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

setgroupsシステムコール


int gidsetsizeはグループID数で、gid_t grouplist[]はgidsetsize個のグループIDです。設定できる最大グループID数は、NGROUPS_MAX=65536(グループID=0を含めて16ビット内のグループID数)です。

groups_alloc()でgidsetsizeに応じたstruct group_info group_infoを取得し、引数のgrouplistを複写し、current->cred->group_info = group_infoとし、プロセスが引数のグループ群に属する事になります。この時、groups_search()でグループIDの検索の効率化のため、groups_sort()でgroup_info内のグループIDを昇順に並び替えます。

ngroupsはグループID数、nblocksはblocks[]の動的に割り当てるPAGE単位ブロックの配列数です。nblocks<NGROUPS_SMALLなら、small_block[]に設定されます。
struct group_info {
       atomic_t        usage;
       int             ngroups;
       int             nblocks;
       gid_t           small_block[NGROUPS_SMALL];
       gid_t           *blocks[0];
};

#define NGROUPS_MAX    65536  
#define NGROUPS_SMALL           32

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;
}
グループIDがNGROUPS_SMALL以下の場合、small_block[NGROUPS_SMALL]に割り当てgroup_info->blocks[0] = small_block、そうでないならPAGE単位で動的に割り当てgroup_info->blocks[i] = bとします。
#define NGROUPS_PER_BLOCK       ((unsigned int)(PAGE_SIZE / sizeof(gid_t)))

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;
       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;
}

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);
}

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;
}
ファイル参照時グループに掛かるチェックでコールされ、inodeのgipがcurrent->cred->group_infoに有しているかチェックします。group_info->blocks[0]から順に走査するのでなく、検索グループ数の半分の位置のIDより大きければ上位半分を、小さければ下位半分を、同じように1/2単位での検索する効率的な実装です。故ユーザ引数のグループを並び替えしてプロセスに設定しているわけです。
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;
}
i / NGROUPS_PER_BLOCKはブロック位置、i % NGROUPS_PER_BLOCKはそのブロックバッファ内の位置
#define GROUP_AT(gi, i) \
       ((gi)->blocks[(i) / NGROUPS_PER_BLOCK][(i) % NGROUPS_PER_BLOCK])

補足

グループID引数の型gid_tは、アーキテクチャに依存せず、32ビットとなります。intだとアーキテクチャにより32/64ビットとなってしまいます。


最終更新 2015/07/20 18:04:00 - north
(2015/07/20 17:59:35 作成)