Blame view

ipc/util.c 22.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
  /*
   * linux/ipc/util.c
   * Copyright (C) 1992 Krishna Balasubramanian
   *
   * Sep 1997 - Call suser() last after "normal" permission checks so we
   *            get BSD style process accounting right.
   *            Occurs in several places in the IPC code.
   *            Chris Evans, <chris@ferret.lmh.ox.ac.uk>
   * Nov 1999 - ipc helper functions, unified SMP locking
624dffcbc   Christian Kujau   correct email add...
10
   *	      Manfred Spraul <manfred@colorfullife.com>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
   * Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary().
   *            Mingming Cao <cmm@us.ibm.com>
073115d6b   Steve Grubb   [PATCH] Rework of...
13
14
   * Mar 2006 - support for audit of ipc object properties
   *            Dustin Kirkland <dustin.kirkland@us.ibm.com>
73ea41302   Kirill Korotaev   [PATCH] IPC names...
15
16
17
   * Jun 2006 - namespaces ssupport
   *            OpenVZ, SWsoft Inc.
   *            Pavel Emelianov <xemul@openvz.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
22
  #include <linux/mm.h>
  #include <linux/shm.h>
  #include <linux/init.h>
  #include <linux/msg.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
  #include <linux/vmalloc.h>
  #include <linux/slab.h>
8f68fa2d1   Andrew Morton   ipc/util.c: use r...
25
  #include <linux/notifier.h>
c59ede7b7   Randy.Dunlap   [PATCH] move capa...
26
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
  #include <linux/highuid.h>
  #include <linux/security.h>
  #include <linux/rcupdate.h>
  #include <linux/workqueue.h>
ae7817745   Mike Waychison   [PATCH] ipc: add ...
31
32
  #include <linux/seq_file.h>
  #include <linux/proc_fs.h>
073115d6b   Steve Grubb   [PATCH] Rework of...
33
  #include <linux/audit.h>
73ea41302   Kirill Korotaev   [PATCH] IPC names...
34
  #include <linux/nsproxy.h>
3e148c799   Nadia Derbey   fix idr_find() lo...
35
  #include <linux/rwsem.h>
b6b337ad1   Nadia Derbey   ipc: recompute ms...
36
  #include <linux/memory.h>
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
37
  #include <linux/ipc_namespace.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
41
  
  #include <asm/unistd.h>
  
  #include "util.h"
ae7817745   Mike Waychison   [PATCH] ipc: add ...
42
43
44
  struct ipc_proc_iface {
  	const char *path;
  	const char *header;
73ea41302   Kirill Korotaev   [PATCH] IPC names...
45
  	int ids;
ae7817745   Mike Waychison   [PATCH] ipc: add ...
46
47
  	int (*show)(struct seq_file *, void *);
  };
424450c1d   Nadia Derbey   ipc: invoke the i...
48
49
50
51
  static void ipc_memory_notifier(struct work_struct *work)
  {
  	ipcns_notify(IPCNS_MEMCHANGED);
  }
b6b337ad1   Nadia Derbey   ipc: recompute ms...
52
53
54
  static int ipc_memory_callback(struct notifier_block *self,
  				unsigned long action, void *arg)
  {
8f68fa2d1   Andrew Morton   ipc/util.c: use r...
55
  	static DECLARE_WORK(ipc_memory_wq, ipc_memory_notifier);
b6b337ad1   Nadia Derbey   ipc: recompute ms...
56
57
58
59
60
61
  	switch (action) {
  	case MEM_ONLINE:    /* memory successfully brought online */
  	case MEM_OFFLINE:   /* or offline: it's time to recompute msgmni */
  		/*
  		 * This is done by invoking the ipcns notifier chain with the
  		 * IPC_MEMCHANGED event.
424450c1d   Nadia Derbey   ipc: invoke the i...
62
63
64
65
  		 * In order not to keep the lock on the hotplug memory chain
  		 * for too long, queue a work item that will, when waken up,
  		 * activate the ipcns notification chain.
  		 * No need to keep several ipc work items on the queue.
b6b337ad1   Nadia Derbey   ipc: recompute ms...
66
  		 */
424450c1d   Nadia Derbey   ipc: invoke the i...
67
68
  		if (!work_pending(&ipc_memory_wq))
  			schedule_work(&ipc_memory_wq);
b6b337ad1   Nadia Derbey   ipc: recompute ms...
69
70
71
72
73
74
75
76
77
78
79
  		break;
  	case MEM_GOING_ONLINE:
  	case MEM_GOING_OFFLINE:
  	case MEM_CANCEL_ONLINE:
  	case MEM_CANCEL_OFFLINE:
  	default:
  		break;
  	}
  
  	return NOTIFY_OK;
  }
8f68fa2d1   Andrew Morton   ipc/util.c: use r...
80
81
82
83
  static struct notifier_block ipc_memory_nb = {
  	.notifier_call = ipc_memory_callback,
  	.priority = IPC_CALLBACK_PRI,
  };
b6b337ad1   Nadia Derbey   ipc: recompute ms...
84

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
  /**
   *	ipc_init	-	initialise IPC subsystem
   *
   *	The various system5 IPC resources (semaphores, messages and shared
72fd4a35a   Robert P. J. Day   [PATCH] Numerous ...
89
   *	memory) are initialised
b6b337ad1   Nadia Derbey   ipc: recompute ms...
90
91
92
   *	A callback routine is registered into the memory hotplug notifier
   *	chain: since msgmni scales to lowmem this callback routine will be
   *	called upon successful memory add / remove to recompute msmgni.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
96
97
98
99
   */
   
  static int __init ipc_init(void)
  {
  	sem_init();
  	msg_init();
  	shm_init();
8f68fa2d1   Andrew Morton   ipc/util.c: use r...
100
  	register_hotmemory_notifier(&ipc_memory_nb);
b6b337ad1   Nadia Derbey   ipc: recompute ms...
101
  	register_ipcns_notifier(&init_ipc_ns);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
105
106
107
108
  	return 0;
  }
  __initcall(ipc_init);
  
  /**
   *	ipc_init_ids		-	initialise IPC identifiers
   *	@ids: Identifier set
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
   *
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
110
111
   *	Set up the sequence range to use for the ipc identifier range (limited
   *	below IPCMNI) then initialise the ids idr.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
   */
   
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
114
  void ipc_init_ids(struct ipc_ids *ids)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
  {
3e148c799   Nadia Derbey   fix idr_find() lo...
116
  	init_rwsem(&ids->rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
  	ids->in_use = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
  	ids->seq = 0;
03f595668   Stanislav Kinsbursky   ipc: add sysctl t...
120
  	ids->next_id = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
122
  	{
  		int seq_limit = INT_MAX/SEQ_MULTIPLIER;
4be929be3   Alexey Dobriyan   kernel-wide: repl...
123
124
  		if (seq_limit > USHRT_MAX)
  			ids->seq_max = USHRT_MAX;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
  		 else
  		 	ids->seq_max = seq_limit;
  	}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
128
  	idr_init(&ids->ipcs_idr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
  }
ae7817745   Mike Waychison   [PATCH] ipc: add ...
130
  #ifdef CONFIG_PROC_FS
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
131
  static const struct file_operations sysvipc_proc_fops;
ae7817745   Mike Waychison   [PATCH] ipc: add ...
132
  /**
72fd4a35a   Robert P. J. Day   [PATCH] Numerous ...
133
   *	ipc_init_proc_interface	-  Create a proc interface for sysipc types using a seq_file interface.
ae7817745   Mike Waychison   [PATCH] ipc: add ...
134
135
136
137
138
139
   *	@path: Path in procfs
   *	@header: Banner to be printed at the beginning of the file.
   *	@ids: ipc id table to iterate.
   *	@show: show routine.
   */
  void __init ipc_init_proc_interface(const char *path, const char *header,
73ea41302   Kirill Korotaev   [PATCH] IPC names...
140
  		int ids, int (*show)(struct seq_file *, void *))
ae7817745   Mike Waychison   [PATCH] ipc: add ...
141
142
143
144
145
146
147
148
149
150
151
  {
  	struct proc_dir_entry *pde;
  	struct ipc_proc_iface *iface;
  
  	iface = kmalloc(sizeof(*iface), GFP_KERNEL);
  	if (!iface)
  		return;
  	iface->path	= path;
  	iface->header	= header;
  	iface->ids	= ids;
  	iface->show	= show;
6a6375db1   Denis V. Lunev   sysvipc: use non-...
152
153
154
155
156
157
  	pde = proc_create_data(path,
  			       S_IRUGO,        /* world readable */
  			       NULL,           /* parent dir */
  			       &sysvipc_proc_fops,
  			       iface);
  	if (!pde) {
ae7817745   Mike Waychison   [PATCH] ipc: add ...
158
159
160
161
  		kfree(iface);
  	}
  }
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
166
  /**
   *	ipc_findkey	-	find a key in an ipc identifier set	
   *	@ids: Identifier set
   *	@key: The key to find
   *	
3e148c799   Nadia Derbey   fix idr_find() lo...
167
   *	Requires ipc_ids.rw_mutex locked.
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
168
169
   *	Returns the LOCKED pointer to the ipc structure if found or NULL
   *	if not.
f4566f048   Nadia Derbey   ipc: fix wrong co...
170
   *	If key is found ipc points to the owning ipc structure
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
   */
   
7748dbfaa   Nadia Derbey   ipc: unify the sy...
173
  static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
  {
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
175
176
177
  	struct kern_ipc_perm *ipc;
  	int next_id;
  	int total;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178

7ca7e564e   Nadia Derbey   ipc: store ipcs i...
179
180
181
182
  	for (total = 0, next_id = 0; total < ids->in_use; next_id++) {
  		ipc = idr_find(&ids->ipcs_idr, next_id);
  
  		if (ipc == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  			continue;
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
184
185
186
187
188
189
190
191
  
  		if (ipc->key != key) {
  			total++;
  			continue;
  		}
  
  		ipc_lock_by_ptr(ipc);
  		return ipc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
  	}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
193
194
  
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
  }
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
196
197
198
199
  /**
   *	ipc_get_maxid 	-	get the last assigned id
   *	@ids: IPC identifier set
   *
3e148c799   Nadia Derbey   fix idr_find() lo...
200
   *	Called with ipc_ids.rw_mutex held.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202

7ca7e564e   Nadia Derbey   ipc: store ipcs i...
203
204
205
206
207
208
209
210
  int ipc_get_maxid(struct ipc_ids *ids)
  {
  	struct kern_ipc_perm *ipc;
  	int max_id = -1;
  	int total, id;
  
  	if (ids->in_use == 0)
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211

7ca7e564e   Nadia Derbey   ipc: store ipcs i...
212
213
214
215
216
217
218
219
220
221
222
223
224
  	if (ids->in_use == IPCMNI)
  		return IPCMNI - 1;
  
  	/* Look for the last assigned id */
  	total = 0;
  	for (id = 0; id < IPCMNI && total < ids->in_use; id++) {
  		ipc = idr_find(&ids->ipcs_idr, id);
  		if (ipc != NULL) {
  			max_id = id;
  			total++;
  		}
  	}
  	return max_id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
229
230
  }
  
  /**
   *	ipc_addid 	-	add an IPC identifier
   *	@ids: IPC identifier set
   *	@new: new IPC permission set
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
231
   *	@size: limit for the number of used ids
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
   *
f4566f048   Nadia Derbey   ipc: fix wrong co...
233
   *	Add an entry 'new' to the IPC ids idr. The permissions object is
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
   *	initialised and the first free entry is set up and the id assigned
f4566f048   Nadia Derbey   ipc: fix wrong co...
235
   *	is returned. The 'new' entry is returned in a locked state on success.
283bb7fad   Pierre Peiffer   IPC: fix error ca...
236
   *	On failure the entry is not locked and a negative err-code is returned.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
   *
dbfcd91f0   Davidlohr Bueso   ipc: move rcu loc...
238
   *	Called with writer ipc_ids.rw_mutex held.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
  int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
  {
1efdb69b0   Eric W. Biederman   userns: Convert i...
242
243
  	kuid_t euid;
  	kgid_t egid;
54924ea33   Tejun Heo   ipc: convert to i...
244
  	int id;
03f595668   Stanislav Kinsbursky   ipc: add sysctl t...
245
  	int next_id = ids->next_id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246

7ca7e564e   Nadia Derbey   ipc: store ipcs i...
247
248
249
250
  	if (size > IPCMNI)
  		size = IPCMNI;
  
  	if (ids->in_use >= size)
283bb7fad   Pierre Peiffer   IPC: fix error ca...
251
  		return -ENOSPC;
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
252

54924ea33   Tejun Heo   ipc: convert to i...
253
  	idr_preload(GFP_KERNEL);
e00b4ff7e   Nadia Derbey   sysvipc: fix the ...
254
255
256
257
  	spin_lock_init(&new->lock);
  	new->deleted = 0;
  	rcu_read_lock();
  	spin_lock(&new->lock);
54924ea33   Tejun Heo   ipc: convert to i...
258
259
260
261
262
  	id = idr_alloc(&ids->ipcs_idr, new,
  		       (next_id < 0) ? 0 : ipcid_to_idx(next_id), 0,
  		       GFP_NOWAIT);
  	idr_preload_end();
  	if (id < 0) {
e00b4ff7e   Nadia Derbey   sysvipc: fix the ...
263
264
  		spin_unlock(&new->lock);
  		rcu_read_unlock();
54924ea33   Tejun Heo   ipc: convert to i...
265
  		return id;
e00b4ff7e   Nadia Derbey   sysvipc: fix the ...
266
  	}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
267

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
  	ids->in_use++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269

414c0708d   David Howells   CRED: Wrap task c...
270
271
272
  	current_euid_egid(&euid, &egid);
  	new->cuid = new->uid = euid;
  	new->gid = new->cgid = egid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273

03f595668   Stanislav Kinsbursky   ipc: add sysctl t...
274
275
276
277
278
279
280
281
  	if (next_id < 0) {
  		new->seq = ids->seq++;
  		if (ids->seq > ids->seq_max)
  			ids->seq = 0;
  	} else {
  		new->seq = ipcid_to_seqx(next_id);
  		ids->next_id = -1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282

48dea404e   Pierre Peiffer   IPC: use ipc_buil...
283
  	new->id = ipc_buildid(id, new->seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
286
287
  	return id;
  }
  
  /**
7748dbfaa   Nadia Derbey   ipc: unify the sy...
288
289
   *	ipcget_new	-	create a new ipc object
   *	@ns: namespace
f4566f048   Nadia Derbey   ipc: fix wrong co...
290
   *	@ids: IPC identifer set
7748dbfaa   Nadia Derbey   ipc: unify the sy...
291
292
293
   *	@ops: the actual creation routine to call
   *	@params: its parameters
   *
f4566f048   Nadia Derbey   ipc: fix wrong co...
294
295
   *	This routine is called by sys_msgget, sys_semget() and sys_shmget()
   *	when the key is IPC_PRIVATE.
7748dbfaa   Nadia Derbey   ipc: unify the sy...
296
   */
b2d75cddc   Pavel Emelyanov   ipc: uninline som...
297
  static int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids,
7748dbfaa   Nadia Derbey   ipc: unify the sy...
298
299
300
  		struct ipc_ops *ops, struct ipc_params *params)
  {
  	int err;
7748dbfaa   Nadia Derbey   ipc: unify the sy...
301

3e148c799   Nadia Derbey   fix idr_find() lo...
302
  	down_write(&ids->rw_mutex);
7748dbfaa   Nadia Derbey   ipc: unify the sy...
303
  	err = ops->getnew(ns, params);
3e148c799   Nadia Derbey   fix idr_find() lo...
304
  	up_write(&ids->rw_mutex);
7748dbfaa   Nadia Derbey   ipc: unify the sy...
305
306
307
308
309
  	return err;
  }
  
  /**
   *	ipc_check_perms	-	check security and permissions for an IPC
6213cfe82   Randy Dunlap   ipc: fix util.c k...
310
   *	@ns: IPC namespace
7748dbfaa   Nadia Derbey   ipc: unify the sy...
311
   *	@ipcp: ipc permission set
7748dbfaa   Nadia Derbey   ipc: unify the sy...
312
313
   *	@ops: the actual security routine to call
   *	@params: its parameters
f4566f048   Nadia Derbey   ipc: fix wrong co...
314
315
316
317
318
319
320
   *
   *	This routine is called by sys_msgget(), sys_semget() and sys_shmget()
   *      when the key is not IPC_PRIVATE and that key already exists in the
   *      ids IDR.
   *
   *	On success, the IPC id is returned.
   *
3e148c799   Nadia Derbey   fix idr_find() lo...
321
   *	It is called with ipc_ids.rw_mutex and ipcp->lock held.
7748dbfaa   Nadia Derbey   ipc: unify the sy...
322
   */
b0e77598f   Serge E. Hallyn   userns: user name...
323
324
325
326
  static int ipc_check_perms(struct ipc_namespace *ns,
  			   struct kern_ipc_perm *ipcp,
  			   struct ipc_ops *ops,
  			   struct ipc_params *params)
7748dbfaa   Nadia Derbey   ipc: unify the sy...
327
328
  {
  	int err;
b0e77598f   Serge E. Hallyn   userns: user name...
329
  	if (ipcperms(ns, ipcp, params->flg))
7748dbfaa   Nadia Derbey   ipc: unify the sy...
330
331
332
333
334
335
336
337
338
339
340
341
342
  		err = -EACCES;
  	else {
  		err = ops->associate(ipcp, params->flg);
  		if (!err)
  			err = ipcp->id;
  	}
  
  	return err;
  }
  
  /**
   *	ipcget_public	-	get an ipc object or create a new one
   *	@ns: namespace
f4566f048   Nadia Derbey   ipc: fix wrong co...
343
   *	@ids: IPC identifer set
7748dbfaa   Nadia Derbey   ipc: unify the sy...
344
345
346
   *	@ops: the actual creation routine to call
   *	@params: its parameters
   *
f4566f048   Nadia Derbey   ipc: fix wrong co...
347
348
349
350
351
352
   *	This routine is called by sys_msgget, sys_semget() and sys_shmget()
   *	when the key is not IPC_PRIVATE.
   *	It adds a new entry if the key is not found and does some permission
   *      / security checkings if the key is found.
   *
   *	On success, the ipc id is returned.
7748dbfaa   Nadia Derbey   ipc: unify the sy...
353
   */
b2d75cddc   Pavel Emelyanov   ipc: uninline som...
354
  static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
7748dbfaa   Nadia Derbey   ipc: unify the sy...
355
356
357
358
359
  		struct ipc_ops *ops, struct ipc_params *params)
  {
  	struct kern_ipc_perm *ipcp;
  	int flg = params->flg;
  	int err;
7748dbfaa   Nadia Derbey   ipc: unify the sy...
360

3e148c799   Nadia Derbey   fix idr_find() lo...
361
362
363
364
365
  	/*
  	 * Take the lock as a writer since we are potentially going to add
  	 * a new entry + read locks are not "upgradable"
  	 */
  	down_write(&ids->rw_mutex);
7748dbfaa   Nadia Derbey   ipc: unify the sy...
366
367
368
369
370
  	ipcp = ipc_findkey(ids, params->key);
  	if (ipcp == NULL) {
  		/* key not used */
  		if (!(flg & IPC_CREAT))
  			err = -ENOENT;
7748dbfaa   Nadia Derbey   ipc: unify the sy...
371
372
373
374
375
376
377
378
379
380
381
382
  		else
  			err = ops->getnew(ns, params);
  	} else {
  		/* ipc object has been locked by ipc_findkey() */
  
  		if (flg & IPC_CREAT && flg & IPC_EXCL)
  			err = -EEXIST;
  		else {
  			err = 0;
  			if (ops->more_checks)
  				err = ops->more_checks(ipcp, params);
  			if (!err)
f4566f048   Nadia Derbey   ipc: fix wrong co...
383
384
385
386
  				/*
  				 * ipc_check_perms returns the IPC id on
  				 * success
  				 */
b0e77598f   Serge E. Hallyn   userns: user name...
387
  				err = ipc_check_perms(ns, ipcp, ops, params);
7748dbfaa   Nadia Derbey   ipc: unify the sy...
388
389
390
  		}
  		ipc_unlock(ipcp);
  	}
3e148c799   Nadia Derbey   fix idr_find() lo...
391
  	up_write(&ids->rw_mutex);
7748dbfaa   Nadia Derbey   ipc: unify the sy...
392
393
394
395
396
397
  
  	return err;
  }
  
  
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
   *	ipc_rmid	-	remove an IPC identifier
f4566f048   Nadia Derbey   ipc: fix wrong co...
399
400
   *	@ids: IPC identifier set
   *	@ipcp: ipc perm structure containing the identifier to remove
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
   *
3e148c799   Nadia Derbey   fix idr_find() lo...
402
403
   *	ipc_ids.rw_mutex (as a writer) and the spinlock for this ID are held
   *	before this function is called, and remain locked on the exit.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
405
   */
   
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
406
  void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
  {
ce621f5ba   Nadia Derbey   ipc: introduce th...
408
  	int lid = ipcid_to_idx(ipcp->id);
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
409
410
  
  	idr_remove(&ids->ipcs_idr, lid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  	ids->in_use--;
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
413
414
415
  	ipcp->deleted = 1;
  
  	return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
418
419
420
421
422
423
424
425
  }
  
  /**
   *	ipc_alloc	-	allocate ipc space
   *	@size: size desired
   *
   *	Allocate memory from the appropriate pools and return a pointer to it.
   *	NULL is returned if the allocation fails
   */
   
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
426
  void *ipc_alloc(int size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
  {
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
428
  	void *out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
431
432
433
434
435
436
437
438
439
440
  	if(size > PAGE_SIZE)
  		out = vmalloc(size);
  	else
  		out = kmalloc(size, GFP_KERNEL);
  	return out;
  }
  
  /**
   *	ipc_free        -       free ipc space
   *	@ptr: pointer returned by ipc_alloc
   *	@size: size of block
   *
72fd4a35a   Robert P. J. Day   [PATCH] Numerous ...
441
   *	Free a block created with ipc_alloc(). The caller must know the size
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
443
444
445
446
447
448
449
450
451
   *	used in the allocation call.
   */
  
  void ipc_free(void* ptr, int size)
  {
  	if(size > PAGE_SIZE)
  		vfree(ptr);
  	else
  		kfree(ptr);
  }
600fe9751   Al Viro   ipc_schedule_free...
452
  struct ipc_rcu {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
  	struct rcu_head rcu;
600fe9751   Al Viro   ipc_schedule_free...
454
  	atomic_t refcount;
196aa0132   Manfred Spraul   ipc/util.c, ipc_r...
455
  } ____cacheline_aligned_in_smp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
459
460
461
  /**
   *	ipc_rcu_alloc	-	allocate ipc and rcu space 
   *	@size: size desired
   *
   *	Allocate memory for the rcu header structure +  the object.
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
462
   *	Returns the pointer to the object or NULL upon failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
   */
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
464
  void *ipc_rcu_alloc(int size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
  {
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
466
  	/*
600fe9751   Al Viro   ipc_schedule_free...
467
  	 * We prepend the allocation with the rcu struct
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
  	 */
600fe9751   Al Viro   ipc_schedule_free...
469
470
471
472
  	struct ipc_rcu *out = ipc_alloc(sizeof(struct ipc_rcu) + size);
  	if (unlikely(!out))
  		return NULL;
  	atomic_set(&out->refcount, 1);
196aa0132   Manfred Spraul   ipc/util.c, ipc_r...
473
  	return out + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
  }
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
475
  int ipc_rcu_getref(void *ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
  {
196aa0132   Manfred Spraul   ipc/util.c, ipc_r...
477
478
479
  	struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
  
  	return atomic_inc_not_zero(&p->refcount);
65f27f384   David Howells   WorkStruct: Pass ...
480
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
  /**
1e5d53314   Randy Dunlap   [PATCH] more kern...
482
483
   * ipc_schedule_free - free ipc + rcu space
   * @head: RCU callback structure for queued work
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484
485
486
   */
  static void ipc_schedule_free(struct rcu_head *head)
  {
600fe9751   Al Viro   ipc_schedule_free...
487
  	vfree(container_of(head, struct ipc_rcu, rcu));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
  void ipc_rcu_putref(void *ptr)
  {
196aa0132   Manfred Spraul   ipc/util.c, ipc_r...
491
  	struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
600fe9751   Al Viro   ipc_schedule_free...
492
493
  
  	if (!atomic_dec_and_test(&p->refcount))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
  		return;
600fe9751   Al Viro   ipc_schedule_free...
495
496
  	if (is_vmalloc_addr(ptr)) {
  		call_rcu(&p->rcu, ipc_schedule_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
  	} else {
600fe9751   Al Viro   ipc_schedule_free...
498
  		kfree_rcu(p, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
501
502
503
  	}
  }
  
  /**
   *	ipcperms	-	check IPC permissions
6213cfe82   Randy Dunlap   ipc: fix util.c k...
504
   *	@ns: IPC namespace
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
508
509
   *	@ipcp: IPC permission set
   *	@flag: desired permission set.
   *
   *	Check user, group, other permissions for access
   *	to ipc resources. return 0 if allowed
b0e77598f   Serge E. Hallyn   userns: user name...
510
511
   *
   * 	@flag will most probably be 0 or S_...UGO from <linux/stat.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
513
   */
   
b0e77598f   Serge E. Hallyn   userns: user name...
514
515
  int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag)
  {
1efdb69b0   Eric W. Biederman   userns: Convert i...
516
  	kuid_t euid = current_euid();
a33e67510   Al Viro   sanitize audit_ip...
517
  	int requested_mode, granted_mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518

a33e67510   Al Viro   sanitize audit_ip...
519
  	audit_ipc_obj(ipcp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
521
  	requested_mode = (flag >> 6) | (flag >> 3) | flag;
  	granted_mode = ipcp->mode;
1efdb69b0   Eric W. Biederman   userns: Convert i...
522
523
  	if (uid_eq(euid, ipcp->cuid) ||
  	    uid_eq(euid, ipcp->uid))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
526
527
528
  		granted_mode >>= 6;
  	else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
  		granted_mode >>= 3;
  	/* is there some bit set in requested_mode but not in granted_mode? */
  	if ((requested_mode & ~granted_mode & 0007) && 
b0e77598f   Serge E. Hallyn   userns: user name...
529
  	    !ns_capable(ns->user_ns, CAP_IPC_OWNER))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  		return -1;
  
  	return security_ipc_permission(ipcp, flag);
  }
  
  /*
   * Functions to convert between the kern_ipc_perm structure and the
   * old/new ipc_perm structures
   */
  
  /**
   *	kernel_to_ipc64_perm	-	convert kernel ipc permissions to user
   *	@in: kernel permissions
   *	@out: new style IPC permissions
   *
72fd4a35a   Robert P. J. Day   [PATCH] Numerous ...
545
546
   *	Turn the kernel object @in into a set of permissions descriptions
   *	for returning to userspace (@out).
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
548
549
550
551
552
   */
   
  
  void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
  {
  	out->key	= in->key;
1efdb69b0   Eric W. Biederman   userns: Convert i...
553
554
555
556
  	out->uid	= from_kuid_munged(current_user_ns(), in->uid);
  	out->gid	= from_kgid_munged(current_user_ns(), in->gid);
  	out->cuid	= from_kuid_munged(current_user_ns(), in->cuid);
  	out->cgid	= from_kgid_munged(current_user_ns(), in->cgid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
558
559
560
561
  	out->mode	= in->mode;
  	out->seq	= in->seq;
  }
  
  /**
f4566f048   Nadia Derbey   ipc: fix wrong co...
562
   *	ipc64_perm_to_ipc_perm	-	convert new ipc permissions to old
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
564
565
   *	@in: new style IPC permissions
   *	@out: old style IPC permissions
   *
72fd4a35a   Robert P. J. Day   [PATCH] Numerous ...
566
567
   *	Turn the new style permissions object @in into a compatibility
   *	object and store it into the @out pointer.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
571
572
573
574
575
576
577
578
579
   */
   
  void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
  {
  	out->key	= in->key;
  	SET_UID(out->uid, in->uid);
  	SET_GID(out->gid, in->gid);
  	SET_UID(out->cuid, in->cuid);
  	SET_GID(out->cgid, in->cgid);
  	out->mode	= in->mode;
  	out->seq	= in->seq;
  }
f4566f048   Nadia Derbey   ipc: fix wrong co...
580
  /**
4d2bff5eb   Davidlohr Bueso   ipc: introduce ob...
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
   * ipc_obtain_object
   * @ids: ipc identifier set
   * @id: ipc id to look for
   *
   * Look for an id in the ipc ids idr and return associated ipc object.
   *
   * Call inside the RCU critical section.
   * The ipc object is *not* locked on exit.
   */
  struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id)
  {
  	struct kern_ipc_perm *out;
  	int lid = ipcid_to_idx(id);
  
  	out = idr_find(&ids->ipcs_idr, lid);
  	if (!out)
  		return ERR_PTR(-EINVAL);
  
  	return out;
  }
  
  /**
3e148c799   Nadia Derbey   fix idr_find() lo...
603
   * ipc_lock - Lock an ipc structure without rw_mutex held
f4566f048   Nadia Derbey   ipc: fix wrong co...
604
605
606
607
608
   * @ids: IPC identifier set
   * @id: ipc id to look for
   *
   * Look for an id in the ipc ids idr and lock the associated ipc object.
   *
4d2bff5eb   Davidlohr Bueso   ipc: introduce ob...
609
   * The ipc object is locked on successful exit.
f4566f048   Nadia Derbey   ipc: fix wrong co...
610
   */
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
611
  struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
  {
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
613
  	struct kern_ipc_perm *out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
614
615
  
  	rcu_read_lock();
4d2bff5eb   Davidlohr Bueso   ipc: introduce ob...
616
617
618
  	out = ipc_obtain_object(ids, id);
  	if (IS_ERR(out))
  		goto err1;
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
619

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620
  	spin_lock(&out->lock);
4d2bff5eb   Davidlohr Bueso   ipc: introduce ob...
621

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
623
624
  	/* ipc_rmid() may have already freed the ID while ipc_lock
  	 * was spinning: here verify that the structure is still valid
  	 */
4d2bff5eb   Davidlohr Bueso   ipc: introduce ob...
625
626
627
628
629
630
631
632
633
  	if (!out->deleted)
  		return out;
  
  	spin_unlock(&out->lock);
  	out = ERR_PTR(-EINVAL);
  err1:
  	rcu_read_unlock();
  	return out;
  }
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
634

4d2bff5eb   Davidlohr Bueso   ipc: introduce ob...
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
  /**
   * ipc_obtain_object_check
   * @ids: ipc identifier set
   * @id: ipc id to look for
   *
   * Similar to ipc_obtain_object() but also checks
   * the ipc object reference counter.
   *
   * Call inside the RCU critical section.
   * The ipc object is *not* locked on exit.
   */
  struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id)
  {
  	struct kern_ipc_perm *out = ipc_obtain_object(ids, id);
  
  	if (IS_ERR(out))
  		goto out;
  
  	if (ipc_checkid(out, id))
  		return ERR_PTR(-EIDRM);
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
  	return out;
  }
b2d75cddc   Pavel Emelyanov   ipc: uninline som...
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
  struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id)
  {
  	struct kern_ipc_perm *out;
  
  	out = ipc_lock(ids, id);
  	if (IS_ERR(out))
  		return out;
  
  	if (ipc_checkid(out, id)) {
  		ipc_unlock(out);
  		return ERR_PTR(-EIDRM);
  	}
  
  	return out;
  }
  
  /**
   * ipcget - Common sys_*get() code
   * @ns : namsepace
   * @ids : IPC identifier set
   * @ops : operations to be called on ipc object creation, permission checks
   *        and further checks
   * @params : the parameters needed by the previous operations.
   *
   * Common routine called by sys_msgget(), sys_semget() and sys_shmget().
   */
  int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
  			struct ipc_ops *ops, struct ipc_params *params)
  {
  	if (params->key == IPC_PRIVATE)
  		return ipcget_new(ns, ids, ops, params);
  	else
  		return ipcget_public(ns, ids, ops, params);
  }
8f4a3809c   Pierre Peiffer   IPC: introduce ip...
692
693
694
695
696
  /**
   * ipc_update_perm - update the permissions of an IPC.
   * @in:  the permission given as input.
   * @out: the permission of the ipc to set.
   */
1efdb69b0   Eric W. Biederman   userns: Convert i...
697
  int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)
8f4a3809c   Pierre Peiffer   IPC: introduce ip...
698
  {
1efdb69b0   Eric W. Biederman   userns: Convert i...
699
700
701
702
703
704
705
  	kuid_t uid = make_kuid(current_user_ns(), in->uid);
  	kgid_t gid = make_kgid(current_user_ns(), in->gid);
  	if (!uid_valid(uid) || !gid_valid(gid))
  		return -EINVAL;
  
  	out->uid = uid;
  	out->gid = gid;
8f4a3809c   Pierre Peiffer   IPC: introduce ip...
706
707
  	out->mode = (out->mode & ~S_IRWXUGO)
  		| (in->mode & S_IRWXUGO);
1efdb69b0   Eric W. Biederman   userns: Convert i...
708
709
  
  	return 0;
8f4a3809c   Pierre Peiffer   IPC: introduce ip...
710
  }
a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
711
712
  /**
   * ipcctl_pre_down - retrieve an ipc and check permissions for some IPC_XXX cmd
6213cfe82   Randy Dunlap   ipc: fix util.c k...
713
   * @ns:  the ipc namespace
a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
714
715
716
717
718
719
720
721
722
723
724
   * @ids:  the table of ids where to look for the ipc
   * @id:   the id of the ipc to retrieve
   * @cmd:  the cmd to check
   * @perm: the permission to set
   * @extra_perm: one extra permission parameter used by msq
   *
   * This function does some common audit and permissions check for some IPC_XXX
   * cmd and is called from semctl_down, shmctl_down and msgctl_down.
   * It must be called without any lock held and
   *  - retrieves the ipc with the given id in the given table.
   *  - performs some audit and permission check, depending on the given cmd
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
725
   *  - returns the ipc with the ipc lock held in case of success
a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
726
   *    or an err-code without any lock held otherwise.
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
727
728
   *
   * Call holding the both the rw_mutex and the rcu read lock.
a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
729
   */
b0e77598f   Serge E. Hallyn   userns: user name...
730
731
  struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
  				      struct ipc_ids *ids, int id, int cmd,
a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
732
733
734
  				      struct ipc64_perm *perm, int extra_perm)
  {
  	struct kern_ipc_perm *ipcp;
444d0f621   Davidlohr Bueso   ipc: introduce lo...
735
736
737
738
739
740
741
742
743
744
745
746
747
748
  
  	ipcp = ipcctl_pre_down_nolock(ns, ids, id, cmd, perm, extra_perm);
  	if (IS_ERR(ipcp))
  		goto out;
  
  	spin_lock(&ipcp->lock);
  out:
  	return ipcp;
  }
  
  struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
  					     struct ipc_ids *ids, int id, int cmd,
  					     struct ipc64_perm *perm, int extra_perm)
  {
1efdb69b0   Eric W. Biederman   userns: Convert i...
749
  	kuid_t euid;
444d0f621   Davidlohr Bueso   ipc: introduce lo...
750
751
  	int err = -EPERM;
  	struct kern_ipc_perm *ipcp;
a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
752

444d0f621   Davidlohr Bueso   ipc: introduce lo...
753
  	ipcp = ipc_obtain_object_check(ids, id);
a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
754
755
  	if (IS_ERR(ipcp)) {
  		err = PTR_ERR(ipcp);
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
756
  		goto err;
a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
757
  	}
a33e67510   Al Viro   sanitize audit_ip...
758
  	audit_ipc_obj(ipcp);
e816f370c   Al Viro   sanitize audit_ip...
759
760
  	if (cmd == IPC_SET)
  		audit_ipc_set_perm(extra_perm, perm->uid,
444d0f621   Davidlohr Bueso   ipc: introduce lo...
761
  				   perm->gid, perm->mode);
414c0708d   David Howells   CRED: Wrap task c...
762
763
  
  	euid = current_euid();
1efdb69b0   Eric W. Biederman   userns: Convert i...
764
  	if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid)  ||
b0e77598f   Serge E. Hallyn   userns: user name...
765
  	    ns_capable(ns->user_ns, CAP_SYS_ADMIN))
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
766
767
  		return ipcp; /* successful lookup */
  err:
a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
768
769
  	return ERR_PTR(err);
  }
c1d7e01d7   Will Deacon   ipc: use Kconfig ...
770
  #ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
771
772
773
774
775
776
777
  
  
  /**
   *	ipc_parse_version	-	IPC call version
   *	@cmd: pointer to command
   *
   *	Return IPC_64 for new style IPC and IPC_OLD for old style IPC. 
72fd4a35a   Robert P. J. Day   [PATCH] Numerous ...
778
   *	The @cmd value is turned from an encoding command and version into
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
781
782
783
784
785
786
787
788
789
790
   *	just the command code.
   */
   
  int ipc_parse_version (int *cmd)
  {
  	if (*cmd & IPC_64) {
  		*cmd ^= IPC_64;
  		return IPC_64;
  	} else {
  		return IPC_OLD;
  	}
  }
c1d7e01d7   Will Deacon   ipc: use Kconfig ...
791
  #endif /* CONFIG_ARCH_WANT_IPC_PARSE_VERSION */
ae7817745   Mike Waychison   [PATCH] ipc: add ...
792
793
  
  #ifdef CONFIG_PROC_FS
bc1fc6d88   Eric W. Biederman   [PATCH] ipc: save...
794
795
796
797
  struct ipc_proc_iter {
  	struct ipc_namespace *ns;
  	struct ipc_proc_iface *iface;
  };
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
798
799
800
  /*
   * This routine locks the ipc structure found at least at position pos.
   */
b524b9adb   Adrian Bunk   make ipc/util.c:s...
801
802
  static struct kern_ipc_perm *sysvipc_find_ipc(struct ipc_ids *ids, loff_t pos,
  					      loff_t *new_pos)
ae7817745   Mike Waychison   [PATCH] ipc: add ...
803
  {
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
804
805
  	struct kern_ipc_perm *ipc;
  	int total, id;
73ea41302   Kirill Korotaev   [PATCH] IPC names...
806

7ca7e564e   Nadia Derbey   ipc: store ipcs i...
807
808
809
810
811
812
  	total = 0;
  	for (id = 0; id < pos && total < ids->in_use; id++) {
  		ipc = idr_find(&ids->ipcs_idr, id);
  		if (ipc != NULL)
  			total++;
  	}
ae7817745   Mike Waychison   [PATCH] ipc: add ...
813

7ca7e564e   Nadia Derbey   ipc: store ipcs i...
814
815
  	if (total >= ids->in_use)
  		return NULL;
ae7817745   Mike Waychison   [PATCH] ipc: add ...
816

7ca7e564e   Nadia Derbey   ipc: store ipcs i...
817
818
819
820
821
  	for ( ; pos < IPCMNI; pos++) {
  		ipc = idr_find(&ids->ipcs_idr, pos);
  		if (ipc != NULL) {
  			*new_pos = pos + 1;
  			ipc_lock_by_ptr(ipc);
ae7817745   Mike Waychison   [PATCH] ipc: add ...
822
823
824
825
826
827
828
  			return ipc;
  		}
  	}
  
  	/* Out of range - return NULL to terminate iteration */
  	return NULL;
  }
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
829
830
831
832
833
834
835
836
837
  static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos)
  {
  	struct ipc_proc_iter *iter = s->private;
  	struct ipc_proc_iface *iface = iter->iface;
  	struct kern_ipc_perm *ipc = it;
  
  	/* If we had an ipc id locked before, unlock it */
  	if (ipc && ipc != SEQ_START_TOKEN)
  		ipc_unlock(ipc);
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
838
  	return sysvipc_find_ipc(&iter->ns->ids[iface->ids], *pos, pos);
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
839
  }
ae7817745   Mike Waychison   [PATCH] ipc: add ...
840
  /*
f4566f048   Nadia Derbey   ipc: fix wrong co...
841
842
   * File positions: pos 0 -> header, pos n -> ipc id = n - 1.
   * SeqFile iterator: iterator value locked ipc pointer or SEQ_TOKEN_START.
ae7817745   Mike Waychison   [PATCH] ipc: add ...
843
844
845
   */
  static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos)
  {
bc1fc6d88   Eric W. Biederman   [PATCH] ipc: save...
846
847
  	struct ipc_proc_iter *iter = s->private;
  	struct ipc_proc_iface *iface = iter->iface;
73ea41302   Kirill Korotaev   [PATCH] IPC names...
848
  	struct ipc_ids *ids;
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
849
  	ids = &iter->ns->ids[iface->ids];
ae7817745   Mike Waychison   [PATCH] ipc: add ...
850
851
852
853
854
  
  	/*
  	 * Take the lock - this will be released by the corresponding
  	 * call to stop().
  	 */
3e148c799   Nadia Derbey   fix idr_find() lo...
855
  	down_read(&ids->rw_mutex);
ae7817745   Mike Waychison   [PATCH] ipc: add ...
856
857
858
859
860
861
862
863
864
865
  
  	/* pos < 0 is invalid */
  	if (*pos < 0)
  		return NULL;
  
  	/* pos == 0 means header */
  	if (*pos == 0)
  		return SEQ_START_TOKEN;
  
  	/* Find the (pos-1)th ipc */
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
866
  	return sysvipc_find_ipc(ids, *pos - 1, pos);
ae7817745   Mike Waychison   [PATCH] ipc: add ...
867
868
869
870
871
  }
  
  static void sysvipc_proc_stop(struct seq_file *s, void *it)
  {
  	struct kern_ipc_perm *ipc = it;
bc1fc6d88   Eric W. Biederman   [PATCH] ipc: save...
872
873
  	struct ipc_proc_iter *iter = s->private;
  	struct ipc_proc_iface *iface = iter->iface;
73ea41302   Kirill Korotaev   [PATCH] IPC names...
874
  	struct ipc_ids *ids;
ae7817745   Mike Waychison   [PATCH] ipc: add ...
875

f4566f048   Nadia Derbey   ipc: fix wrong co...
876
  	/* If we had a locked structure, release it */
ae7817745   Mike Waychison   [PATCH] ipc: add ...
877
878
  	if (ipc && ipc != SEQ_START_TOKEN)
  		ipc_unlock(ipc);
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
879
  	ids = &iter->ns->ids[iface->ids];
ae7817745   Mike Waychison   [PATCH] ipc: add ...
880
  	/* Release the lock we took in start() */
3e148c799   Nadia Derbey   fix idr_find() lo...
881
  	up_read(&ids->rw_mutex);
ae7817745   Mike Waychison   [PATCH] ipc: add ...
882
883
884
885
  }
  
  static int sysvipc_proc_show(struct seq_file *s, void *it)
  {
bc1fc6d88   Eric W. Biederman   [PATCH] ipc: save...
886
887
  	struct ipc_proc_iter *iter = s->private;
  	struct ipc_proc_iface *iface = iter->iface;
ae7817745   Mike Waychison   [PATCH] ipc: add ...
888
889
890
891
892
893
  
  	if (it == SEQ_START_TOKEN)
  		return seq_puts(s, iface->header);
  
  	return iface->show(s, it);
  }
88e9d34c7   James Morris   seq_file: constif...
894
  static const struct seq_operations sysvipc_proc_seqops = {
ae7817745   Mike Waychison   [PATCH] ipc: add ...
895
896
897
898
899
  	.start = sysvipc_proc_start,
  	.stop  = sysvipc_proc_stop,
  	.next  = sysvipc_proc_next,
  	.show  = sysvipc_proc_show,
  };
bc1fc6d88   Eric W. Biederman   [PATCH] ipc: save...
900
901
  static int sysvipc_proc_open(struct inode *inode, struct file *file)
  {
ae7817745   Mike Waychison   [PATCH] ipc: add ...
902
903
  	int ret;
  	struct seq_file *seq;
bc1fc6d88   Eric W. Biederman   [PATCH] ipc: save...
904
905
906
907
908
909
  	struct ipc_proc_iter *iter;
  
  	ret = -ENOMEM;
  	iter = kmalloc(sizeof(*iter), GFP_KERNEL);
  	if (!iter)
  		goto out;
ae7817745   Mike Waychison   [PATCH] ipc: add ...
910
911
  
  	ret = seq_open(file, &sysvipc_proc_seqops);
bc1fc6d88   Eric W. Biederman   [PATCH] ipc: save...
912
913
914
915
916
  	if (ret)
  		goto out_kfree;
  
  	seq = file->private_data;
  	seq->private = iter;
d9dda78ba   Al Viro   procfs: new helpe...
917
  	iter->iface = PDE_DATA(inode);
bc1fc6d88   Eric W. Biederman   [PATCH] ipc: save...
918
919
  	iter->ns    = get_ipc_ns(current->nsproxy->ipc_ns);
  out:
ae7817745   Mike Waychison   [PATCH] ipc: add ...
920
  	return ret;
bc1fc6d88   Eric W. Biederman   [PATCH] ipc: save...
921
922
923
924
925
926
927
928
929
930
931
  out_kfree:
  	kfree(iter);
  	goto out;
  }
  
  static int sysvipc_proc_release(struct inode *inode, struct file *file)
  {
  	struct seq_file *seq = file->private_data;
  	struct ipc_proc_iter *iter = seq->private;
  	put_ipc_ns(iter->ns);
  	return seq_release_private(inode, file);
ae7817745   Mike Waychison   [PATCH] ipc: add ...
932
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
933
  static const struct file_operations sysvipc_proc_fops = {
ae7817745   Mike Waychison   [PATCH] ipc: add ...
934
935
936
  	.open    = sysvipc_proc_open,
  	.read    = seq_read,
  	.llseek  = seq_lseek,
bc1fc6d88   Eric W. Biederman   [PATCH] ipc: save...
937
  	.release = sysvipc_proc_release,
ae7817745   Mike Waychison   [PATCH] ipc: add ...
938
939
  };
  #endif /* CONFIG_PROC_FS */