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