linux-old/ipc/util.c
<<
>>
Prefs
   1/*
   2 * linux/ipc/util.c
   3 * Copyright (C) 1992 Krishna Balasubramanian
   4 *
   5 * Sep 1997 - Call suser() last after "normal" permission checks so we
   6 *            get BSD style process accounting right.
   7 *            Occurs in several places in the IPC code.
   8 *            Chris Evans, <chris@ferret.lmh.ox.ac.uk>
   9 * Nov 1999 - ipc helper functions, unified SMP locking
  10 *            Manfred Spraul <manfreds@colorfullife.com>
  11 */
  12
  13#include <linux/config.h>
  14#include <linux/mm.h>
  15#include <linux/shm.h>
  16#include <linux/init.h>
  17#include <linux/msg.h>
  18#include <linux/smp_lock.h>
  19#include <linux/vmalloc.h>
  20#include <linux/slab.h>
  21#include <linux/highuid.h>
  22
  23#if defined(CONFIG_SYSVIPC)
  24
  25#include "util.h"
  26
  27/**
  28 *      ipc_init        -       initialise IPC subsystem
  29 *
  30 *      The various system5 IPC resources (semaphores, messages and shared
  31 *      memory are initialised
  32 */
  33 
  34void __init ipc_init (void)
  35{
  36        sem_init();
  37        msg_init();
  38        shm_init();
  39        return;
  40}
  41
  42/**
  43 *      ipc_init_ids            -       initialise IPC identifiers
  44 *      @ids: Identifier set
  45 *      @size: Number of identifiers
  46 *
  47 *      Given a size for the ipc identifier range (limited below IPCMNI)
  48 *      set up the sequence range to use then allocate and initialise the
  49 *      array itself. 
  50 */
  51 
  52void __init ipc_init_ids(struct ipc_ids* ids, int size)
  53{
  54        int i;
  55        sema_init(&ids->sem,1);
  56
  57        if(size > IPCMNI)
  58                size = IPCMNI;
  59        ids->size = size;
  60        ids->in_use = 0;
  61        ids->max_id = -1;
  62        ids->seq = 0;
  63        {
  64                int seq_limit = INT_MAX/SEQ_MULTIPLIER;
  65                if(seq_limit > USHRT_MAX)
  66                        ids->seq_max = USHRT_MAX;
  67                 else
  68                        ids->seq_max = seq_limit;
  69        }
  70
  71        ids->entries = ipc_alloc(sizeof(struct ipc_id)*size);
  72
  73        if(ids->entries == NULL) {
  74                printk(KERN_ERR "ipc_init_ids() failed, ipc service disabled.\n");
  75                ids->size = 0;
  76        }
  77        ids->ary = SPIN_LOCK_UNLOCKED;
  78        for(i=0;i<ids->size;i++)
  79                ids->entries[i].p = NULL;
  80}
  81
  82/**
  83 *      ipc_findkey     -       find a key in an ipc identifier set     
  84 *      @ids: Identifier set
  85 *      @key: The key to find
  86 *
  87 *      Returns the identifier if found or -1 if not.
  88 */
  89 
  90int ipc_findkey(struct ipc_ids* ids, key_t key)
  91{
  92        int id;
  93        struct kern_ipc_perm* p;
  94
  95        for (id = 0; id <= ids->max_id; id++) {
  96                p = ids->entries[id].p;
  97                if(p==NULL)
  98                        continue;
  99                if (key == p->key)
 100                        return id;
 101        }
 102        return -1;
 103}
 104
 105static int grow_ary(struct ipc_ids* ids, int newsize)
 106{
 107        struct ipc_id* new;
 108        struct ipc_id* old;
 109        int i;
 110
 111        if(newsize > IPCMNI)
 112                newsize = IPCMNI;
 113        if(newsize <= ids->size)
 114                return newsize;
 115
 116        new = ipc_alloc(sizeof(struct ipc_id)*newsize);
 117        if(new == NULL)
 118                return ids->size;
 119        memcpy(new, ids->entries, sizeof(struct ipc_id)*ids->size);
 120        for(i=ids->size;i<newsize;i++) {
 121                new[i].p = NULL;
 122        }
 123        spin_lock(&ids->ary);
 124
 125        old = ids->entries;
 126        ids->entries = new;
 127        i = ids->size;
 128        ids->size = newsize;
 129        spin_unlock(&ids->ary);
 130        ipc_free(old, sizeof(struct ipc_id)*i);
 131        return ids->size;
 132}
 133
 134/**
 135 *      ipc_addid       -       add an IPC identifier
 136 *      @ids: IPC identifier set
 137 *      @new: new IPC permission set
 138 *      @size: new size limit for the id array
 139 *
 140 *      Add an entry 'new' to the IPC arrays. The permissions object is
 141 *      initialised and the first free entry is set up and the id assigned
 142 *      is returned. The list is returned in a locked state on success.
 143 *      On failure the list is not locked and -1 is returned.
 144 */
 145 
 146int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
 147{
 148        int id;
 149
 150        size = grow_ary(ids,size);
 151        for (id = 0; id < size; id++) {
 152                if(ids->entries[id].p == NULL)
 153                        goto found;
 154        }
 155        return -1;
 156found:
 157        ids->in_use++;
 158        if (id > ids->max_id)
 159                ids->max_id = id;
 160
 161        new->cuid = new->uid = current->euid;
 162        new->gid = new->cgid = current->egid;
 163
 164        new->seq = ids->seq++;
 165        if(ids->seq > ids->seq_max)
 166                ids->seq = 0;
 167
 168        spin_lock(&ids->ary);
 169        ids->entries[id].p = new;
 170        return id;
 171}
 172
 173/**
 174 *      ipc_rmid        -       remove an IPC identifier
 175 *      @ids: identifier set
 176 *      @id: Identifier to remove
 177 *
 178 *      The identifier must be valid, and in use. The kernel will panic if
 179 *      fed an invalid identifier. The entry is removed and internal
 180 *      variables recomputed. The object associated with the identifier
 181 *      is returned.
 182 */
 183 
 184struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
 185{
 186        struct kern_ipc_perm* p;
 187        int lid = id % SEQ_MULTIPLIER;
 188        if(lid >= ids->size)
 189                BUG();
 190        p = ids->entries[lid].p;
 191        ids->entries[lid].p = NULL;
 192        if(p==NULL)
 193                BUG();
 194        ids->in_use--;
 195
 196        if (lid == ids->max_id) {
 197                do {
 198                        lid--;
 199                        if(lid == -1)
 200                                break;
 201                } while (ids->entries[lid].p == NULL);
 202                ids->max_id = lid;
 203        }
 204        return p;
 205}
 206
 207/**
 208 *      ipc_alloc       -       allocate ipc space
 209 *      @size: size desired
 210 *
 211 *      Allocate memory from the appropriate pools and return a pointer to it.
 212 *      NULL is returned if the allocation fails
 213 */
 214 
 215void* ipc_alloc(int size)
 216{
 217        void* out;
 218        if(size > PAGE_SIZE)
 219                out = vmalloc(size);
 220        else
 221                out = kmalloc(size, GFP_KERNEL);
 222        return out;
 223}
 224
 225/**
 226 *      ipc_free        -       free ipc space
 227 *      @ptr: pointer returned by ipc_alloc
 228 *      @size: size of block
 229 *
 230 *      Free a block created with ipc_alloc. The caller must know the size
 231 *      used in the allocation call.
 232 */
 233 
 234void ipc_free(void* ptr, int size)
 235{
 236        if(size > PAGE_SIZE)
 237                vfree(ptr);
 238        else
 239                kfree(ptr);
 240}
 241
 242/**
 243 *      ipcperms        -       check IPC permissions
 244 *      @ipcp: IPC permission set
 245 *      @flag: desired permission set.
 246 *
 247 *      Check user, group, other permissions for access
 248 *      to ipc resources. return 0 if allowed
 249 */
 250 
 251int ipcperms (struct kern_ipc_perm *ipcp, short flag)
 252{       /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
 253        int requested_mode, granted_mode;
 254
 255        requested_mode = (flag >> 6) | (flag >> 3) | flag;
 256        granted_mode = ipcp->mode;
 257        if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
 258                granted_mode >>= 6;
 259        else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
 260                granted_mode >>= 3;
 261        /* is there some bit set in requested_mode but not in granted_mode? */
 262        if ((requested_mode & ~granted_mode & 0007) && 
 263            !capable(CAP_IPC_OWNER))
 264                return -1;
 265
 266        return 0;
 267}
 268
 269/*
 270 * Functions to convert between the kern_ipc_perm structure and the
 271 * old/new ipc_perm structures
 272 */
 273
 274/**
 275 *      kernel_to_ipc64_perm    -       convert kernel ipc permissions to user
 276 *      @in: kernel permissions
 277 *      @out: new style IPC permissions
 278 *
 279 *      Turn the kernel object 'in' into a set of permissions descriptions
 280 *      for returning to userspace (out).
 281 */
 282 
 283
 284void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
 285{
 286        out->key        = in->key;
 287        out->uid        = in->uid;
 288        out->gid        = in->gid;
 289        out->cuid       = in->cuid;
 290        out->cgid       = in->cgid;
 291        out->mode       = in->mode;
 292        out->seq        = in->seq;
 293}
 294
 295/**
 296 *      ipc64_perm_to_ipc_perm  -       convert old ipc permissions to new
 297 *      @in: new style IPC permissions
 298 *      @out: old style IPC permissions
 299 *
 300 *      Turn the new style permissions object in into a compatibility
 301 *      object and store it into the 'out' pointer.
 302 */
 303 
 304void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
 305{
 306        out->key        = in->key;
 307        out->uid        = NEW_TO_OLD_UID(in->uid);
 308        out->gid        = NEW_TO_OLD_GID(in->gid);
 309        out->cuid       = NEW_TO_OLD_UID(in->cuid);
 310        out->cgid       = NEW_TO_OLD_GID(in->cgid);
 311        out->mode       = in->mode;
 312        out->seq        = in->seq;
 313}
 314
 315#if !defined(__ia64__) && !defined(__hppa__)
 316
 317/**
 318 *      ipc_parse_version       -       IPC call version
 319 *      @cmd: pointer to command
 320 *
 321 *      Return IPC_64 for new style IPC and IPC_OLD for old style IPC. 
 322 *      The cmd value is turned from an encoding command and version into
 323 *      just the command code.
 324 */
 325 
 326int ipc_parse_version (int *cmd)
 327{
 328#ifdef __x86_64__
 329        if (!(current->thread.flags & THREAD_IA32))
 330                return IPC_64; 
 331#endif
 332        if (*cmd & IPC_64) {
 333                *cmd ^= IPC_64;
 334                return IPC_64;
 335        } else {
 336                return IPC_OLD;
 337        }
 338}
 339
 340#endif /* __ia64__ */
 341
 342#else
 343/*
 344 * Dummy functions when SYSV IPC isn't configured
 345 */
 346
 347void sem_exit (void)
 348{
 349    return;
 350}
 351
 352asmlinkage long sys_semget (key_t key, int nsems, int semflg)
 353{
 354        return -ENOSYS;
 355}
 356
 357asmlinkage long sys_semop (int semid, struct sembuf *sops, unsigned nsops)
 358{
 359        return -ENOSYS;
 360}
 361
 362asmlinkage long sys_semtimedop(int semid, struct sembuf *sops, unsigned nsops,
 363                               const struct timespec *timeout)
 364{
 365        return -ENOSYS;
 366}
 367
 368asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
 369{
 370        return -ENOSYS;
 371}
 372
 373asmlinkage long sys_msgget (key_t key, int msgflg)
 374{
 375        return -ENOSYS;
 376}
 377
 378asmlinkage long sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
 379{
 380        return -ENOSYS;
 381}
 382
 383asmlinkage long sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp,
 384                       int msgflg)
 385{
 386        return -ENOSYS;
 387}
 388
 389asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
 390{
 391        return -ENOSYS;
 392}
 393
 394asmlinkage long sys_shmget (key_t key, size_t size, int shmflag)
 395{
 396        return -ENOSYS;
 397}
 398
 399asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *addr)
 400{
 401        return -ENOSYS;
 402}
 403
 404asmlinkage long sys_shmdt (char *shmaddr)
 405{
 406        return -ENOSYS;
 407}
 408
 409asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
 410{
 411        return -ENOSYS;
 412}
 413
 414#endif /* CONFIG_SYSVIPC */
 415
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.