]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
2d21ac55 | 2 | * Copyright (c) 2000-2007 Apple Inc. All rights reserved. |
1c79356b | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 5 | * |
2d21ac55 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
27 | */ |
28 | /* | |
29 | * @OSF_FREE_COPYRIGHT@ | |
30 | */ | |
31 | /* | |
32 | * Mach Operating System | |
33 | * Copyright (c) 1991,1990,1989 Carnegie Mellon University | |
34 | * All Rights Reserved. | |
35 | * | |
36 | * Permission to use, copy, modify and distribute this software and its | |
37 | * documentation is hereby granted, provided that both the copyright | |
38 | * notice and this permission notice appear in all copies of the | |
39 | * software, derivative works or modified versions, and any portions | |
40 | * thereof, and that both notices appear in supporting documentation. | |
41 | * | |
42 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" | |
43 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |
44 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |
45 | * | |
46 | * Carnegie Mellon requests users of this software to return to | |
47 | * | |
48 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |
49 | * School of Computer Science | |
50 | * Carnegie Mellon University | |
51 | * Pittsburgh PA 15213-3890 | |
52 | * | |
53 | * any improvements or extensions that they make and grant Carnegie Mellon | |
54 | * the rights to redistribute these changes. | |
55 | */ | |
2d21ac55 A |
56 | /* |
57 | * NOTICE: This file was modified by McAfee Research in 2004 to introduce | |
58 | * support for mandatory and extensible security protections. This notice | |
59 | * is included in support of clause 2.2 (b) of the Apple Public License, | |
60 | * Version 2.0. | |
61 | * Copyright (c) 2005-2006 SPARTA, Inc. | |
62 | */ | |
1c79356b A |
63 | /* |
64 | */ | |
65 | /* | |
66 | * File: ipc/ipc_right.c | |
67 | * Author: Rich Draves | |
68 | * Date: 1989 | |
69 | * | |
70 | * Functions to manipulate IPC capabilities. | |
71 | */ | |
72 | ||
73 | #include <mach/boolean.h> | |
74 | #include <mach/kern_return.h> | |
75 | #include <mach/port.h> | |
76 | #include <mach/message.h> | |
77 | #include <kern/assert.h> | |
78 | #include <kern/misc_protos.h> | |
1c79356b A |
79 | #include <ipc/port.h> |
80 | #include <ipc/ipc_entry.h> | |
81 | #include <ipc/ipc_space.h> | |
82 | #include <ipc/ipc_object.h> | |
83 | #include <ipc/ipc_hash.h> | |
84 | #include <ipc/ipc_port.h> | |
85 | #include <ipc/ipc_pset.h> | |
86 | #include <ipc/ipc_right.h> | |
87 | #include <ipc/ipc_notify.h> | |
88 | #include <ipc/ipc_table.h> | |
2d21ac55 | 89 | #include <security/mac_mach_internal.h> |
1c79356b A |
90 | |
91 | /* | |
92 | * Routine: ipc_right_lookup_write | |
93 | * Purpose: | |
94 | * Finds an entry in a space, given the name. | |
95 | * Conditions: | |
96 | * Nothing locked. If successful, the space is write-locked. | |
97 | * Returns: | |
98 | * KERN_SUCCESS Found an entry. | |
99 | * KERN_INVALID_TASK The space is dead. | |
100 | * KERN_INVALID_NAME Name doesn't exist in space. | |
101 | */ | |
102 | ||
103 | kern_return_t | |
104 | ipc_right_lookup_write( | |
105 | ipc_space_t space, | |
106 | mach_port_name_t name, | |
107 | ipc_entry_t *entryp) | |
108 | { | |
109 | ipc_entry_t entry; | |
110 | ||
111 | assert(space != IS_NULL); | |
112 | ||
113 | is_write_lock(space); | |
114 | ||
115 | if (!space->is_active) { | |
116 | is_write_unlock(space); | |
117 | return KERN_INVALID_TASK; | |
118 | } | |
119 | ||
120 | if ((entry = ipc_entry_lookup(space, name)) == IE_NULL) { | |
121 | is_write_unlock(space); | |
122 | return KERN_INVALID_NAME; | |
123 | } | |
124 | ||
125 | *entryp = entry; | |
126 | return KERN_SUCCESS; | |
127 | } | |
128 | ||
129 | /* | |
130 | * Routine: ipc_right_lookup_two_write | |
131 | * Purpose: | |
132 | * Like ipc_right_lookup except that it returns two | |
133 | * entries for two different names that were looked | |
134 | * up under the same space lock. | |
135 | * Conditions: | |
136 | * Nothing locked. If successful, the space is write-locked. | |
137 | * Returns: | |
138 | * KERN_INVALID_TASK The space is dead. | |
139 | * KERN_INVALID_NAME Name doesn't exist in space. | |
140 | */ | |
141 | ||
142 | kern_return_t | |
143 | ipc_right_lookup_two_write( | |
144 | ipc_space_t space, | |
145 | mach_port_name_t name1, | |
146 | ipc_entry_t *entryp1, | |
147 | mach_port_name_t name2, | |
148 | ipc_entry_t *entryp2) | |
149 | { | |
150 | ipc_entry_t entry1; | |
151 | ipc_entry_t entry2; | |
152 | ||
153 | assert(space != IS_NULL); | |
154 | ||
155 | is_write_lock(space); | |
156 | ||
157 | if (!space->is_active) { | |
158 | is_write_unlock(space); | |
159 | return KERN_INVALID_TASK; | |
160 | } | |
161 | ||
162 | if ((entry1 = ipc_entry_lookup(space, name1)) == IE_NULL) { | |
163 | is_write_unlock(space); | |
164 | return KERN_INVALID_NAME; | |
165 | } | |
166 | if ((entry2 = ipc_entry_lookup(space, name2)) == IE_NULL) { | |
167 | is_write_unlock(space); | |
168 | return KERN_INVALID_NAME; | |
169 | } | |
170 | *entryp1 = entry1; | |
171 | *entryp2 = entry2; | |
172 | return KERN_SUCCESS; | |
173 | } | |
174 | ||
175 | /* | |
176 | * Routine: ipc_right_reverse | |
177 | * Purpose: | |
178 | * Translate (space, object) -> (name, entry). | |
179 | * Only finds send/receive rights. | |
180 | * Returns TRUE if an entry is found; if so, | |
181 | * the object is locked and active. | |
182 | * Conditions: | |
183 | * The space must be locked (read or write) and active. | |
184 | * Nothing else locked. | |
185 | */ | |
186 | ||
187 | boolean_t | |
188 | ipc_right_reverse( | |
189 | ipc_space_t space, | |
190 | ipc_object_t object, | |
191 | mach_port_name_t *namep, | |
192 | ipc_entry_t *entryp) | |
193 | { | |
194 | ipc_port_t port; | |
195 | mach_port_name_t name; | |
196 | ipc_entry_t entry; | |
197 | ||
198 | /* would switch on io_otype to handle multiple types of object */ | |
199 | ||
200 | assert(space->is_active); | |
201 | assert(io_otype(object) == IOT_PORT); | |
202 | ||
203 | port = (ipc_port_t) object; | |
204 | ||
205 | ip_lock(port); | |
206 | if (!ip_active(port)) { | |
207 | ip_unlock(port); | |
208 | ||
209 | return FALSE; | |
210 | } | |
211 | ||
212 | if (port->ip_receiver == space) { | |
213 | name = port->ip_receiver_name; | |
214 | assert(name != MACH_PORT_NULL); | |
215 | ||
216 | entry = ipc_entry_lookup(space, name); | |
217 | ||
218 | assert(entry != IE_NULL); | |
219 | assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE); | |
220 | assert(port == (ipc_port_t) entry->ie_object); | |
221 | ||
222 | *namep = name; | |
223 | *entryp = entry; | |
224 | return TRUE; | |
225 | } | |
226 | ||
227 | if (ipc_hash_lookup(space, (ipc_object_t) port, namep, entryp)) { | |
228 | assert((entry = *entryp) != IE_NULL); | |
229 | assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_SEND); | |
230 | assert(port == (ipc_port_t) entry->ie_object); | |
231 | ||
232 | return TRUE; | |
233 | } | |
234 | ||
235 | ip_unlock(port); | |
236 | return FALSE; | |
237 | } | |
238 | ||
239 | /* | |
240 | * Routine: ipc_right_dnrequest | |
241 | * Purpose: | |
242 | * Make a dead-name request, returning the previously | |
243 | * registered send-once right. If notify is IP_NULL, | |
244 | * just cancels the previously registered request. | |
245 | * | |
1c79356b A |
246 | * Conditions: |
247 | * Nothing locked. May allocate memory. | |
248 | * Only consumes/returns refs if successful. | |
249 | * Returns: | |
250 | * KERN_SUCCESS Made/canceled dead-name request. | |
251 | * KERN_INVALID_TASK The space is dead. | |
252 | * KERN_INVALID_NAME Name doesn't exist in space. | |
253 | * KERN_INVALID_RIGHT Name doesn't denote port/dead rights. | |
254 | * KERN_INVALID_ARGUMENT Name denotes dead name, but | |
255 | * immediate is FALSE or notify is IP_NULL. | |
256 | * KERN_UREFS_OVERFLOW Name denotes dead name, but | |
257 | * generating immediate notif. would overflow urefs. | |
258 | * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. | |
259 | */ | |
260 | ||
261 | kern_return_t | |
6d2010ae | 262 | ipc_right_request_alloc( |
1c79356b A |
263 | ipc_space_t space, |
264 | mach_port_name_t name, | |
265 | boolean_t immediate, | |
6d2010ae | 266 | boolean_t send_possible, |
1c79356b A |
267 | ipc_port_t notify, |
268 | ipc_port_t *previousp) | |
269 | { | |
6d2010ae A |
270 | ipc_port_request_index_t prev_request; |
271 | ipc_port_t previous = IP_NULL; | |
272 | ipc_entry_t entry; | |
273 | kern_return_t kr; | |
1c79356b A |
274 | |
275 | for (;;) { | |
1c79356b A |
276 | kr = ipc_right_lookup_write(space, name, &entry); |
277 | if (kr != KERN_SUCCESS) | |
278 | return kr; | |
6d2010ae | 279 | |
1c79356b | 280 | /* space is write-locked and active */ |
6d2010ae A |
281 | |
282 | prev_request = entry->ie_request; | |
283 | ||
284 | /* if nothing to do or undo, we're done */ | |
285 | if (notify == IP_NULL && prev_request == IE_REQ_NONE) { | |
286 | is_write_unlock(space); | |
287 | *previousp = IP_NULL; | |
288 | return KERN_SUCCESS; | |
289 | } | |
290 | ||
291 | /* see if the entry is of proper type for requests */ | |
292 | if (entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) { | |
293 | ipc_port_request_index_t new_request; | |
1c79356b | 294 | ipc_port_t port; |
1c79356b A |
295 | |
296 | port = (ipc_port_t) entry->ie_object; | |
297 | assert(port != IP_NULL); | |
298 | ||
299 | if (!ipc_right_check(space, port, name, entry)) { | |
300 | /* port is locked and active */ | |
301 | ||
6d2010ae | 302 | /* if no new request, just cancel previous */ |
1c79356b | 303 | if (notify == IP_NULL) { |
6d2010ae A |
304 | if (prev_request != IE_REQ_NONE) |
305 | previous = ipc_port_request_cancel(port, name, prev_request); | |
1c79356b | 306 | ip_unlock(port); |
6d2010ae | 307 | entry->ie_request = IE_REQ_NONE; |
1c79356b A |
308 | is_write_unlock(space); |
309 | break; | |
310 | } | |
311 | ||
312 | /* | |
6d2010ae A |
313 | * send-once rights, kernel objects, and non-full other queues |
314 | * fire immediately (if immediate specified). | |
1c79356b | 315 | */ |
6d2010ae A |
316 | if (send_possible && immediate && |
317 | ((entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE) || | |
318 | port->ip_receiver == ipc_space_kernel || !ip_full(port))) { | |
319 | if (prev_request != IE_REQ_NONE) | |
320 | previous = ipc_port_request_cancel(port, name, prev_request); | |
321 | ip_unlock(port); | |
322 | entry->ie_request = IE_REQ_NONE; | |
323 | is_write_unlock(space); | |
1c79356b | 324 | |
6d2010ae A |
325 | ipc_notify_send_possible(notify, name); |
326 | break; | |
327 | } | |
1c79356b | 328 | |
6d2010ae A |
329 | /* |
330 | * If there is a previous request, free it. Any subsequent | |
331 | * allocation cannot fail, thus assuring an atomic swap. | |
332 | */ | |
333 | if (prev_request != IE_REQ_NONE) | |
334 | previous = ipc_port_request_cancel(port, name, prev_request); | |
335 | ||
336 | kr = ipc_port_request_alloc(port, name, notify, | |
337 | send_possible, immediate, | |
338 | &new_request); | |
1c79356b A |
339 | if (kr != KERN_SUCCESS) { |
340 | assert(previous == IP_NULL); | |
341 | is_write_unlock(space); | |
342 | ||
6d2010ae | 343 | kr = ipc_port_request_grow(port, ITS_SIZE_NONE); |
1c79356b | 344 | /* port is unlocked */ |
6d2010ae | 345 | |
1c79356b A |
346 | if (kr != KERN_SUCCESS) |
347 | return kr; | |
348 | ||
349 | continue; | |
350 | } | |
351 | ||
6d2010ae | 352 | assert(new_request != IE_REQ_NONE); |
1c79356b | 353 | ip_unlock(port); |
6d2010ae | 354 | entry->ie_request = new_request; |
1c79356b A |
355 | is_write_unlock(space); |
356 | break; | |
1c79356b | 357 | } |
6d2010ae | 358 | /* entry may have changed to dead-name by ipc_right_check() */ |
1c79356b | 359 | |
1c79356b A |
360 | } |
361 | ||
6d2010ae A |
362 | /* treat send_possible requests as immediate w.r.t. dead-name */ |
363 | if ((send_possible || immediate) && notify != IP_NULL && | |
364 | (entry->ie_bits & MACH_PORT_TYPE_DEAD_NAME)) { | |
365 | mach_port_urefs_t urefs = IE_BITS_UREFS(entry->ie_bits); | |
1c79356b | 366 | |
1c79356b A |
367 | assert(urefs > 0); |
368 | ||
369 | if (MACH_PORT_UREFS_OVERFLOW(urefs, 1)) { | |
370 | is_write_unlock(space); | |
371 | return KERN_UREFS_OVERFLOW; | |
372 | } | |
373 | ||
374 | (entry->ie_bits)++; /* increment urefs */ | |
375 | is_write_unlock(space); | |
376 | ||
377 | ipc_notify_dead_name(notify, name); | |
378 | previous = IP_NULL; | |
379 | break; | |
380 | } | |
381 | ||
382 | is_write_unlock(space); | |
6d2010ae | 383 | if (entry->ie_bits & MACH_PORT_TYPE_PORT_OR_DEAD) |
1c79356b A |
384 | return KERN_INVALID_ARGUMENT; |
385 | else | |
386 | return KERN_INVALID_RIGHT; | |
387 | } | |
388 | ||
389 | *previousp = previous; | |
390 | return KERN_SUCCESS; | |
391 | } | |
392 | ||
393 | /* | |
6d2010ae | 394 | * Routine: ipc_right_request_cancel |
1c79356b | 395 | * Purpose: |
6d2010ae | 396 | * Cancel a notification request and return the send-once right. |
1c79356b A |
397 | * Afterwards, entry->ie_request == 0. |
398 | * Conditions: | |
399 | * The space must be write-locked; the port must be locked. | |
400 | * The port must be active; the space doesn't have to be. | |
401 | */ | |
402 | ||
403 | ipc_port_t | |
6d2010ae | 404 | ipc_right_request_cancel( |
91447636 A |
405 | __unused ipc_space_t space, |
406 | ipc_port_t port, | |
407 | mach_port_name_t name, | |
408 | ipc_entry_t entry) | |
1c79356b | 409 | { |
6d2010ae | 410 | ipc_port_t previous; |
1c79356b A |
411 | |
412 | assert(ip_active(port)); | |
413 | assert(port == (ipc_port_t) entry->ie_object); | |
414 | ||
6d2010ae A |
415 | if (entry->ie_request == IE_REQ_NONE) |
416 | return IP_NULL; | |
1c79356b | 417 | |
6d2010ae A |
418 | previous = ipc_port_request_cancel(port, name, entry->ie_request); |
419 | entry->ie_request = IE_REQ_NONE; | |
420 | return previous; | |
1c79356b A |
421 | } |
422 | ||
423 | /* | |
424 | * Routine: ipc_right_inuse | |
425 | * Purpose: | |
426 | * Check if an entry is being used. | |
427 | * Returns TRUE if it is. | |
428 | * Conditions: | |
429 | * The space is write-locked and active. | |
430 | * It is unlocked if the entry is inuse. | |
431 | */ | |
432 | ||
433 | boolean_t | |
434 | ipc_right_inuse( | |
91447636 A |
435 | ipc_space_t space, |
436 | __unused mach_port_name_t name, | |
437 | ipc_entry_t entry) | |
1c79356b A |
438 | { |
439 | if (IE_BITS_TYPE(entry->ie_bits) != MACH_PORT_TYPE_NONE) { | |
440 | is_write_unlock(space); | |
441 | return TRUE; | |
442 | } | |
443 | return FALSE; | |
444 | } | |
445 | ||
446 | /* | |
447 | * Routine: ipc_right_check | |
448 | * Purpose: | |
449 | * Check if the port has died. If it has, | |
450 | * clean up the entry and return TRUE. | |
451 | * Conditions: | |
452 | * The space is write-locked; the port is not locked. | |
453 | * If returns FALSE, the port is also locked and active. | |
454 | * Otherwise, entry is converted to a dead name, freeing | |
455 | * a reference to port. | |
456 | */ | |
457 | ||
458 | boolean_t | |
459 | ipc_right_check( | |
460 | ipc_space_t space, | |
461 | ipc_port_t port, | |
462 | mach_port_name_t name, | |
463 | ipc_entry_t entry) | |
464 | { | |
465 | ipc_entry_bits_t bits; | |
466 | ||
467 | assert(space->is_active); | |
468 | assert(port == (ipc_port_t) entry->ie_object); | |
469 | ||
470 | ip_lock(port); | |
471 | if (ip_active(port)) | |
472 | return FALSE; | |
473 | ip_unlock(port); | |
474 | ||
475 | /* this was either a pure send right or a send-once right */ | |
476 | ||
477 | bits = entry->ie_bits; | |
478 | assert((bits & MACH_PORT_TYPE_RECEIVE) == 0); | |
479 | assert(IE_BITS_UREFS(bits) > 0); | |
480 | ||
481 | if (bits & MACH_PORT_TYPE_SEND) { | |
482 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); | |
483 | } else { | |
484 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); | |
485 | assert(IE_BITS_UREFS(bits) == 1); | |
486 | } | |
487 | ||
488 | ||
1c79356b A |
489 | /* convert entry to dead name */ |
490 | ||
491 | if ((bits & MACH_PORT_TYPE_SEND) && !(bits & MACH_PORT_TYPE_RECEIVE)) | |
492 | ipc_hash_delete(space, (ipc_object_t)port, name, entry); | |
493 | ||
494 | bits = (bits &~ IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME; | |
495 | ||
496 | /* | |
497 | * If there was a notification request outstanding on this | |
6d2010ae A |
498 | * name, and the port went dead, that notification |
499 | * must already be on its way up from the port layer. | |
500 | * | |
501 | * Add the reference that the notification carries. It | |
502 | * is done here, and not in the notification delivery, | |
503 | * because the latter doesn't have a space reference and | |
504 | * trying to actually move a send-right reference would | |
505 | * get short-circuited into a MACH_PORT_DEAD by IPC. Since | |
506 | * all calls that deal with the right eventually come | |
507 | * through here, it has the same result. | |
1c79356b | 508 | * |
6d2010ae A |
509 | * Once done, clear the request index so we only account |
510 | * for it once. | |
1c79356b | 511 | */ |
6d2010ae A |
512 | if (entry->ie_request != IE_REQ_NONE) { |
513 | if (ipc_port_request_type(port, name, entry->ie_request) != 0) { | |
514 | assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX); | |
515 | bits++; | |
516 | } | |
517 | entry->ie_request = IE_REQ_NONE; | |
1c79356b A |
518 | } |
519 | entry->ie_bits = bits; | |
520 | entry->ie_object = IO_NULL; | |
6d2010ae A |
521 | |
522 | ipc_port_release(port); | |
523 | ||
1c79356b A |
524 | return TRUE; |
525 | } | |
526 | ||
527 | /* | |
528 | * Routine: ipc_right_clean | |
529 | * Purpose: | |
530 | * Cleans up an entry in a dead space. | |
531 | * The entry isn't deallocated or removed | |
532 | * from reverse hash tables. | |
533 | * Conditions: | |
534 | * The space is dead and unlocked. | |
535 | */ | |
536 | ||
537 | void | |
538 | ipc_right_clean( | |
539 | ipc_space_t space, | |
540 | mach_port_name_t name, | |
541 | ipc_entry_t entry) | |
542 | { | |
543 | ipc_entry_bits_t bits; | |
544 | mach_port_type_t type; | |
545 | ||
546 | bits = entry->ie_bits; | |
547 | type = IE_BITS_TYPE(bits); | |
548 | ||
549 | assert(!space->is_active); | |
550 | ||
551 | /* | |
552 | * IE_BITS_COMPAT/ipc_right_dncancel doesn't have this | |
553 | * problem, because we check that the port is active. If | |
554 | * we didn't cancel IE_BITS_COMPAT, ipc_port_destroy | |
555 | * would still work, but dead space refs would accumulate | |
556 | * in ip_dnrequests. They would use up slots in | |
557 | * ip_dnrequests and keep the spaces from being freed. | |
558 | */ | |
559 | ||
560 | switch (type) { | |
561 | case MACH_PORT_TYPE_DEAD_NAME: | |
6d2010ae | 562 | assert(entry->ie_request == IE_REQ_NONE); |
1c79356b A |
563 | assert(entry->ie_object == IO_NULL); |
564 | break; | |
565 | ||
566 | case MACH_PORT_TYPE_PORT_SET: { | |
567 | ipc_pset_t pset = (ipc_pset_t) entry->ie_object; | |
568 | ||
6d2010ae | 569 | assert(entry->ie_request == IE_REQ_NONE); |
1c79356b A |
570 | assert(pset != IPS_NULL); |
571 | ||
572 | ips_lock(pset); | |
573 | assert(ips_active(pset)); | |
574 | ||
575 | ipc_pset_destroy(pset); /* consumes ref, unlocks */ | |
576 | break; | |
577 | } | |
578 | ||
579 | case MACH_PORT_TYPE_SEND: | |
580 | case MACH_PORT_TYPE_RECEIVE: | |
581 | case MACH_PORT_TYPE_SEND_RECEIVE: | |
582 | case MACH_PORT_TYPE_SEND_ONCE: { | |
583 | ipc_port_t port = (ipc_port_t) entry->ie_object; | |
6d2010ae | 584 | ipc_port_t request; |
1c79356b | 585 | ipc_port_t nsrequest = IP_NULL; |
91447636 | 586 | mach_port_mscount_t mscount = 0; |
1c79356b A |
587 | |
588 | assert(port != IP_NULL); | |
589 | ip_lock(port); | |
590 | ||
591 | if (!ip_active(port)) { | |
592 | ip_release(port); | |
593 | ip_check_unlock(port); | |
594 | break; | |
595 | } | |
596 | ||
6d2010ae | 597 | request = ipc_right_request_cancel_macro(space, port, |
1c79356b A |
598 | name, entry); |
599 | ||
600 | if (type & MACH_PORT_TYPE_SEND) { | |
601 | assert(port->ip_srights > 0); | |
602 | if (--port->ip_srights == 0 | |
603 | ) { | |
604 | nsrequest = port->ip_nsrequest; | |
605 | if (nsrequest != IP_NULL) { | |
606 | port->ip_nsrequest = IP_NULL; | |
607 | mscount = port->ip_mscount; | |
608 | } | |
609 | } | |
610 | } | |
611 | ||
612 | if (type & MACH_PORT_TYPE_RECEIVE) { | |
613 | assert(port->ip_receiver_name == name); | |
614 | assert(port->ip_receiver == space); | |
615 | ||
616 | ipc_port_clear_receiver(port); | |
617 | ipc_port_destroy(port); /* consumes our ref, unlocks */ | |
618 | } else if (type & MACH_PORT_TYPE_SEND_ONCE) { | |
619 | assert(port->ip_sorights > 0); | |
620 | ip_unlock(port); | |
621 | ||
622 | ipc_notify_send_once(port); /* consumes our ref */ | |
623 | } else { | |
624 | assert(port->ip_receiver != space); | |
625 | ||
626 | ip_release(port); | |
627 | ip_unlock(port); /* port is active */ | |
628 | } | |
629 | ||
630 | if (nsrequest != IP_NULL) | |
631 | ipc_notify_no_senders(nsrequest, mscount); | |
632 | ||
6d2010ae A |
633 | if (request != IP_NULL) |
634 | ipc_notify_port_deleted(request, name); | |
1c79356b A |
635 | break; |
636 | } | |
637 | ||
638 | default: | |
c910b4d9 | 639 | panic("ipc_right_clean: strange type - 0x%x", type); |
1c79356b A |
640 | } |
641 | } | |
642 | ||
643 | /* | |
644 | * Routine: ipc_right_destroy | |
645 | * Purpose: | |
646 | * Destroys an entry in a space. | |
647 | * Conditions: | |
648 | * The space is write-locked. | |
649 | * The space must be active. | |
650 | * Returns: | |
651 | * KERN_SUCCESS The entry was destroyed. | |
652 | */ | |
653 | ||
654 | kern_return_t | |
655 | ipc_right_destroy( | |
656 | ipc_space_t space, | |
657 | mach_port_name_t name, | |
658 | ipc_entry_t entry) | |
659 | { | |
660 | ipc_entry_bits_t bits; | |
661 | mach_port_type_t type; | |
662 | ||
663 | bits = entry->ie_bits; | |
664 | entry->ie_bits &= ~IE_BITS_TYPE_MASK; | |
665 | type = IE_BITS_TYPE(bits); | |
666 | ||
667 | assert(space->is_active); | |
668 | ||
669 | switch (type) { | |
670 | case MACH_PORT_TYPE_DEAD_NAME: | |
6d2010ae | 671 | assert(entry->ie_request == IE_REQ_NONE); |
1c79356b A |
672 | assert(entry->ie_object == IO_NULL); |
673 | ||
674 | ipc_entry_dealloc(space, name, entry); | |
675 | break; | |
676 | ||
677 | case MACH_PORT_TYPE_PORT_SET: { | |
678 | ipc_pset_t pset = (ipc_pset_t) entry->ie_object; | |
679 | ||
6d2010ae | 680 | assert(entry->ie_request == IE_REQ_NONE); |
1c79356b A |
681 | assert(pset != IPS_NULL); |
682 | ||
683 | entry->ie_object = IO_NULL; | |
1c79356b A |
684 | ipc_entry_dealloc(space, name, entry); |
685 | ||
686 | ips_lock(pset); | |
687 | assert(ips_active(pset)); | |
688 | ||
689 | ipc_pset_destroy(pset); /* consumes ref, unlocks */ | |
690 | break; | |
691 | } | |
692 | ||
693 | case MACH_PORT_TYPE_SEND: | |
694 | case MACH_PORT_TYPE_RECEIVE: | |
695 | case MACH_PORT_TYPE_SEND_RECEIVE: | |
696 | case MACH_PORT_TYPE_SEND_ONCE: { | |
697 | ipc_port_t port = (ipc_port_t) entry->ie_object; | |
698 | ipc_port_t nsrequest = IP_NULL; | |
91447636 | 699 | mach_port_mscount_t mscount = 0; |
6d2010ae | 700 | ipc_port_t request; |
1c79356b A |
701 | |
702 | assert(port != IP_NULL); | |
703 | ||
704 | if (type == MACH_PORT_TYPE_SEND) | |
705 | ipc_hash_delete(space, (ipc_object_t) port, | |
706 | name, entry); | |
707 | ||
708 | ip_lock(port); | |
709 | ||
710 | if (!ip_active(port)) { | |
711 | assert((type & MACH_PORT_TYPE_RECEIVE) == 0); | |
712 | ip_release(port); | |
713 | ip_check_unlock(port); | |
714 | ||
6d2010ae | 715 | entry->ie_request = IE_REQ_NONE; |
1c79356b A |
716 | entry->ie_object = IO_NULL; |
717 | ipc_entry_dealloc(space, name, entry); | |
718 | ||
719 | break; | |
720 | } | |
721 | ||
6d2010ae | 722 | request = ipc_right_request_cancel_macro(space, port, name, entry); |
1c79356b A |
723 | |
724 | entry->ie_object = IO_NULL; | |
725 | ipc_entry_dealloc(space, name, entry); | |
726 | ||
727 | if (type & MACH_PORT_TYPE_SEND) { | |
728 | assert(port->ip_srights > 0); | |
729 | if (--port->ip_srights == 0) { | |
730 | nsrequest = port->ip_nsrequest; | |
731 | if (nsrequest != IP_NULL) { | |
732 | port->ip_nsrequest = IP_NULL; | |
733 | mscount = port->ip_mscount; | |
734 | } | |
735 | } | |
736 | } | |
737 | ||
738 | if (type & MACH_PORT_TYPE_RECEIVE) { | |
739 | assert(ip_active(port)); | |
740 | assert(port->ip_receiver == space); | |
741 | ||
1c79356b A |
742 | ipc_port_clear_receiver(port); |
743 | ipc_port_destroy(port); /* consumes our ref, unlocks */ | |
744 | } else if (type & MACH_PORT_TYPE_SEND_ONCE) { | |
745 | assert(port->ip_sorights > 0); | |
746 | ip_unlock(port); | |
747 | ||
748 | ipc_notify_send_once(port); /* consumes our ref */ | |
749 | } else { | |
750 | assert(port->ip_receiver != space); | |
751 | ||
752 | ip_release(port); | |
753 | ip_unlock(port); | |
754 | } | |
755 | ||
756 | if (nsrequest != IP_NULL) | |
757 | ipc_notify_no_senders(nsrequest, mscount); | |
758 | ||
6d2010ae A |
759 | if (request != IP_NULL) |
760 | ipc_notify_port_deleted(request, name); | |
1c79356b A |
761 | break; |
762 | } | |
763 | ||
764 | default: | |
765 | panic("ipc_right_destroy: strange type"); | |
766 | } | |
767 | ||
768 | return KERN_SUCCESS; | |
769 | } | |
770 | ||
771 | /* | |
772 | * Routine: ipc_right_dealloc | |
773 | * Purpose: | |
774 | * Releases a send/send-once/dead-name user ref. | |
775 | * Like ipc_right_delta with a delta of -1, | |
776 | * but looks at the entry to determine the right. | |
777 | * Conditions: | |
778 | * The space is write-locked, and is unlocked upon return. | |
779 | * The space must be active. | |
780 | * Returns: | |
781 | * KERN_SUCCESS A user ref was released. | |
782 | * KERN_INVALID_RIGHT Entry has wrong type. | |
783 | */ | |
784 | ||
785 | kern_return_t | |
786 | ipc_right_dealloc( | |
787 | ipc_space_t space, | |
788 | mach_port_name_t name, | |
789 | ipc_entry_t entry) | |
790 | { | |
791 | ||
792 | ipc_entry_bits_t bits; | |
793 | mach_port_type_t type; | |
794 | ||
795 | bits = entry->ie_bits; | |
796 | type = IE_BITS_TYPE(bits); | |
797 | ||
798 | ||
799 | assert(space->is_active); | |
800 | ||
801 | switch (type) { | |
802 | case MACH_PORT_TYPE_DEAD_NAME: { | |
803 | dead_name: | |
804 | ||
805 | assert(IE_BITS_UREFS(bits) > 0); | |
6d2010ae | 806 | assert(entry->ie_request == IE_REQ_NONE); |
1c79356b A |
807 | assert(entry->ie_object == IO_NULL); |
808 | ||
809 | if (IE_BITS_UREFS(bits) == 1) { | |
810 | ipc_entry_dealloc(space, name, entry); | |
811 | } | |
812 | else | |
813 | entry->ie_bits = bits-1; /* decrement urefs */ | |
814 | ||
815 | is_write_unlock(space); | |
816 | break; | |
817 | } | |
818 | ||
819 | case MACH_PORT_TYPE_SEND_ONCE: { | |
6d2010ae | 820 | ipc_port_t port, request; |
1c79356b A |
821 | |
822 | assert(IE_BITS_UREFS(bits) == 1); | |
823 | ||
824 | port = (ipc_port_t) entry->ie_object; | |
825 | assert(port != IP_NULL); | |
826 | ||
827 | if (ipc_right_check(space, port, name, entry)) { | |
828 | ||
829 | bits = entry->ie_bits; | |
830 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); | |
831 | goto dead_name; | |
832 | } | |
833 | /* port is locked and active */ | |
834 | ||
835 | assert(port->ip_sorights > 0); | |
836 | ||
6d2010ae | 837 | request = ipc_right_request_cancel_macro(space, port, name, entry); |
1c79356b A |
838 | ip_unlock(port); |
839 | ||
840 | entry->ie_object = IO_NULL; | |
841 | ipc_entry_dealloc(space, name, entry); | |
842 | ||
843 | is_write_unlock(space); | |
844 | ||
845 | ipc_notify_send_once(port); | |
846 | ||
6d2010ae A |
847 | if (request != IP_NULL) |
848 | ipc_notify_port_deleted(request, name); | |
1c79356b A |
849 | break; |
850 | } | |
851 | ||
852 | case MACH_PORT_TYPE_SEND: { | |
853 | ipc_port_t port; | |
6d2010ae | 854 | ipc_port_t request = IP_NULL; |
1c79356b | 855 | ipc_port_t nsrequest = IP_NULL; |
91447636 | 856 | mach_port_mscount_t mscount = 0; |
1c79356b A |
857 | |
858 | ||
859 | assert(IE_BITS_UREFS(bits) > 0); | |
860 | ||
861 | port = (ipc_port_t) entry->ie_object; | |
862 | assert(port != IP_NULL); | |
863 | ||
864 | if (ipc_right_check(space, port, name, entry)) { | |
865 | bits = entry->ie_bits; | |
866 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); | |
867 | goto dead_name; | |
868 | } | |
869 | /* port is locked and active */ | |
870 | ||
871 | assert(port->ip_srights > 0); | |
872 | ||
873 | if (IE_BITS_UREFS(bits) == 1) { | |
874 | if (--port->ip_srights == 0) { | |
875 | nsrequest = port->ip_nsrequest; | |
876 | if (nsrequest != IP_NULL) { | |
877 | port->ip_nsrequest = IP_NULL; | |
878 | mscount = port->ip_mscount; | |
879 | } | |
880 | } | |
881 | ||
6d2010ae | 882 | request = ipc_right_request_cancel_macro(space, port, |
1c79356b A |
883 | name, entry); |
884 | ipc_hash_delete(space, (ipc_object_t) port, | |
885 | name, entry); | |
886 | ||
887 | ip_release(port); | |
888 | entry->ie_object = IO_NULL; | |
889 | ipc_entry_dealloc(space, name, entry); | |
890 | ||
891 | } else | |
892 | entry->ie_bits = bits-1; /* decrement urefs */ | |
893 | ||
894 | /* even if dropped a ref, port is active */ | |
895 | ip_unlock(port); | |
896 | is_write_unlock(space); | |
897 | ||
898 | if (nsrequest != IP_NULL) | |
899 | ipc_notify_no_senders(nsrequest, mscount); | |
900 | ||
6d2010ae A |
901 | if (request != IP_NULL) |
902 | ipc_notify_port_deleted(request, name); | |
1c79356b A |
903 | break; |
904 | } | |
905 | ||
906 | case MACH_PORT_TYPE_SEND_RECEIVE: { | |
907 | ipc_port_t port; | |
908 | ipc_port_t nsrequest = IP_NULL; | |
91447636 | 909 | mach_port_mscount_t mscount = 0; |
1c79356b A |
910 | |
911 | assert(IE_BITS_UREFS(bits) > 0); | |
912 | ||
913 | port = (ipc_port_t) entry->ie_object; | |
914 | assert(port != IP_NULL); | |
915 | ||
916 | ip_lock(port); | |
917 | assert(ip_active(port)); | |
918 | assert(port->ip_receiver_name == name); | |
919 | assert(port->ip_receiver == space); | |
920 | assert(port->ip_srights > 0); | |
921 | ||
922 | if (IE_BITS_UREFS(bits) == 1) { | |
923 | if (--port->ip_srights == 0) { | |
924 | nsrequest = port->ip_nsrequest; | |
925 | if (nsrequest != IP_NULL) { | |
926 | port->ip_nsrequest = IP_NULL; | |
927 | mscount = port->ip_mscount; | |
928 | } | |
929 | } | |
930 | ||
931 | entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK | | |
932 | MACH_PORT_TYPE_SEND); | |
933 | } else | |
934 | entry->ie_bits = bits-1; /* decrement urefs */ | |
935 | ||
936 | ip_unlock(port); | |
937 | is_write_unlock(space); | |
938 | ||
939 | if (nsrequest != IP_NULL) | |
940 | ipc_notify_no_senders(nsrequest, mscount); | |
941 | break; | |
942 | } | |
943 | ||
944 | default: | |
945 | is_write_unlock(space); | |
946 | return KERN_INVALID_RIGHT; | |
947 | } | |
948 | ||
949 | return KERN_SUCCESS; | |
950 | } | |
951 | ||
952 | /* | |
953 | * Routine: ipc_right_delta | |
954 | * Purpose: | |
955 | * Modifies the user-reference count for a right. | |
956 | * May deallocate the right, if the count goes to zero. | |
957 | * Conditions: | |
958 | * The space is write-locked, and is unlocked upon return. | |
959 | * The space must be active. | |
960 | * Returns: | |
961 | * KERN_SUCCESS Count was modified. | |
962 | * KERN_INVALID_RIGHT Entry has wrong type. | |
963 | * KERN_INVALID_VALUE Bad delta for the right. | |
964 | * KERN_UREFS_OVERFLOW OK delta, except would overflow. | |
965 | */ | |
966 | ||
967 | kern_return_t | |
968 | ipc_right_delta( | |
969 | ipc_space_t space, | |
970 | mach_port_name_t name, | |
971 | ipc_entry_t entry, | |
972 | mach_port_right_t right, | |
973 | mach_port_delta_t delta) | |
974 | { | |
975 | ipc_entry_bits_t bits; | |
976 | ||
977 | bits = entry->ie_bits; | |
978 | ||
979 | ||
980 | /* | |
981 | * The following is used (for case MACH_PORT_RIGHT_DEAD_NAME) in the | |
982 | * switch below. It is used to keep track of those cases (in DIPC) | |
983 | * where we have postponed the dropping of a port reference. Since | |
984 | * the dropping of the reference could cause the port to disappear | |
985 | * we postpone doing so when we are holding the space lock. | |
986 | */ | |
987 | ||
988 | assert(space->is_active); | |
989 | assert(right < MACH_PORT_RIGHT_NUMBER); | |
990 | ||
991 | /* Rights-specific restrictions and operations. */ | |
992 | ||
993 | switch (right) { | |
994 | case MACH_PORT_RIGHT_PORT_SET: { | |
995 | ipc_pset_t pset; | |
996 | ||
997 | if ((bits & MACH_PORT_TYPE_PORT_SET) == 0) | |
998 | goto invalid_right; | |
999 | ||
1000 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_PORT_SET); | |
1001 | assert(IE_BITS_UREFS(bits) == 0); | |
6d2010ae | 1002 | assert(entry->ie_request == IE_REQ_NONE); |
1c79356b A |
1003 | |
1004 | if (delta == 0) | |
1005 | goto success; | |
1006 | ||
1007 | if (delta != -1) | |
1008 | goto invalid_value; | |
1009 | ||
1010 | pset = (ipc_pset_t) entry->ie_object; | |
1011 | assert(pset != IPS_NULL); | |
1012 | ||
1013 | ||
1014 | ||
1015 | entry->ie_object = IO_NULL; | |
1016 | ipc_entry_dealloc(space, name, entry); | |
1017 | ||
1018 | ||
1019 | ips_lock(pset); | |
1020 | assert(ips_active(pset)); | |
1021 | is_write_unlock(space); | |
1022 | ||
1023 | ipc_pset_destroy(pset); /* consumes ref, unlocks */ | |
1024 | break; | |
1025 | } | |
1026 | ||
1027 | case MACH_PORT_RIGHT_RECEIVE: { | |
1028 | ipc_port_t port; | |
6d2010ae | 1029 | ipc_port_t request = IP_NULL; |
1c79356b A |
1030 | |
1031 | if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) | |
1032 | goto invalid_right; | |
1033 | ||
1034 | if (delta == 0) | |
1035 | goto success; | |
1036 | ||
1037 | if (delta != -1) | |
1038 | goto invalid_value; | |
1039 | ||
1040 | port = (ipc_port_t) entry->ie_object; | |
1041 | assert(port != IP_NULL); | |
1042 | ||
1043 | /* | |
1044 | * The port lock is needed for ipc_right_dncancel; | |
1045 | * otherwise, we wouldn't have to take the lock | |
1046 | * until just before dropping the space lock. | |
1047 | */ | |
1048 | ||
1049 | ip_lock(port); | |
1050 | assert(ip_active(port)); | |
1051 | assert(port->ip_receiver_name == name); | |
1052 | assert(port->ip_receiver == space); | |
1053 | ||
1054 | if (bits & MACH_PORT_TYPE_SEND) { | |
1055 | assert(IE_BITS_TYPE(bits) == | |
1056 | MACH_PORT_TYPE_SEND_RECEIVE); | |
1057 | assert(IE_BITS_UREFS(bits) > 0); | |
1058 | assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX); | |
1059 | assert(port->ip_srights > 0); | |
1060 | ||
6d2010ae A |
1061 | if (port->ip_pdrequest != NULL) { |
1062 | /* | |
1063 | * Since another task has requested a | |
1064 | * destroy notification for this port, it | |
1065 | * isn't actually being destroyed - the receive | |
1066 | * right is just being moved to another task. | |
1067 | * Since we still have one or more send rights, | |
1068 | * we need to record the loss of the receive | |
1069 | * right and enter the remaining send right | |
1070 | * into the hash table. | |
1071 | */ | |
1072 | entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE; | |
1073 | ipc_hash_insert(space, (ipc_object_t) port, | |
1074 | name, entry); | |
1075 | ip_reference(port); | |
1076 | } else { | |
1077 | /* | |
1078 | * The remaining send right turns into a | |
1079 | * dead name. Notice we don't decrement | |
1080 | * ip_srights, generate a no-senders notif, | |
1081 | * or use ipc_right_dncancel, because the | |
1082 | * port is destroyed "first". | |
1083 | */ | |
1084 | bits &= ~IE_BITS_TYPE_MASK; | |
1085 | bits |= MACH_PORT_TYPE_DEAD_NAME; | |
1086 | if (entry->ie_request) { | |
1087 | entry->ie_request = IE_REQ_NONE; | |
1088 | bits++; | |
1089 | } | |
1090 | entry->ie_bits = bits; | |
1091 | entry->ie_object = IO_NULL; | |
1c79356b | 1092 | } |
1c79356b A |
1093 | } else { |
1094 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); | |
1095 | assert(IE_BITS_UREFS(bits) == 0); | |
1096 | ||
6d2010ae | 1097 | request = ipc_right_request_cancel_macro(space, port, |
1c79356b A |
1098 | name, entry); |
1099 | entry->ie_object = IO_NULL; | |
1100 | ipc_entry_dealloc(space, name, entry); | |
1101 | } | |
1102 | is_write_unlock(space); | |
1103 | ||
1104 | ipc_port_clear_receiver(port); | |
1105 | ipc_port_destroy(port); /* consumes ref, unlocks */ | |
1106 | ||
6d2010ae A |
1107 | if (request != IP_NULL) |
1108 | ipc_notify_port_deleted(request, name); | |
1c79356b A |
1109 | break; |
1110 | } | |
1111 | ||
1112 | case MACH_PORT_RIGHT_SEND_ONCE: { | |
6d2010ae | 1113 | ipc_port_t port, request; |
1c79356b A |
1114 | |
1115 | if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) | |
1116 | goto invalid_right; | |
1117 | ||
1118 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); | |
1119 | assert(IE_BITS_UREFS(bits) == 1); | |
1120 | ||
1121 | port = (ipc_port_t) entry->ie_object; | |
1122 | assert(port != IP_NULL); | |
1123 | ||
1124 | if (ipc_right_check(space, port, name, entry)) { | |
1125 | assert(!(entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE)); | |
1126 | goto invalid_right; | |
1127 | } | |
1128 | /* port is locked and active */ | |
1129 | ||
1130 | assert(port->ip_sorights > 0); | |
1131 | ||
1132 | if ((delta > 0) || (delta < -1)) { | |
1133 | ip_unlock(port); | |
1134 | goto invalid_value; | |
1135 | } | |
1136 | ||
1137 | if (delta == 0) { | |
1138 | ip_unlock(port); | |
1139 | goto success; | |
1140 | } | |
1141 | ||
6d2010ae | 1142 | request = ipc_right_request_cancel_macro(space, port, name, entry); |
1c79356b A |
1143 | ip_unlock(port); |
1144 | ||
1145 | entry->ie_object = IO_NULL; | |
1146 | ipc_entry_dealloc(space, name, entry); | |
1147 | ||
1148 | is_write_unlock(space); | |
1149 | ||
1150 | ipc_notify_send_once(port); | |
1151 | ||
6d2010ae A |
1152 | if (request != IP_NULL) |
1153 | ipc_notify_port_deleted(request, name); | |
1c79356b A |
1154 | break; |
1155 | } | |
1156 | ||
1157 | case MACH_PORT_RIGHT_DEAD_NAME: { | |
1158 | mach_port_urefs_t urefs; | |
1159 | ||
1160 | if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { | |
1161 | ipc_port_t port; | |
1162 | ||
1163 | port = (ipc_port_t) entry->ie_object; | |
1164 | assert(port != IP_NULL); | |
1165 | ||
1166 | if (!ipc_right_check(space, port, name, entry)) { | |
1167 | /* port is locked and active */ | |
1168 | ip_unlock(port); | |
1169 | goto invalid_right; | |
1170 | } | |
1171 | bits = entry->ie_bits; | |
1172 | } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0) | |
1173 | goto invalid_right; | |
1174 | ||
1175 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); | |
1176 | assert(IE_BITS_UREFS(bits) > 0); | |
1177 | assert(entry->ie_object == IO_NULL); | |
6d2010ae | 1178 | assert(entry->ie_request == IE_REQ_NONE); |
1c79356b A |
1179 | |
1180 | urefs = IE_BITS_UREFS(bits); | |
1181 | if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) | |
1182 | goto invalid_value; | |
1183 | if (MACH_PORT_UREFS_OVERFLOW(urefs, delta)) | |
1184 | goto urefs_overflow; | |
1185 | ||
1186 | if ((urefs + delta) == 0) { | |
1187 | ipc_entry_dealloc(space, name, entry); | |
1188 | } | |
1189 | else | |
1190 | entry->ie_bits = bits + delta; | |
1191 | ||
1192 | is_write_unlock(space); | |
1193 | ||
1194 | break; | |
1195 | } | |
1196 | ||
1197 | case MACH_PORT_RIGHT_SEND: { | |
1198 | mach_port_urefs_t urefs; | |
1199 | ipc_port_t port; | |
6d2010ae | 1200 | ipc_port_t request = IP_NULL; |
1c79356b | 1201 | ipc_port_t nsrequest = IP_NULL; |
91447636 | 1202 | mach_port_mscount_t mscount = 0; |
1c79356b A |
1203 | |
1204 | if ((bits & MACH_PORT_TYPE_SEND) == 0) | |
1205 | goto invalid_right; | |
1206 | ||
1207 | /* maximum urefs for send is MACH_PORT_UREFS_MAX-1 */ | |
1208 | ||
1209 | port = (ipc_port_t) entry->ie_object; | |
1210 | assert(port != IP_NULL); | |
1211 | ||
1212 | if (ipc_right_check(space, port, name, entry)) { | |
1213 | assert((entry->ie_bits & MACH_PORT_TYPE_SEND) == 0); | |
1214 | goto invalid_right; | |
1215 | } | |
1216 | /* port is locked and active */ | |
1217 | ||
1218 | assert(port->ip_srights > 0); | |
1219 | ||
1220 | urefs = IE_BITS_UREFS(bits); | |
1221 | if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) { | |
1222 | ip_unlock(port); | |
1223 | goto invalid_value; | |
1224 | } | |
1225 | if (MACH_PORT_UREFS_OVERFLOW(urefs+1, delta)) { | |
1226 | ip_unlock(port); | |
1227 | goto urefs_overflow; | |
1228 | } | |
1229 | ||
1230 | if ((urefs + delta) == 0) { | |
1231 | if (--port->ip_srights == 0) { | |
1232 | nsrequest = port->ip_nsrequest; | |
1233 | if (nsrequest != IP_NULL) { | |
1234 | port->ip_nsrequest = IP_NULL; | |
1235 | mscount = port->ip_mscount; | |
1236 | } | |
1237 | } | |
1238 | ||
1239 | if (bits & MACH_PORT_TYPE_RECEIVE) { | |
1240 | assert(port->ip_receiver_name == name); | |
1241 | assert(port->ip_receiver == space); | |
1242 | assert(IE_BITS_TYPE(bits) == | |
1243 | MACH_PORT_TYPE_SEND_RECEIVE); | |
1244 | ||
1245 | entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK| | |
1246 | MACH_PORT_TYPE_SEND); | |
1247 | } else { | |
1248 | assert(IE_BITS_TYPE(bits) == | |
1249 | MACH_PORT_TYPE_SEND); | |
1250 | ||
6d2010ae | 1251 | request = ipc_right_request_cancel_macro(space, port, |
1c79356b A |
1252 | name, entry); |
1253 | ipc_hash_delete(space, (ipc_object_t) port, | |
1254 | name, entry); | |
1255 | ||
1256 | ip_release(port); | |
1257 | ||
1258 | entry->ie_object = IO_NULL; | |
1259 | ipc_entry_dealloc(space, name, entry); | |
1260 | } | |
1261 | } else | |
1262 | entry->ie_bits = bits + delta; | |
1263 | ||
1264 | /* even if dropped a ref, port is active */ | |
1265 | ip_unlock(port); | |
1266 | is_write_unlock(space); | |
1267 | ||
1268 | if (nsrequest != IP_NULL) | |
1269 | ipc_notify_no_senders(nsrequest, mscount); | |
1270 | ||
6d2010ae A |
1271 | if (request != IP_NULL) |
1272 | ipc_notify_port_deleted(request, name); | |
1c79356b A |
1273 | break; |
1274 | } | |
1275 | ||
1276 | default: | |
1277 | panic("ipc_right_delta: strange right"); | |
1278 | } | |
1279 | ||
1280 | return KERN_SUCCESS; | |
1281 | ||
1282 | success: | |
1283 | is_write_unlock(space); | |
1284 | return KERN_SUCCESS; | |
1285 | ||
1286 | invalid_right: | |
1287 | is_write_unlock(space); | |
1288 | return KERN_INVALID_RIGHT; | |
1289 | ||
1290 | invalid_value: | |
1291 | is_write_unlock(space); | |
1292 | return KERN_INVALID_VALUE; | |
1293 | ||
1294 | urefs_overflow: | |
1295 | is_write_unlock(space); | |
1296 | return KERN_UREFS_OVERFLOW; | |
1297 | } | |
1298 | ||
1299 | /* | |
1300 | * Routine: ipc_right_info | |
1301 | * Purpose: | |
1302 | * Retrieves information about the right. | |
1303 | * Conditions: | |
1304 | * The space is write-locked, and is unlocked upon return | |
1305 | * if the call is unsuccessful. The space must be active. | |
1306 | * Returns: | |
1307 | * KERN_SUCCESS Retrieved info; space still locked. | |
1308 | */ | |
1309 | ||
1310 | kern_return_t | |
1311 | ipc_right_info( | |
1312 | ipc_space_t space, | |
1313 | mach_port_name_t name, | |
1314 | ipc_entry_t entry, | |
1315 | mach_port_type_t *typep, | |
1316 | mach_port_urefs_t *urefsp) | |
1317 | { | |
6d2010ae | 1318 | ipc_port_t port; |
1c79356b | 1319 | ipc_entry_bits_t bits; |
6d2010ae | 1320 | mach_port_type_t type = 0; |
1c79356b A |
1321 | ipc_port_request_index_t request; |
1322 | ||
1323 | bits = entry->ie_bits; | |
6d2010ae A |
1324 | request = entry->ie_request; |
1325 | port = (ipc_port_t) entry->ie_object; | |
1c79356b | 1326 | |
6d2010ae A |
1327 | if (bits & MACH_PORT_TYPE_RECEIVE) { |
1328 | assert(IP_VALID(port)); | |
1c79356b | 1329 | |
6d2010ae A |
1330 | if (request != IE_REQ_NONE) { |
1331 | ip_lock(port); | |
1332 | assert(ip_active(port)); | |
1333 | type |= ipc_port_request_type(port, name, request); | |
1334 | ip_unlock(port); | |
1335 | } | |
1336 | ||
1337 | } else if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { | |
1338 | /* | |
1339 | * validate port is still alive - if so, get request | |
1340 | * types while we still have it locked. Otherwise, | |
1341 | * recapture the (now dead) bits. | |
1342 | */ | |
1343 | if (!ipc_right_check(space, port, name, entry)) { | |
1344 | if (request != IE_REQ_NONE) | |
1345 | type |= ipc_port_request_type(port, name, request); | |
1346 | ip_unlock(port); | |
1347 | } else { | |
1c79356b A |
1348 | bits = entry->ie_bits; |
1349 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); | |
6d2010ae | 1350 | } |
1c79356b A |
1351 | } |
1352 | ||
6d2010ae | 1353 | type |= IE_BITS_TYPE(bits); |
1c79356b A |
1354 | |
1355 | *typep = type; | |
1356 | *urefsp = IE_BITS_UREFS(bits); | |
1357 | return KERN_SUCCESS; | |
1358 | } | |
1359 | ||
1360 | /* | |
1361 | * Routine: ipc_right_copyin_check | |
1362 | * Purpose: | |
1363 | * Check if a subsequent ipc_right_copyin would succeed. | |
1364 | * Conditions: | |
1365 | * The space is locked (read or write) and active. | |
1366 | */ | |
1367 | ||
1368 | boolean_t | |
1369 | ipc_right_copyin_check( | |
91447636 A |
1370 | __assert_only ipc_space_t space, |
1371 | __unused mach_port_name_t name, | |
1372 | ipc_entry_t entry, | |
1373 | mach_msg_type_name_t msgt_name) | |
1c79356b A |
1374 | { |
1375 | ipc_entry_bits_t bits; | |
2d21ac55 A |
1376 | ipc_port_t port; |
1377 | #if CONFIG_MACF_MACH | |
1378 | task_t self = current_task(); | |
1379 | int rc = 0; | |
1380 | #endif | |
1c79356b A |
1381 | |
1382 | bits= entry->ie_bits; | |
1383 | assert(space->is_active); | |
1384 | ||
1385 | switch (msgt_name) { | |
1386 | case MACH_MSG_TYPE_MAKE_SEND: | |
2d21ac55 A |
1387 | if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) |
1388 | return FALSE; | |
1389 | ||
1390 | #if CONFIG_MACF_MACH | |
1391 | port = (ipc_port_t) entry->ie_object; | |
1392 | ip_lock(port); | |
1393 | tasklabel_lock(self); | |
1394 | rc = mac_port_check_make_send(&self->maclabel, &port->ip_label); tasklabel_unlock(self); | |
1395 | ip_unlock(port); | |
1396 | if (rc) | |
1397 | return FALSE; | |
1398 | #endif | |
1399 | break; | |
1400 | ||
1c79356b | 1401 | case MACH_MSG_TYPE_MAKE_SEND_ONCE: |
2d21ac55 A |
1402 | if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) |
1403 | return FALSE; | |
1404 | ||
1405 | #if CONFIG_MACF_MACH | |
1406 | port = (ipc_port_t) entry->ie_object; | |
1407 | ip_lock(port); | |
1408 | tasklabel_lock(self); | |
1409 | rc = mac_port_check_make_send_once(&self->maclabel, &port->ip_label); | |
1410 | tasklabel_unlock(self); | |
1411 | ip_unlock(port); | |
1412 | if (rc) | |
1413 | return FALSE; | |
1414 | #endif | |
1415 | break; | |
1416 | ||
1c79356b A |
1417 | case MACH_MSG_TYPE_MOVE_RECEIVE: |
1418 | if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) | |
1419 | return FALSE; | |
1420 | ||
2d21ac55 A |
1421 | #if CONFIG_MACF_MACH |
1422 | port = (ipc_port_t) entry->ie_object; | |
1423 | ip_lock(port); | |
1424 | tasklabel_lock(self); | |
1425 | rc = mac_port_check_move_receive(&self->maclabel, &port->ip_label); | |
1426 | tasklabel_unlock(self); | |
1427 | ip_unlock(port); | |
1428 | if (rc) | |
1429 | return FALSE; | |
1430 | #endif | |
1c79356b A |
1431 | break; |
1432 | ||
1433 | case MACH_MSG_TYPE_COPY_SEND: | |
1434 | case MACH_MSG_TYPE_MOVE_SEND: | |
1435 | case MACH_MSG_TYPE_MOVE_SEND_ONCE: { | |
1c79356b A |
1436 | boolean_t active; |
1437 | ||
1438 | if (bits & MACH_PORT_TYPE_DEAD_NAME) | |
1439 | break; | |
1440 | ||
1441 | if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) | |
1442 | return FALSE; | |
1443 | ||
1444 | port = (ipc_port_t) entry->ie_object; | |
1445 | assert(port != IP_NULL); | |
1446 | ||
1447 | ip_lock(port); | |
1448 | active = ip_active(port); | |
2d21ac55 A |
1449 | #if CONFIG_MACF_MACH |
1450 | tasklabel_lock(self); | |
1451 | switch (msgt_name) { | |
1452 | case MACH_MSG_TYPE_COPY_SEND: | |
1453 | rc = mac_port_check_copy_send(&self->maclabel, | |
1454 | &port->ip_label); | |
1455 | break; | |
1456 | case MACH_MSG_TYPE_MOVE_SEND: | |
1457 | rc = mac_port_check_move_send(&self->maclabel, | |
1458 | &port->ip_label); | |
1459 | break; | |
1460 | case MACH_MSG_TYPE_MOVE_SEND_ONCE: | |
1461 | rc = mac_port_check_move_send_once(&self->maclabel, | |
1462 | &port->ip_label); | |
1463 | break; | |
1464 | default: | |
1465 | panic("ipc_right_copyin_check: strange rights"); | |
1466 | } | |
1467 | tasklabel_unlock(self); | |
1468 | if (rc) { | |
1469 | ip_unlock(port); | |
1470 | return FALSE; | |
1471 | } | |
1472 | #endif | |
1c79356b A |
1473 | ip_unlock(port); |
1474 | ||
1475 | if (!active) { | |
1476 | break; | |
1477 | } | |
1478 | ||
1479 | if (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE) { | |
1480 | if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) | |
1481 | return FALSE; | |
1482 | } else { | |
1483 | if ((bits & MACH_PORT_TYPE_SEND) == 0) | |
1484 | return FALSE; | |
1485 | } | |
1486 | ||
1487 | break; | |
1488 | } | |
1489 | ||
1490 | default: | |
1491 | panic("ipc_right_copyin_check: strange rights"); | |
1492 | } | |
1493 | ||
1494 | return TRUE; | |
1495 | } | |
1496 | ||
1497 | /* | |
1498 | * Routine: ipc_right_copyin | |
1499 | * Purpose: | |
1500 | * Copyin a capability from a space. | |
1501 | * If successful, the caller gets a ref | |
1502 | * for the resulting object, unless it is IO_DEAD, | |
1503 | * and possibly a send-once right which should | |
1504 | * be used in a port-deleted notification. | |
1505 | * | |
1506 | * If deadok is not TRUE, the copyin operation | |
1507 | * will fail instead of producing IO_DEAD. | |
1508 | * | |
1509 | * The entry is never deallocated (except | |
1510 | * when KERN_INVALID_NAME), so the caller | |
1511 | * should deallocate the entry if its type | |
1512 | * is MACH_PORT_TYPE_NONE. | |
1513 | * Conditions: | |
1514 | * The space is write-locked and active. | |
1515 | * Returns: | |
1516 | * KERN_SUCCESS Acquired an object, possibly IO_DEAD. | |
1517 | * KERN_INVALID_RIGHT Name doesn't denote correct right. | |
1518 | */ | |
1519 | ||
1520 | kern_return_t | |
1521 | ipc_right_copyin( | |
1522 | ipc_space_t space, | |
1523 | mach_port_name_t name, | |
1524 | ipc_entry_t entry, | |
1525 | mach_msg_type_name_t msgt_name, | |
1526 | boolean_t deadok, | |
1527 | ipc_object_t *objectp, | |
1528 | ipc_port_t *sorightp) | |
1529 | { | |
1530 | ipc_entry_bits_t bits; | |
2d21ac55 A |
1531 | #if CONFIG_MACF_MACH |
1532 | task_t self = current_task(); | |
1533 | int rc; | |
1534 | #endif | |
1c79356b A |
1535 | |
1536 | bits = entry->ie_bits; | |
1537 | ||
1538 | assert(space->is_active); | |
1539 | ||
1540 | switch (msgt_name) { | |
1541 | case MACH_MSG_TYPE_MAKE_SEND: { | |
1542 | ipc_port_t port; | |
1543 | ||
1544 | if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) | |
1545 | goto invalid_right; | |
1546 | ||
1547 | port = (ipc_port_t) entry->ie_object; | |
1548 | assert(port != IP_NULL); | |
1549 | ||
1550 | ip_lock(port); | |
1551 | assert(ip_active(port)); | |
1552 | assert(port->ip_receiver_name == name); | |
1553 | assert(port->ip_receiver == space); | |
1554 | ||
2d21ac55 A |
1555 | #if CONFIG_MACF_MACH |
1556 | tasklabel_lock(self); | |
1557 | rc = mac_port_check_make_send(&self->maclabel, &port->ip_label); | |
1558 | tasklabel_unlock(self); | |
1559 | if (rc) { | |
1560 | ip_unlock(port); | |
1561 | return KERN_NO_ACCESS; | |
1562 | } | |
1563 | #endif | |
1564 | ||
1c79356b A |
1565 | port->ip_mscount++; |
1566 | port->ip_srights++; | |
1567 | ip_reference(port); | |
1568 | ip_unlock(port); | |
1569 | ||
1570 | *objectp = (ipc_object_t) port; | |
1571 | *sorightp = IP_NULL; | |
1572 | break; | |
1573 | } | |
1574 | ||
1575 | case MACH_MSG_TYPE_MAKE_SEND_ONCE: { | |
1576 | ipc_port_t port; | |
1577 | ||
1578 | if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) | |
1579 | goto invalid_right; | |
1580 | ||
1581 | port = (ipc_port_t) entry->ie_object; | |
1582 | assert(port != IP_NULL); | |
1583 | ||
1584 | ip_lock(port); | |
1585 | assert(ip_active(port)); | |
1586 | assert(port->ip_receiver_name == name); | |
1587 | assert(port->ip_receiver == space); | |
1588 | ||
2d21ac55 A |
1589 | #if CONFIG_MACF_MACH |
1590 | tasklabel_lock(self); | |
1591 | rc = mac_port_check_make_send_once(&self->maclabel, &port->ip_label); | |
1592 | tasklabel_unlock(self); | |
1593 | if (rc) { | |
1594 | ip_unlock(port); | |
1595 | return KERN_NO_ACCESS; | |
1596 | } | |
1597 | #endif | |
1598 | ||
1c79356b A |
1599 | port->ip_sorights++; |
1600 | ip_reference(port); | |
1601 | ip_unlock(port); | |
1602 | ||
1603 | *objectp = (ipc_object_t) port; | |
1604 | *sorightp = IP_NULL; | |
1605 | break; | |
1606 | } | |
1607 | ||
1608 | case MACH_MSG_TYPE_MOVE_RECEIVE: { | |
1609 | ipc_port_t port; | |
6d2010ae | 1610 | ipc_port_t request = IP_NULL; |
1c79356b A |
1611 | |
1612 | if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) | |
1613 | goto invalid_right; | |
1614 | ||
1615 | port = (ipc_port_t) entry->ie_object; | |
1616 | assert(port != IP_NULL); | |
1617 | ||
1618 | ip_lock(port); | |
1619 | assert(ip_active(port)); | |
1620 | assert(port->ip_receiver_name == name); | |
1621 | assert(port->ip_receiver == space); | |
1622 | ||
2d21ac55 A |
1623 | #if CONFIG_MACF_MACH |
1624 | tasklabel_lock(self); | |
1625 | rc = mac_port_check_move_receive(&self->maclabel, | |
1626 | &port->ip_label); | |
1627 | tasklabel_unlock(self); | |
1628 | if (rc) { | |
1629 | ip_unlock(port); | |
1630 | return KERN_NO_ACCESS; | |
1631 | } | |
1632 | #endif | |
1633 | ||
1c79356b A |
1634 | if (bits & MACH_PORT_TYPE_SEND) { |
1635 | assert(IE_BITS_TYPE(bits) == | |
1636 | MACH_PORT_TYPE_SEND_RECEIVE); | |
1637 | assert(IE_BITS_UREFS(bits) > 0); | |
1638 | assert(port->ip_srights > 0); | |
1639 | ||
1640 | ipc_hash_insert(space, (ipc_object_t) port, | |
1641 | name, entry); | |
1642 | ip_reference(port); | |
1643 | } else { | |
1644 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); | |
1645 | assert(IE_BITS_UREFS(bits) == 0); | |
1646 | ||
6d2010ae | 1647 | request = ipc_right_request_cancel_macro(space, port, |
1c79356b A |
1648 | name, entry); |
1649 | entry->ie_object = IO_NULL; | |
1650 | } | |
1651 | entry->ie_bits = bits &~ MACH_PORT_TYPE_RECEIVE; | |
1652 | ||
1653 | ipc_port_clear_receiver(port); | |
1654 | ||
1655 | port->ip_receiver_name = MACH_PORT_NULL; | |
1656 | port->ip_destination = IP_NULL; | |
1657 | ip_unlock(port); | |
1658 | ||
1659 | *objectp = (ipc_object_t) port; | |
6d2010ae | 1660 | *sorightp = request; |
1c79356b A |
1661 | break; |
1662 | } | |
1663 | ||
1664 | case MACH_MSG_TYPE_COPY_SEND: { | |
1665 | ipc_port_t port; | |
1666 | ||
1667 | if (bits & MACH_PORT_TYPE_DEAD_NAME) | |
1668 | goto copy_dead; | |
1669 | ||
1670 | /* allow for dead send-once rights */ | |
1671 | ||
1672 | if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) | |
1673 | goto invalid_right; | |
1674 | ||
1675 | assert(IE_BITS_UREFS(bits) > 0); | |
1676 | ||
1677 | port = (ipc_port_t) entry->ie_object; | |
1678 | assert(port != IP_NULL); | |
1679 | ||
1680 | if (ipc_right_check(space, port, name, entry)) { | |
1681 | bits = entry->ie_bits; | |
1682 | goto copy_dead; | |
1683 | } | |
1684 | /* port is locked and active */ | |
1685 | ||
2d21ac55 A |
1686 | #if CONFIG_MACF_MACH |
1687 | tasklabel_lock(self); | |
1688 | rc = mac_port_check_copy_send(&self->maclabel, &port->ip_label); | |
1689 | tasklabel_unlock(self); | |
1690 | if (rc) { | |
1691 | ip_unlock(port); | |
1692 | return KERN_NO_ACCESS; | |
1693 | } | |
1694 | #endif | |
1695 | ||
1c79356b A |
1696 | if ((bits & MACH_PORT_TYPE_SEND) == 0) { |
1697 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); | |
1698 | assert(port->ip_sorights > 0); | |
1699 | ||
1700 | ip_unlock(port); | |
1701 | goto invalid_right; | |
1702 | } | |
1703 | ||
1704 | assert(port->ip_srights > 0); | |
1705 | ||
1706 | port->ip_srights++; | |
1707 | ip_reference(port); | |
1708 | ip_unlock(port); | |
1709 | ||
1710 | *objectp = (ipc_object_t) port; | |
1711 | *sorightp = IP_NULL; | |
1712 | break; | |
1713 | } | |
1714 | ||
1715 | case MACH_MSG_TYPE_MOVE_SEND: { | |
1716 | ipc_port_t port; | |
6d2010ae | 1717 | ipc_port_t request = IP_NULL; |
1c79356b A |
1718 | |
1719 | if (bits & MACH_PORT_TYPE_DEAD_NAME) | |
1720 | goto move_dead; | |
1721 | ||
1722 | /* allow for dead send-once rights */ | |
1723 | ||
1724 | if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) | |
1725 | goto invalid_right; | |
1726 | ||
1727 | assert(IE_BITS_UREFS(bits) > 0); | |
1728 | ||
1729 | port = (ipc_port_t) entry->ie_object; | |
1730 | assert(port != IP_NULL); | |
1731 | ||
1732 | if (ipc_right_check(space, port, name, entry)) { | |
1733 | bits = entry->ie_bits; | |
1734 | goto move_dead; | |
1735 | } | |
1736 | /* port is locked and active */ | |
1737 | ||
2d21ac55 A |
1738 | #if CONFIG_MACF_MACH |
1739 | tasklabel_lock (self); | |
1740 | rc = mac_port_check_copy_send (&self->maclabel, &port->ip_label); | |
1741 | tasklabel_unlock (self); | |
1742 | if (rc) | |
1743 | { | |
1744 | ip_unlock (port); | |
1745 | return KERN_NO_ACCESS; | |
1746 | } | |
1747 | #endif | |
1748 | ||
1c79356b A |
1749 | if ((bits & MACH_PORT_TYPE_SEND) == 0) { |
1750 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); | |
1751 | assert(port->ip_sorights > 0); | |
1752 | ||
1753 | ip_unlock(port); | |
1754 | goto invalid_right; | |
1755 | } | |
1756 | ||
1757 | assert(port->ip_srights > 0); | |
1758 | ||
1759 | if (IE_BITS_UREFS(bits) == 1) { | |
1760 | if (bits & MACH_PORT_TYPE_RECEIVE) { | |
1761 | assert(port->ip_receiver_name == name); | |
1762 | assert(port->ip_receiver == space); | |
1763 | assert(IE_BITS_TYPE(bits) == | |
1764 | MACH_PORT_TYPE_SEND_RECEIVE); | |
1765 | ||
1766 | ip_reference(port); | |
1767 | } else { | |
1768 | assert(IE_BITS_TYPE(bits) == | |
1769 | MACH_PORT_TYPE_SEND); | |
1770 | ||
6d2010ae | 1771 | request = ipc_right_request_cancel_macro(space, port, |
1c79356b A |
1772 | name, entry); |
1773 | ipc_hash_delete(space, (ipc_object_t) port, | |
1774 | name, entry); | |
1775 | entry->ie_object = IO_NULL; | |
1776 | } | |
1777 | entry->ie_bits = bits &~ | |
1778 | (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND); | |
1779 | } else { | |
1780 | port->ip_srights++; | |
1781 | ip_reference(port); | |
1782 | entry->ie_bits = bits-1; /* decrement urefs */ | |
1783 | } | |
1784 | ||
1785 | ip_unlock(port); | |
1786 | ||
1787 | *objectp = (ipc_object_t) port; | |
6d2010ae | 1788 | *sorightp = request; |
1c79356b A |
1789 | break; |
1790 | } | |
1791 | ||
1792 | case MACH_MSG_TYPE_MOVE_SEND_ONCE: { | |
1793 | ipc_port_t port; | |
6d2010ae | 1794 | ipc_port_t request; |
1c79356b A |
1795 | |
1796 | if (bits & MACH_PORT_TYPE_DEAD_NAME) | |
1797 | goto move_dead; | |
1798 | ||
1799 | /* allow for dead send rights */ | |
1800 | ||
1801 | if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) | |
1802 | goto invalid_right; | |
1803 | ||
1804 | assert(IE_BITS_UREFS(bits) > 0); | |
1805 | ||
1806 | port = (ipc_port_t) entry->ie_object; | |
1807 | assert(port != IP_NULL); | |
1808 | ||
1809 | if (ipc_right_check(space, port, name, entry)) { | |
1810 | bits = entry->ie_bits; | |
1811 | goto move_dead; | |
1812 | } | |
1813 | /* port is locked and active */ | |
1814 | ||
2d21ac55 A |
1815 | #if CONFIG_MACF_MACH |
1816 | tasklabel_lock (self); | |
1817 | rc = mac_port_check_copy_send (&self->maclabel, &port->ip_label); | |
1818 | tasklabel_unlock (self); | |
1819 | if (rc) | |
1820 | { | |
1821 | ip_unlock (port); | |
1822 | return KERN_NO_ACCESS; | |
1823 | } | |
1824 | #endif | |
1825 | ||
1c79356b A |
1826 | if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) { |
1827 | assert(bits & MACH_PORT_TYPE_SEND); | |
1828 | assert(port->ip_srights > 0); | |
1829 | ||
1830 | ip_unlock(port); | |
1831 | goto invalid_right; | |
1832 | } | |
1833 | ||
1834 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); | |
1835 | assert(IE_BITS_UREFS(bits) == 1); | |
1836 | assert(port->ip_sorights > 0); | |
1837 | ||
6d2010ae | 1838 | request = ipc_right_request_cancel_macro(space, port, name, entry); |
1c79356b A |
1839 | ip_unlock(port); |
1840 | ||
1841 | entry->ie_object = IO_NULL; | |
1842 | entry->ie_bits = bits &~ | |
1843 | (IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND_ONCE); | |
1844 | ||
1845 | *objectp = (ipc_object_t) port; | |
6d2010ae | 1846 | *sorightp = request; |
1c79356b A |
1847 | break; |
1848 | } | |
1849 | ||
1850 | default: | |
1851 | invalid_right: | |
1852 | return KERN_INVALID_RIGHT; | |
1853 | } | |
1854 | ||
1855 | return KERN_SUCCESS; | |
1856 | ||
1857 | copy_dead: | |
1858 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); | |
1859 | assert(IE_BITS_UREFS(bits) > 0); | |
6d2010ae | 1860 | assert(entry->ie_request == IE_REQ_NONE); |
1c79356b A |
1861 | assert(entry->ie_object == 0); |
1862 | ||
1863 | if (!deadok) | |
1864 | goto invalid_right; | |
1865 | ||
1866 | *objectp = IO_DEAD; | |
1867 | *sorightp = IP_NULL; | |
1868 | return KERN_SUCCESS; | |
1869 | ||
1870 | move_dead: | |
1871 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); | |
1872 | assert(IE_BITS_UREFS(bits) > 0); | |
6d2010ae | 1873 | assert(entry->ie_request == IE_REQ_NONE); |
1c79356b A |
1874 | assert(entry->ie_object == 0); |
1875 | ||
1876 | if (!deadok) | |
1877 | goto invalid_right; | |
1878 | ||
1879 | if (IE_BITS_UREFS(bits) == 1) { | |
1880 | bits &= ~MACH_PORT_TYPE_DEAD_NAME; | |
1881 | } | |
1882 | entry->ie_bits = bits-1; /* decrement urefs */ | |
1883 | ||
1884 | *objectp = IO_DEAD; | |
1885 | *sorightp = IP_NULL; | |
1886 | return KERN_SUCCESS; | |
1887 | ||
1888 | } | |
1889 | ||
1890 | /* | |
1891 | * Routine: ipc_right_copyin_undo | |
1892 | * Purpose: | |
1893 | * Undoes the effects of an ipc_right_copyin | |
1894 | * of a send/send-once right that is dead. | |
1895 | * (Object is either IO_DEAD or a dead port.) | |
1896 | * Conditions: | |
1897 | * The space is write-locked and active. | |
1898 | */ | |
1899 | ||
1900 | void | |
1901 | ipc_right_copyin_undo( | |
1902 | ipc_space_t space, | |
1903 | mach_port_name_t name, | |
1904 | ipc_entry_t entry, | |
1905 | mach_msg_type_name_t msgt_name, | |
1906 | ipc_object_t object, | |
1907 | ipc_port_t soright) | |
1908 | { | |
1909 | ipc_entry_bits_t bits; | |
1910 | ||
1911 | bits = entry->ie_bits; | |
1912 | ||
1913 | assert(space->is_active); | |
1914 | ||
1915 | assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || | |
1916 | (msgt_name == MACH_MSG_TYPE_COPY_SEND) || | |
1917 | (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); | |
1918 | ||
1919 | if (soright != IP_NULL) { | |
1920 | assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || | |
1921 | (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); | |
1922 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); | |
1923 | assert(object != IO_DEAD); | |
1924 | ||
1925 | entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) | | |
1926 | MACH_PORT_TYPE_DEAD_NAME | 2); | |
1927 | ||
1928 | } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE) { | |
1929 | assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || | |
1930 | (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); | |
1931 | ||
1932 | entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) | | |
1933 | MACH_PORT_TYPE_DEAD_NAME | 1); | |
1934 | } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME) { | |
1935 | assert(object == IO_DEAD); | |
1936 | assert(IE_BITS_UREFS(bits) > 0); | |
1937 | ||
1938 | if (msgt_name != MACH_MSG_TYPE_COPY_SEND) { | |
1939 | assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX); | |
1940 | entry->ie_bits = bits+1; /* increment urefs */ | |
1941 | } | |
1942 | } else { | |
1943 | assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || | |
1944 | (msgt_name == MACH_MSG_TYPE_COPY_SEND)); | |
1945 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); | |
1946 | assert(object != IO_DEAD); | |
1947 | assert(entry->ie_object == object); | |
1948 | assert(IE_BITS_UREFS(bits) > 0); | |
1949 | ||
1950 | if (msgt_name != MACH_MSG_TYPE_COPY_SEND) { | |
1951 | assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX-1); | |
1952 | entry->ie_bits = bits+1; /* increment urefs */ | |
1953 | } | |
1954 | ||
1955 | /* | |
1956 | * May as well convert the entry to a dead name. | |
1957 | * (Or if it is a compat entry, destroy it.) | |
1958 | */ | |
1959 | ||
1960 | (void) ipc_right_check(space, (ipc_port_t) object, | |
1961 | name, entry); | |
1962 | /* object is dead so it is not locked */ | |
1963 | } | |
1964 | ||
1965 | /* release the reference acquired by copyin */ | |
1966 | ||
1967 | if (object != IO_DEAD) | |
1968 | ipc_object_release(object); | |
1969 | } | |
1970 | ||
1971 | /* | |
1972 | * Routine: ipc_right_copyin_two | |
1973 | * Purpose: | |
1974 | * Like ipc_right_copyin with MACH_MSG_TYPE_MOVE_SEND | |
1975 | * and deadok == FALSE, except that this moves two | |
1976 | * send rights at once. | |
1977 | * Conditions: | |
1978 | * The space is write-locked and active. | |
1979 | * The object is returned with two refs/send rights. | |
1980 | * Returns: | |
1981 | * KERN_SUCCESS Acquired an object. | |
1982 | * KERN_INVALID_RIGHT Name doesn't denote correct right. | |
1983 | */ | |
1984 | ||
1985 | kern_return_t | |
1986 | ipc_right_copyin_two( | |
1987 | ipc_space_t space, | |
1988 | mach_port_name_t name, | |
1989 | ipc_entry_t entry, | |
1990 | ipc_object_t *objectp, | |
1991 | ipc_port_t *sorightp) | |
1992 | { | |
1993 | ipc_entry_bits_t bits; | |
1994 | mach_port_urefs_t urefs; | |
1995 | ipc_port_t port; | |
6d2010ae | 1996 | ipc_port_t request = IP_NULL; |
2d21ac55 A |
1997 | #if CONFIG_MACF_MACH |
1998 | task_t self = current_task(); | |
1999 | int rc; | |
2000 | #endif | |
1c79356b A |
2001 | |
2002 | assert(space->is_active); | |
2003 | ||
2004 | bits = entry->ie_bits; | |
2005 | ||
2006 | if ((bits & MACH_PORT_TYPE_SEND) == 0) | |
2007 | goto invalid_right; | |
2008 | ||
2009 | urefs = IE_BITS_UREFS(bits); | |
2010 | if (urefs < 2) | |
2011 | goto invalid_right; | |
2012 | ||
2013 | port = (ipc_port_t) entry->ie_object; | |
2014 | assert(port != IP_NULL); | |
2015 | ||
2016 | if (ipc_right_check(space, port, name, entry)) { | |
2017 | goto invalid_right; | |
2018 | } | |
2019 | /* port is locked and active */ | |
2020 | ||
2d21ac55 A |
2021 | #if CONFIG_MACF_MACH |
2022 | tasklabel_lock(self); | |
2023 | rc = mac_port_check_copy_send(&self->maclabel, &port->ip_label); | |
2024 | tasklabel_unlock(self); | |
2025 | if (rc) { | |
2026 | ip_unlock(port); | |
2027 | return KERN_NO_ACCESS; | |
2028 | } | |
2029 | #endif | |
2030 | ||
1c79356b A |
2031 | assert(port->ip_srights > 0); |
2032 | ||
2033 | if (urefs == 2) { | |
2034 | if (bits & MACH_PORT_TYPE_RECEIVE) { | |
2035 | assert(port->ip_receiver_name == name); | |
2036 | assert(port->ip_receiver == space); | |
2037 | assert(IE_BITS_TYPE(bits) == | |
2038 | MACH_PORT_TYPE_SEND_RECEIVE); | |
2039 | ||
2040 | port->ip_srights++; | |
2041 | ip_reference(port); | |
2042 | ip_reference(port); | |
2043 | } else { | |
2044 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); | |
2045 | ||
6d2010ae | 2046 | request = ipc_right_request_cancel_macro(space, port, |
1c79356b A |
2047 | name, entry); |
2048 | ||
2049 | port->ip_srights++; | |
2050 | ip_reference(port); | |
2051 | ipc_hash_delete(space, (ipc_object_t) port, | |
2052 | name, entry); | |
2053 | entry->ie_object = IO_NULL; | |
2054 | } | |
2055 | entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND); | |
2056 | } else { | |
2057 | port->ip_srights += 2; | |
2058 | ip_reference(port); | |
2059 | ip_reference(port); | |
2060 | entry->ie_bits = bits-2; /* decrement urefs */ | |
2061 | } | |
2062 | ip_unlock(port); | |
2063 | ||
2064 | *objectp = (ipc_object_t) port; | |
6d2010ae | 2065 | *sorightp = request; |
1c79356b A |
2066 | return KERN_SUCCESS; |
2067 | ||
2068 | invalid_right: | |
2069 | return KERN_INVALID_RIGHT; | |
2070 | } | |
2071 | ||
2072 | /* | |
2073 | * Routine: ipc_right_copyout | |
2074 | * Purpose: | |
2075 | * Copyout a capability to a space. | |
2076 | * If successful, consumes a ref for the object. | |
2077 | * | |
2078 | * Always succeeds when given a newly-allocated entry, | |
2079 | * because user-reference overflow isn't a possibility. | |
2080 | * | |
2081 | * If copying out the object would cause the user-reference | |
2082 | * count in the entry to overflow, and overflow is TRUE, | |
2083 | * then instead the user-reference count is left pegged | |
2084 | * to its maximum value and the copyout succeeds anyway. | |
2085 | * Conditions: | |
2086 | * The space is write-locked and active. | |
2087 | * The object is locked and active. | |
2088 | * The object is unlocked; the space isn't. | |
2089 | * Returns: | |
2090 | * KERN_SUCCESS Copied out capability. | |
2091 | * KERN_UREFS_OVERFLOW User-refs would overflow; | |
2092 | * guaranteed not to happen with a fresh entry | |
2093 | * or if overflow=TRUE was specified. | |
2094 | */ | |
2095 | ||
2096 | kern_return_t | |
2097 | ipc_right_copyout( | |
2098 | ipc_space_t space, | |
2099 | mach_port_name_t name, | |
2100 | ipc_entry_t entry, | |
2101 | mach_msg_type_name_t msgt_name, | |
2102 | boolean_t overflow, | |
2103 | ipc_object_t object) | |
2104 | { | |
2105 | ipc_entry_bits_t bits; | |
2106 | ipc_port_t port; | |
2d21ac55 A |
2107 | #if CONFIG_MACF_MACH |
2108 | int rc; | |
2109 | #endif | |
1c79356b A |
2110 | |
2111 | bits = entry->ie_bits; | |
2112 | ||
2113 | assert(IO_VALID(object)); | |
2114 | assert(io_otype(object) == IOT_PORT); | |
2115 | assert(io_active(object)); | |
2116 | assert(entry->ie_object == object); | |
2117 | ||
2118 | port = (ipc_port_t) object; | |
2119 | ||
2120 | switch (msgt_name) { | |
2121 | case MACH_MSG_TYPE_PORT_SEND_ONCE: | |
2122 | ||
2123 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); | |
2124 | assert(port->ip_sorights > 0); | |
2125 | ||
2d21ac55 A |
2126 | #if CONFIG_MACF_MACH |
2127 | if (space->is_task) { | |
2128 | tasklabel_lock(space->is_task); | |
2129 | rc = mac_port_check_hold_send_once(&space->is_task->maclabel, | |
2130 | &port->ip_label); | |
2131 | tasklabel_unlock(space->is_task); | |
2132 | ||
2133 | if (rc) { | |
2134 | ip_unlock(port); | |
2135 | return KERN_NO_ACCESS; | |
2136 | } | |
2137 | } | |
2138 | #endif | |
1c79356b A |
2139 | /* transfer send-once right and ref to entry */ |
2140 | ip_unlock(port); | |
2141 | ||
2142 | entry->ie_bits = bits | (MACH_PORT_TYPE_SEND_ONCE | 1); | |
2143 | break; | |
2144 | ||
2145 | case MACH_MSG_TYPE_PORT_SEND: | |
2146 | assert(port->ip_srights > 0); | |
2147 | ||
2d21ac55 A |
2148 | #if CONFIG_MACF_MACH |
2149 | if (space->is_task) { | |
2150 | tasklabel_lock(space->is_task); | |
2151 | rc = mac_port_check_hold_send(&space->is_task->maclabel, | |
2152 | &port->ip_label); | |
2153 | tasklabel_unlock(space->is_task); | |
2154 | ||
2155 | if (rc) { | |
2156 | ip_unlock(port); | |
2157 | return KERN_NO_ACCESS; | |
2158 | } | |
2159 | } | |
2160 | #endif | |
2161 | ||
1c79356b A |
2162 | if (bits & MACH_PORT_TYPE_SEND) { |
2163 | mach_port_urefs_t urefs = IE_BITS_UREFS(bits); | |
2164 | ||
2165 | assert(port->ip_srights > 1); | |
2166 | assert(urefs > 0); | |
2167 | assert(urefs < MACH_PORT_UREFS_MAX); | |
2168 | ||
2169 | if (urefs+1 == MACH_PORT_UREFS_MAX) { | |
2170 | if (overflow) { | |
2171 | /* leave urefs pegged to maximum */ | |
2172 | ||
2173 | port->ip_srights--; | |
2174 | ip_release(port); | |
2175 | ip_unlock(port); | |
2176 | return KERN_SUCCESS; | |
2177 | } | |
2178 | ||
2179 | ip_unlock(port); | |
2180 | return KERN_UREFS_OVERFLOW; | |
2181 | } | |
2182 | ||
2183 | port->ip_srights--; | |
2184 | ip_release(port); | |
2185 | ip_unlock(port); | |
2186 | } else if (bits & MACH_PORT_TYPE_RECEIVE) { | |
2187 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); | |
2188 | assert(IE_BITS_UREFS(bits) == 0); | |
2189 | ||
2190 | /* transfer send right to entry */ | |
2191 | ip_release(port); | |
2192 | ip_unlock(port); | |
2193 | } else { | |
2194 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); | |
2195 | assert(IE_BITS_UREFS(bits) == 0); | |
2196 | ||
2197 | /* transfer send right and ref to entry */ | |
2198 | ip_unlock(port); | |
2199 | ||
2200 | /* entry is locked holding ref, so can use port */ | |
2201 | ||
2202 | ipc_hash_insert(space, (ipc_object_t) port, | |
2203 | name, entry); | |
2204 | } | |
2205 | ||
2206 | entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1; | |
2207 | break; | |
2208 | ||
2209 | case MACH_MSG_TYPE_PORT_RECEIVE: { | |
2210 | ipc_port_t dest; | |
2211 | ||
2212 | assert(port->ip_mscount == 0); | |
2213 | assert(port->ip_receiver_name == MACH_PORT_NULL); | |
2214 | dest = port->ip_destination; | |
2215 | ||
2d21ac55 A |
2216 | #if CONFIG_MACF_MACH |
2217 | if (space->is_task) { | |
2218 | tasklabel_lock(space->is_task); | |
2219 | rc = mac_port_check_hold_receive(&space->is_task->maclabel, | |
2220 | &port->ip_label); | |
2221 | tasklabel_unlock(space->is_task); | |
2222 | ||
2223 | if (rc) { | |
2224 | ip_unlock(port); | |
2225 | return KERN_NO_ACCESS; | |
2226 | } | |
2227 | } | |
2228 | #endif | |
2229 | ||
1c79356b A |
2230 | port->ip_receiver_name = name; |
2231 | port->ip_receiver = space; | |
2232 | ||
2233 | assert((bits & MACH_PORT_TYPE_RECEIVE) == 0); | |
2234 | ||
2235 | if (bits & MACH_PORT_TYPE_SEND) { | |
2236 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); | |
2237 | assert(IE_BITS_UREFS(bits) > 0); | |
2238 | assert(port->ip_srights > 0); | |
2239 | ||
2240 | ip_release(port); | |
2241 | ip_unlock(port); | |
2242 | ||
2243 | /* entry is locked holding ref, so can use port */ | |
2244 | ||
2245 | ipc_hash_delete(space, (ipc_object_t) port, | |
2246 | name, entry); | |
2247 | } else { | |
2248 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); | |
2249 | assert(IE_BITS_UREFS(bits) == 0); | |
2250 | ||
2251 | /* transfer ref to entry */ | |
2252 | ip_unlock(port); | |
2253 | } | |
2254 | entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE; | |
2255 | ||
2256 | if (dest != IP_NULL) | |
2257 | ipc_port_release(dest); | |
2258 | break; | |
2259 | } | |
2260 | ||
2261 | default: | |
2262 | panic("ipc_right_copyout: strange rights"); | |
2263 | } | |
2264 | ||
2265 | return KERN_SUCCESS; | |
2266 | } | |
2267 | ||
2268 | /* | |
2269 | * Routine: ipc_right_rename | |
2270 | * Purpose: | |
2271 | * Transfer an entry from one name to another. | |
2272 | * The old entry is deallocated. | |
2273 | * Conditions: | |
2274 | * The space is write-locked and active. | |
2275 | * The new entry is unused. Upon return, | |
2276 | * the space is unlocked. | |
2277 | * Returns: | |
2278 | * KERN_SUCCESS Moved entry to new name. | |
2279 | */ | |
2280 | ||
2281 | kern_return_t | |
2282 | ipc_right_rename( | |
2283 | ipc_space_t space, | |
2284 | mach_port_name_t oname, | |
2285 | ipc_entry_t oentry, | |
2286 | mach_port_name_t nname, | |
2287 | ipc_entry_t nentry) | |
2288 | { | |
2289 | ipc_port_request_index_t request = oentry->ie_request; | |
2290 | ipc_entry_bits_t bits = oentry->ie_bits; | |
2291 | ipc_object_t object = oentry->ie_object; | |
2292 | ||
2293 | assert(space->is_active); | |
2294 | assert(oname != nname); | |
2295 | ||
2296 | /* | |
2297 | * If IE_BITS_COMPAT, we can't allow the entry to be renamed | |
2298 | * if the port is dead. (This would foil ipc_port_destroy.) | |
2299 | * Instead we should fail because oentry shouldn't exist. | |
2300 | * Note IE_BITS_COMPAT implies ie_request != 0. | |
2301 | */ | |
2302 | ||
6d2010ae | 2303 | if (request != IE_REQ_NONE) { |
1c79356b A |
2304 | ipc_port_t port; |
2305 | ||
2306 | assert(bits & MACH_PORT_TYPE_PORT_RIGHTS); | |
2307 | port = (ipc_port_t) object; | |
2308 | assert(port != IP_NULL); | |
2309 | ||
2310 | if (ipc_right_check(space, port, oname, oentry)) { | |
6d2010ae | 2311 | request = IE_REQ_NONE; |
1c79356b A |
2312 | object = IO_NULL; |
2313 | bits = oentry->ie_bits; | |
2314 | assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); | |
6d2010ae | 2315 | assert(oentry->ie_request == IE_REQ_NONE); |
1c79356b A |
2316 | } else { |
2317 | /* port is locked and active */ | |
2318 | ||
6d2010ae | 2319 | ipc_port_request_rename(port, request, oname, nname); |
1c79356b | 2320 | ip_unlock(port); |
6d2010ae | 2321 | oentry->ie_request = IE_REQ_NONE; |
1c79356b A |
2322 | } |
2323 | } | |
2324 | ||
2325 | /* initialize nentry before letting ipc_hash_insert see it */ | |
2326 | ||
2327 | assert((nentry->ie_bits & IE_BITS_RIGHT_MASK) == 0); | |
2328 | nentry->ie_bits |= bits & IE_BITS_RIGHT_MASK; | |
2329 | nentry->ie_request = request; | |
2330 | nentry->ie_object = object; | |
2331 | ||
2332 | switch (IE_BITS_TYPE(bits)) { | |
2333 | case MACH_PORT_TYPE_SEND: { | |
2334 | ipc_port_t port; | |
2335 | ||
2336 | port = (ipc_port_t) object; | |
2337 | assert(port != IP_NULL); | |
2338 | ||
2339 | /* remember, there are no other share entries possible */ | |
2340 | /* or we can't do the rename. Therefore we do not need */ | |
2341 | /* to check the other subspaces */ | |
2342 | ipc_hash_delete(space, (ipc_object_t) port, oname, oentry); | |
2343 | ipc_hash_insert(space, (ipc_object_t) port, nname, nentry); | |
2344 | break; | |
2345 | } | |
2346 | ||
2347 | case MACH_PORT_TYPE_RECEIVE: | |
2348 | case MACH_PORT_TYPE_SEND_RECEIVE: { | |
2349 | ipc_port_t port; | |
2350 | ||
2351 | port = (ipc_port_t) object; | |
2352 | assert(port != IP_NULL); | |
2353 | ||
2354 | ip_lock(port); | |
2355 | assert(ip_active(port)); | |
2356 | assert(port->ip_receiver_name == oname); | |
2357 | assert(port->ip_receiver == space); | |
2358 | ||
2359 | port->ip_receiver_name = nname; | |
2360 | ip_unlock(port); | |
2361 | break; | |
2362 | } | |
2363 | ||
2364 | case MACH_PORT_TYPE_PORT_SET: { | |
2365 | ipc_pset_t pset; | |
2366 | ||
2367 | pset = (ipc_pset_t) object; | |
2368 | assert(pset != IPS_NULL); | |
2369 | ||
2370 | ips_lock(pset); | |
2371 | assert(ips_active(pset)); | |
2372 | assert(pset->ips_local_name == oname); | |
2373 | ||
2374 | pset->ips_local_name = nname; | |
2375 | ips_unlock(pset); | |
2376 | break; | |
2377 | } | |
2378 | ||
2379 | case MACH_PORT_TYPE_SEND_ONCE: | |
2380 | case MACH_PORT_TYPE_DEAD_NAME: | |
2381 | break; | |
2382 | ||
2383 | default: | |
2384 | panic("ipc_right_rename: strange rights"); | |
2385 | } | |
2386 | ||
6d2010ae | 2387 | assert(oentry->ie_request == IE_REQ_NONE); |
1c79356b A |
2388 | oentry->ie_object = IO_NULL; |
2389 | ipc_entry_dealloc(space, oname, oentry); | |
2390 | is_write_unlock(space); | |
2391 | ||
2392 | return KERN_SUCCESS; | |
2393 | } |