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_semctl (int semid, int semnum, int cmd, union semun arg) 363{ 364 return -ENOSYS; 365} 366 367asmlinkage long sys_msgget (key_t key, int msgflg) 368{ 369 return -ENOSYS; 370} 371 372asmlinkage long sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg) 373{ 374 return -ENOSYS; 375} 376 377asmlinkage long sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, 378 int msgflg) 379{ 380 return -ENOSYS; 381} 382 383asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds *buf) 384{ 385 return -ENOSYS; 386} 387 388asmlinkage long sys_shmget (key_t key, size_t size, int shmflag) 389{ 390 return -ENOSYS; 391} 392 393asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *addr) 394{ 395 return -ENOSYS; 396} 397 398asmlinkage long sys_shmdt (char *shmaddr) 399{ 400 return -ENOSYS; 401} 402 403asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf) 404{ 405 return -ENOSYS; 406} 407 408#endif /* CONFIG_SYSVIPC */ 409