summaryrefslogtreecommitdiff
path: root/ipc/ipc_object.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/ipc_object.c')
-rw-r--r--ipc/ipc_object.c1346
1 files changed, 1346 insertions, 0 deletions
diff --git a/ipc/ipc_object.c b/ipc/ipc_object.c
new file mode 100644
index 0000000..cdef3cd
--- /dev/null
+++ b/ipc/ipc_object.c
@@ -0,0 +1,1346 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_object.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Functions to manipulate IPC objects.
+ */
+
+#include <mach_ipc_compat.h>
+
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <mach/message.h>
+#include <ipc/port.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_hash.h>
+#include <ipc/ipc_right.h>
+#include <ipc/ipc_notify.h>
+#include <ipc/ipc_pset.h>
+
+zone_t ipc_object_zones[IOT_NUMBER];
+
+
+
+/*
+ * Routine: ipc_object_reference
+ * Purpose:
+ * Take a reference to an object.
+ */
+
+void
+ipc_object_reference(
+ ipc_object_t object)
+{
+ io_lock(object);
+ assert(object->io_references > 0);
+ io_reference(object);
+ io_unlock(object);
+}
+
+/*
+ * Routine: ipc_object_release
+ * Purpose:
+ * Release a reference to an object.
+ */
+
+void
+ipc_object_release(
+ ipc_object_t object)
+{
+ io_lock(object);
+ assert(object->io_references > 0);
+ io_release(object);
+ io_check_unlock(object);
+}
+
+/*
+ * Routine: ipc_object_translate
+ * Purpose:
+ * Look up an object in a space.
+ * Conditions:
+ * Nothing locked before. If successful, the object
+ * is returned locked. The caller doesn't get a ref.
+ * Returns:
+ * KERN_SUCCESS Objected returned locked.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote the correct right.
+ */
+
+kern_return_t
+ipc_object_translate(
+ ipc_space_t space,
+ mach_port_t name,
+ mach_port_right_t right,
+ ipc_object_t *objectp)
+{
+ ipc_entry_t entry;
+ ipc_object_t object;
+ kern_return_t kr;
+
+ kr = ipc_right_lookup_read(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is read-locked and active */
+
+ if ((entry->ie_bits & MACH_PORT_TYPE(right)) == (mach_port_right_t) 0) {
+ is_read_unlock(space);
+ return KERN_INVALID_RIGHT;
+ }
+
+ object = entry->ie_object;
+ assert(object != IO_NULL);
+
+ io_lock(object);
+ is_read_unlock(space);
+
+ *objectp = object;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_object_alloc_dead
+ * Purpose:
+ * Allocate a dead-name entry.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS The dead name is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NO_SPACE No room for an entry in the space.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_object_alloc_dead(
+ ipc_space_t space,
+ mach_port_t *namep)
+{
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+
+ kr = ipc_entry_alloc(space, namep, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked */
+
+ /* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */
+
+ assert(entry->ie_object == IO_NULL);
+ entry->ie_bits |= MACH_PORT_TYPE_DEAD_NAME | 1;
+
+ is_write_unlock(space);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_object_alloc_dead_name
+ * Purpose:
+ * Allocate a dead-name entry, with a specific name.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS The dead name is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NAME_EXISTS The name already denotes a right.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_object_alloc_dead_name(
+ ipc_space_t space,
+ mach_port_t name)
+{
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+
+ kr = ipc_entry_alloc_name(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked */
+
+ if (ipc_right_inuse(space, name, entry))
+ return KERN_NAME_EXISTS;
+
+ /* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */
+
+ assert(entry->ie_object == IO_NULL);
+ entry->ie_bits |= MACH_PORT_TYPE_DEAD_NAME | 1;
+
+ is_write_unlock(space);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_object_alloc
+ * Purpose:
+ * Allocate an object.
+ * Conditions:
+ * Nothing locked. If successful, the object is returned locked.
+ * The caller doesn't get a reference for the object.
+ * Returns:
+ * KERN_SUCCESS The object is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NO_SPACE No room for an entry in the space.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_object_alloc(
+ ipc_space_t space,
+ ipc_object_type_t otype,
+ mach_port_type_t type,
+ mach_port_urefs_t urefs,
+ mach_port_t *namep,
+ ipc_object_t *objectp)
+{
+ ipc_object_t object;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ assert(otype < IOT_NUMBER);
+ assert((type & MACH_PORT_TYPE_ALL_RIGHTS) == type);
+ assert(type != MACH_PORT_TYPE_NONE);
+ assert(urefs <= MACH_PORT_UREFS_MAX);
+
+ object = io_alloc(otype);
+ if (object == IO_NULL)
+ return KERN_RESOURCE_SHORTAGE;
+
+ if (otype == IOT_PORT) {
+ ipc_port_t port = (ipc_port_t)object;
+
+ bzero((char *)port, sizeof(*port));
+ } else if (otype == IOT_PORT_SET) {
+ ipc_pset_t pset = (ipc_pset_t)object;
+
+ bzero((char *)pset, sizeof(*pset));
+ }
+ *namep = (mach_port_t)object;
+ kr = ipc_entry_alloc(space, namep, &entry);
+ if (kr != KERN_SUCCESS) {
+ io_free(otype, object);
+ return kr;
+ }
+ /* space is write-locked */
+
+ entry->ie_bits |= type | urefs;
+ entry->ie_object = object;
+
+ io_lock_init(object);
+ io_lock(object);
+ is_write_unlock(space);
+
+ object->io_references = 1; /* for entry, not caller */
+ object->io_bits = io_makebits(TRUE, otype, 0);
+
+ *objectp = object;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_object_alloc_name
+ * Purpose:
+ * Allocate an object, with a specific name.
+ * Conditions:
+ * Nothing locked. If successful, the object is returned locked.
+ * The caller doesn't get a reference for the object.
+ * Returns:
+ * KERN_SUCCESS The object is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NAME_EXISTS The name already denotes a right.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_object_alloc_name(
+ ipc_space_t space,
+ ipc_object_type_t otype,
+ mach_port_type_t type,
+ mach_port_urefs_t urefs,
+ mach_port_t name,
+ ipc_object_t *objectp)
+{
+ ipc_object_t object;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ assert(otype < IOT_NUMBER);
+ assert((type & MACH_PORT_TYPE_ALL_RIGHTS) == type);
+ assert(type != MACH_PORT_TYPE_NONE);
+ assert(urefs <= MACH_PORT_UREFS_MAX);
+
+ object = io_alloc(otype);
+ if (object == IO_NULL)
+ return KERN_RESOURCE_SHORTAGE;
+
+ if (otype == IOT_PORT) {
+ ipc_port_t port = (ipc_port_t)object;
+
+ bzero((char *)port, sizeof(*port));
+ } else if (otype == IOT_PORT_SET) {
+ ipc_pset_t pset = (ipc_pset_t)object;
+
+ bzero((char *)pset, sizeof(*pset));
+ }
+
+ kr = ipc_entry_alloc_name(space, name, &entry);
+ if (kr != KERN_SUCCESS) {
+ io_free(otype, object);
+ return kr;
+ }
+ /* space is write-locked */
+
+ if (ipc_right_inuse(space, name, entry)) {
+ io_free(otype, object);
+ return KERN_NAME_EXISTS;
+ }
+
+ entry->ie_bits |= type | urefs;
+ entry->ie_object = object;
+
+ io_lock_init(object);
+ io_lock(object);
+ is_write_unlock(space);
+
+ object->io_references = 1; /* for entry, not caller */
+ object->io_bits = io_makebits(TRUE, otype, 0);
+
+ *objectp = object;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_object_copyin_type
+ * Purpose:
+ * Convert a send type name to a received type name.
+ */
+
+mach_msg_type_name_t
+ipc_object_copyin_type(
+ mach_msg_type_name_t msgt_name)
+{
+ switch (msgt_name) {
+ case 0:
+ return 0;
+
+ case MACH_MSG_TYPE_MOVE_RECEIVE:
+ return MACH_MSG_TYPE_PORT_RECEIVE;
+
+ case MACH_MSG_TYPE_MOVE_SEND_ONCE:
+ case MACH_MSG_TYPE_MAKE_SEND_ONCE:
+ return MACH_MSG_TYPE_PORT_SEND_ONCE;
+
+ case MACH_MSG_TYPE_MOVE_SEND:
+ case MACH_MSG_TYPE_MAKE_SEND:
+ case MACH_MSG_TYPE_COPY_SEND:
+ return MACH_MSG_TYPE_PORT_SEND;
+
+#if MACH_IPC_COMPAT
+ case MSG_TYPE_PORT:
+ return MACH_MSG_TYPE_PORT_SEND;
+
+ case MSG_TYPE_PORT_ALL:
+ return MACH_MSG_TYPE_PORT_RECEIVE;
+#endif MACH_IPC_COMPAT
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_object_copyin_type: strange rights");
+#else
+ panic("ipc_object_copyin_type: strange rights");
+#endif
+ return 0; /* in case assert/panic returns */
+ }
+}
+
+/*
+ * Routine: ipc_object_copyin
+ * Purpose:
+ * Copyin a capability from a space.
+ * If successful, the caller gets a ref
+ * for the resulting object, unless it is IO_DEAD.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Acquired an object, possibly IO_DEAD.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME Name doesn't exist in space.
+ * KERN_INVALID_RIGHT Name doesn't denote correct right.
+ */
+
+kern_return_t
+ipc_object_copyin(
+ ipc_space_t space,
+ mach_port_t name,
+ mach_msg_type_name_t msgt_name,
+ ipc_object_t *objectp)
+{
+ ipc_entry_t entry;
+ ipc_port_t soright;
+ kern_return_t kr;
+
+ /*
+ * Could first try a read lock when doing
+ * MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND,
+ * and MACH_MSG_TYPE_MAKE_SEND_ONCE.
+ */
+
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked and active */
+
+ kr = ipc_right_copyin(space, name, entry,
+ msgt_name, TRUE,
+ objectp, &soright);
+ if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+
+ if ((kr == KERN_SUCCESS) && (soright != IP_NULL))
+ ipc_notify_port_deleted(soright, name);
+
+ return kr;
+}
+
+/*
+ * Routine: ipc_object_copyin_from_kernel
+ * Purpose:
+ * Copyin a naked capability from the kernel.
+ *
+ * MACH_MSG_TYPE_MOVE_RECEIVE
+ * The receiver must be ipc_space_kernel.
+ * Consumes the naked receive right.
+ * MACH_MSG_TYPE_COPY_SEND
+ * A naked send right must be supplied.
+ * The port gains a reference, and a send right
+ * if the port is still active.
+ * MACH_MSG_TYPE_MAKE_SEND
+ * The receiver must be ipc_space_kernel.
+ * The port gains a reference and a send right.
+ * MACH_MSG_TYPE_MOVE_SEND
+ * Consumes a naked send right.
+ * MACH_MSG_TYPE_MAKE_SEND_ONCE
+ * The receiver must be ipc_space_kernel.
+ * The port gains a reference and a send-once right.
+ * MACH_MSG_TYPE_MOVE_SEND_ONCE
+ * Consumes a naked send-once right.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_object_copyin_from_kernel(
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name)
+{
+ assert(IO_VALID(object));
+
+ switch (msgt_name) {
+ case MACH_MSG_TYPE_MOVE_RECEIVE: {
+ ipc_port_t port = (ipc_port_t) object;
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name != MACH_PORT_NULL);
+ assert(port->ip_receiver == ipc_space_kernel);
+
+ /* relevant part of ipc_port_clear_receiver */
+ ipc_port_set_mscount(port, 0);
+
+ port->ip_receiver_name = MACH_PORT_NULL;
+ port->ip_destination = IP_NULL;
+ ip_unlock(port);
+ break;
+ }
+
+ case MACH_MSG_TYPE_COPY_SEND: {
+ ipc_port_t port = (ipc_port_t) object;
+
+ ip_lock(port);
+ if (ip_active(port)) {
+ assert(port->ip_srights > 0);
+ port->ip_srights++;
+ }
+ ip_reference(port);
+ ip_unlock(port);
+ break;
+ }
+
+ case MACH_MSG_TYPE_MAKE_SEND: {
+ ipc_port_t port = (ipc_port_t) object;
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name != MACH_PORT_NULL);
+ assert(port->ip_receiver == ipc_space_kernel);
+
+ ip_reference(port);
+ port->ip_mscount++;
+ port->ip_srights++;
+ ip_unlock(port);
+ break;
+ }
+
+ case MACH_MSG_TYPE_MOVE_SEND:
+ /* move naked send right into the message */
+ break;
+
+ case MACH_MSG_TYPE_MAKE_SEND_ONCE: {
+ ipc_port_t port = (ipc_port_t) object;
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name != MACH_PORT_NULL);
+ assert(port->ip_receiver == ipc_space_kernel);
+
+ ip_reference(port);
+ port->ip_sorights++;
+ ip_unlock(port);
+ break;
+ }
+
+ case MACH_MSG_TYPE_MOVE_SEND_ONCE:
+ /* move naked send-once right into the message */
+ break;
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_object_copyin_from_kernel: strange rights");
+#else
+ panic("ipc_object_copyin_from_kernel: strange rights");
+#endif
+ }
+}
+
+/*
+ * Routine: ipc_object_destroy
+ * Purpose:
+ * Destroys a naked capability.
+ * Consumes a ref for the object.
+ *
+ * A receive right should be in limbo or in transit.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_object_destroy(
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name)
+{
+ assert(IO_VALID(object));
+ assert(io_otype(object) == IOT_PORT);
+
+ switch (msgt_name) {
+ case MACH_MSG_TYPE_PORT_SEND:
+ ipc_port_release_send((ipc_port_t) object);
+ break;
+
+ case MACH_MSG_TYPE_PORT_SEND_ONCE:
+ ipc_notify_send_once((ipc_port_t) object);
+ break;
+
+ case MACH_MSG_TYPE_PORT_RECEIVE:
+ ipc_port_release_receive((ipc_port_t) object);
+ break;
+
+ default:
+ panic("ipc_object_destroy: strange rights");
+ }
+}
+
+/*
+ * Routine: ipc_object_copyout
+ * Purpose:
+ * Copyout a capability, placing it into a space.
+ * If successful, consumes a ref for the object.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Copied out object, consumed ref.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_CAPABILITY The object is dead.
+ * KERN_NO_SPACE No room in space for another right.
+ * KERN_RESOURCE_SHORTAGE No memory available.
+ * KERN_UREFS_OVERFLOW Urefs limit exceeded
+ * and overflow wasn't specified.
+ */
+
+kern_return_t
+ipc_object_copyout(
+ ipc_space_t space,
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name,
+ boolean_t overflow,
+ mach_port_t *namep)
+{
+ mach_port_t name;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ assert(IO_VALID(object));
+ assert(io_otype(object) == IOT_PORT);
+
+ is_write_lock(space);
+
+ for (;;) {
+ if (!space->is_active) {
+ is_write_unlock(space);
+ return KERN_INVALID_TASK;
+ }
+
+ if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
+ ipc_right_reverse(space, object, &name, &entry)) {
+ /* object is locked and active */
+
+ assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
+ break;
+ }
+
+ kr = ipc_entry_get(space, &name, &entry);
+ if (kr != KERN_SUCCESS) {
+ /* unlocks/locks space, so must start again */
+
+ kr = ipc_entry_grow_table(space);
+ if (kr != KERN_SUCCESS)
+ return kr; /* space is unlocked */
+
+ continue;
+ }
+
+ assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
+ assert(entry->ie_object == IO_NULL);
+
+ io_lock(object);
+ if (!io_active(object)) {
+ io_unlock(object);
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ entry->ie_object = object;
+ break;
+ }
+
+ /* space is write-locked and active, object is locked and active */
+
+ kr = ipc_right_copyout(space, name, entry,
+ msgt_name, overflow, object);
+ /* object is unlocked */
+ is_write_unlock(space);
+
+ if (kr == KERN_SUCCESS)
+ *namep = name;
+ return kr;
+}
+
+#if 0
+/* XXX same, but don't check for already-existing send rights */
+kern_return_t
+ipc_object_copyout_multiname(space, object, namep)
+ ipc_space_t space;
+ ipc_object_t object;
+ mach_port_t *namep;
+{
+ mach_port_t name;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ assert(IO_VALID(object));
+ assert(io_otype(object) == IOT_PORT);
+
+ is_write_lock(space);
+
+ for (;;) {
+ if (!space->is_active) {
+ is_write_unlock(space);
+ return KERN_INVALID_TASK;
+ }
+
+ kr = ipc_entry_get(space, &name, &entry);
+ if (kr != KERN_SUCCESS) {
+ /* unlocks/locks space, so must start again */
+
+ kr = ipc_entry_grow_table(space);
+ if (kr != KERN_SUCCESS)
+ return kr; /* space is unlocked */
+
+ continue;
+ }
+
+ assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
+ assert(entry->ie_object == IO_NULL);
+
+ io_lock(object);
+ if (!io_active(object)) {
+ io_unlock(object);
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ entry->ie_object = object;
+ break;
+ }
+
+ /* space is write-locked and active, object is locked and active */
+
+ kr = ipc_right_copyout_multiname(space, name, entry, object);
+ /* object is unlocked */
+ is_write_unlock(space);
+
+ if (kr == KERN_SUCCESS)
+ *namep = name;
+ return kr;
+}
+#endif 0
+
+/*
+ * Routine: ipc_object_copyout_name
+ * Purpose:
+ * Copyout a capability, placing it into a space.
+ * The specified name is used for the capability.
+ * If successful, consumes a ref for the object.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Copied out object, consumed ref.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_CAPABILITY The object is dead.
+ * KERN_RESOURCE_SHORTAGE No memory available.
+ * KERN_UREFS_OVERFLOW Urefs limit exceeded
+ * and overflow wasn't specified.
+ * KERN_RIGHT_EXISTS Space has rights under another name.
+ * KERN_NAME_EXISTS Name is already used.
+ */
+
+kern_return_t
+ipc_object_copyout_name(
+ ipc_space_t space,
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name,
+ boolean_t overflow,
+ mach_port_t name)
+{
+ mach_port_t oname;
+ ipc_entry_t oentry;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ assert(IO_VALID(object));
+ assert(io_otype(object) == IOT_PORT);
+
+ kr = ipc_entry_alloc_name(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked and active */
+
+ if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
+ ipc_right_reverse(space, object, &oname, &oentry)) {
+ /* object is locked and active */
+
+ if (name != oname) {
+ io_unlock(object);
+
+ if (IE_BITS_TYPE(entry->ie_bits)
+ == MACH_PORT_TYPE_NONE)
+ ipc_entry_dealloc(space, name, entry);
+
+ is_write_unlock(space);
+ return KERN_RIGHT_EXISTS;
+ }
+
+ assert(entry == oentry);
+ assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
+ } else {
+ if (ipc_right_inuse(space, name, entry))
+ return KERN_NAME_EXISTS;
+
+ assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
+ assert(entry->ie_object == IO_NULL);
+
+ io_lock(object);
+ if (!io_active(object)) {
+ io_unlock(object);
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ entry->ie_object = object;
+ }
+
+ /* space is write-locked and active, object is locked and active */
+
+ kr = ipc_right_copyout(space, name, entry,
+ msgt_name, overflow, object);
+ /* object is unlocked */
+ is_write_unlock(space);
+ return kr;
+}
+
+/*
+ * Routine: ipc_object_copyout_dest
+ * Purpose:
+ * Translates/consumes the destination right of a message.
+ * This is unlike normal copyout because the right is consumed
+ * in a funny way instead of being given to the receiving space.
+ * The receiver gets his name for the port, if he has receive
+ * rights, otherwise MACH_PORT_NULL.
+ * Conditions:
+ * The object is locked and active. Nothing else locked.
+ * The object is unlocked and loses a reference.
+ */
+
+void
+ipc_object_copyout_dest(
+ ipc_space_t space,
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name,
+ mach_port_t *namep)
+{
+ mach_port_t name;
+
+ assert(IO_VALID(object));
+ assert(io_active(object));
+
+ io_release(object);
+
+ /*
+ * If the space is the receiver/owner of the object,
+ * then we quietly consume the right and return
+ * the space's name for the object. Otherwise
+ * we destroy the right and return MACH_PORT_NULL.
+ */
+
+ switch (msgt_name) {
+ case MACH_MSG_TYPE_PORT_SEND: {
+ ipc_port_t port = (ipc_port_t) object;
+ ipc_port_t nsrequest = IP_NULL;
+ mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */
+
+ assert(port->ip_srights > 0);
+ if (--port->ip_srights == 0) {
+ nsrequest = port->ip_nsrequest;
+ if (nsrequest != IP_NULL) {
+ port->ip_nsrequest = IP_NULL;
+ mscount = port->ip_mscount;
+ }
+ }
+
+ if (port->ip_receiver == space)
+ name = port->ip_receiver_name;
+ else
+ name = MACH_PORT_NULL;
+
+ ip_unlock(port);
+
+ if (nsrequest != IP_NULL)
+ ipc_notify_no_senders(nsrequest, mscount);
+
+ break;
+ }
+
+ case MACH_MSG_TYPE_PORT_SEND_ONCE: {
+ ipc_port_t port = (ipc_port_t) object;
+
+ assert(port->ip_sorights > 0);
+
+ if (port->ip_receiver == space) {
+ /* quietly consume the send-once right */
+
+ port->ip_sorights--;
+ name = port->ip_receiver_name;
+ ip_unlock(port);
+ } else {
+ /*
+ * A very bizarre case. The message
+ * was received, but before this copyout
+ * happened the space lost receive rights.
+ * We can't quietly consume the soright
+ * out from underneath some other task,
+ * so generate a send-once notification.
+ */
+
+ ip_reference(port); /* restore ref */
+ ip_unlock(port);
+
+ ipc_notify_send_once(port);
+ name = MACH_PORT_NULL;
+ }
+
+ break;
+ }
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_object_copyout_dest: strange rights");
+#else
+ panic("ipc_object_copyout_dest: strange rights");
+#endif
+
+ }
+
+ *namep = name;
+}
+
+/*
+ * Routine: ipc_object_rename
+ * Purpose:
+ * Rename an entry in a space.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Renamed the entry.
+ * KERN_INVALID_TASK The space was dead.
+ * KERN_INVALID_NAME oname didn't denote an entry.
+ * KERN_NAME_EXISTS nname already denoted an entry.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate new entry.
+ */
+
+kern_return_t
+ipc_object_rename(
+ ipc_space_t space,
+ mach_port_t oname,
+ mach_port_t nname)
+{
+ ipc_entry_t oentry, nentry;
+ kern_return_t kr;
+
+ kr = ipc_entry_alloc_name(space, nname, &nentry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked and active */
+
+ if (ipc_right_inuse(space, nname, nentry)) {
+ /* space is unlocked */
+ return KERN_NAME_EXISTS;
+ }
+
+ /* don't let ipc_entry_lookup see the uninitialized new entry */
+
+ if ((oname == nname) ||
+ ((oentry = ipc_entry_lookup(space, oname)) == IE_NULL)) {
+ ipc_entry_dealloc(space, nname, nentry);
+ is_write_unlock(space);
+ return KERN_INVALID_NAME;
+ }
+
+ kr = ipc_right_rename(space, oname, oentry, nname, nentry);
+ /* space is unlocked */
+ return kr;
+}
+
+#if MACH_IPC_COMPAT
+
+/*
+ * Routine: ipc_object_copyout_type_compat
+ * Purpose:
+ * Convert a carried type name to an old type name.
+ */
+
+mach_msg_type_name_t
+ipc_object_copyout_type_compat(msgt_name)
+ mach_msg_type_name_t msgt_name;
+{
+ switch (msgt_name) {
+ case MACH_MSG_TYPE_PORT_SEND:
+ case MACH_MSG_TYPE_PORT_SEND_ONCE:
+ return MSG_TYPE_PORT;
+
+ case MACH_MSG_TYPE_PORT_RECEIVE:
+ return MSG_TYPE_PORT_ALL;
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_object_copyout_type_compat: strange rights");
+#else
+ panic("ipc_object_copyout_type_compat: strange rights");
+#endif
+ }
+}
+
+/*
+ * Routine: ipc_object_copyin_compat
+ * Purpose:
+ * Copyin a capability from a space.
+ * If successful, the caller gets a ref
+ * for the resulting object, which is always valid.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Acquired a valid object.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME Name doesn't exist in space.
+ * KERN_INVALID_RIGHT Name doesn't denote correct right.
+ */
+
+kern_return_t
+ipc_object_copyin_compat(space, name, msgt_name, dealloc, objectp)
+ ipc_space_t space;
+ mach_port_t name;
+ mach_msg_type_name_t msgt_name;
+ boolean_t dealloc;
+ ipc_object_t *objectp;
+{
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked and active */
+
+ kr = ipc_right_copyin_compat(space, name, entry,
+ msgt_name, dealloc, objectp);
+ /* space is unlocked */
+ return kr;
+}
+
+/*
+ * Routine: ipc_object_copyin_header
+ * Purpose:
+ * Copyin a capability from a space.
+ * If successful, the caller gets a ref
+ * for the resulting object, which is always valid.
+ * The type of the acquired capability is returned.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Acquired a valid object.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME Name doesn't exist in space.
+ * KERN_INVALID_RIGHT Name doesn't denote correct right.
+ */
+
+kern_return_t
+ipc_object_copyin_header(space, name, objectp, msgt_namep)
+ ipc_space_t space;
+ mach_port_t name;
+ ipc_object_t *objectp;
+ mach_msg_type_name_t *msgt_namep;
+{
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked and active */
+
+ kr = ipc_right_copyin_header(space, name, entry,
+ objectp, msgt_namep);
+ /* space is unlocked */
+ return kr;
+}
+
+/*
+ * Routine: ipc_object_copyout_compat
+ * Purpose:
+ * Copyout a capability, placing it into a space.
+ * If successful, consumes a ref for the object.
+ *
+ * Marks new entries with IE_BITS_COMPAT.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Copied out object, consumed ref.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_CAPABILITY The object is dead.
+ * KERN_NO_SPACE No room in space for another right.
+ * KERN_RESOURCE_SHORTAGE No memory available.
+ */
+
+kern_return_t
+ipc_object_copyout_compat(space, object, msgt_name, namep)
+ ipc_space_t space;
+ ipc_object_t object;
+ mach_msg_type_name_t msgt_name;
+ mach_port_t *namep;
+{
+ mach_port_t name;
+ ipc_entry_t entry;
+ ipc_port_t port;
+ kern_return_t kr;
+
+ assert(IO_VALID(object));
+ assert(io_otype(object) == IOT_PORT);
+ port = (ipc_port_t) object;
+
+ is_write_lock(space);
+
+ for (;;) {
+ ipc_port_request_index_t request;
+
+ if (!space->is_active) {
+ is_write_unlock(space);
+ return KERN_INVALID_TASK;
+ }
+
+ if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
+ ipc_right_reverse(space, (ipc_object_t) port,
+ &name, &entry)) {
+ /* port is locked and active */
+
+ assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
+ break;
+ }
+
+ kr = ipc_entry_get(space, &name, &entry);
+ if (kr != KERN_SUCCESS) {
+ /* unlocks/locks space, so must start again */
+
+ kr = ipc_entry_grow_table(space);
+ if (kr != KERN_SUCCESS)
+ return kr; /* space is unlocked */
+
+ continue;
+ }
+
+ assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
+ assert(entry->ie_object == IO_NULL);
+
+ ip_lock(port);
+ if (!ip_active(port)) {
+ ip_unlock(port);
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ kr = ipc_port_dnrequest(port, name, ipr_spacem(space),
+ &request);
+ if (kr != KERN_SUCCESS) {
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+
+ kr = ipc_port_dngrow(port);
+ /* port is unlocked */
+ if (kr != KERN_SUCCESS)
+ return kr;
+
+ is_write_lock(space);
+ continue;
+ }
+
+ is_reference(space); /* for dnrequest */
+ entry->ie_object = (ipc_object_t) port;
+ entry->ie_request = request;
+ entry->ie_bits |= IE_BITS_COMPAT;
+ break;
+ }
+
+ /* space is write-locked and active, port is locked and active */
+
+ kr = ipc_right_copyout(space, name, entry,
+ msgt_name, TRUE, (ipc_object_t) port);
+ /* object is unlocked */
+ is_write_unlock(space);
+
+ if (kr == KERN_SUCCESS)
+ *namep = name;
+ return kr;
+}
+
+/*
+ * Routine: ipc_object_copyout_name_compat
+ * Purpose:
+ * Copyout a capability, placing it into a space.
+ * The specified name is used for the capability.
+ * If successful, consumes a ref for the object.
+ *
+ * Like ipc_object_copyout_name, except that
+ * the name can't be in use at all, even for the same
+ * port, and IE_BITS_COMPAT gets turned on.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Copied out object, consumed ref.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_CAPABILITY The object is dead.
+ * KERN_RESOURCE_SHORTAGE No memory available.
+ * KERN_RIGHT_EXISTS Space has rights under another name.
+ * KERN_NAME_EXISTS Name is already used.
+ */
+
+kern_return_t
+ipc_object_copyout_name_compat(space, object, msgt_name, name)
+ ipc_space_t space;
+ ipc_object_t object;
+ mach_msg_type_name_t msgt_name;
+ mach_port_t name;
+{
+ ipc_entry_t entry;
+ ipc_port_t port;
+ kern_return_t kr;
+
+ assert(IO_VALID(object));
+ assert(io_otype(object) == IOT_PORT);
+ port = (ipc_port_t) object;
+
+ for (;;) {
+ mach_port_t oname;
+ ipc_entry_t oentry;
+ ipc_port_request_index_t request;
+
+ kr = ipc_entry_alloc_name(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked and active */
+
+ if (ipc_right_inuse(space, name, entry))
+ return KERN_NAME_EXISTS; /* space is unlocked */
+
+ assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
+ assert(entry->ie_object == IO_NULL);
+
+ if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
+ ipc_right_reverse(space, (ipc_object_t) port,
+ &oname, &oentry)) {
+ /* port is locked and active */
+
+ ip_unlock(port);
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+ return KERN_RIGHT_EXISTS;
+ }
+
+ ip_lock(port);
+ if (!ip_active(port)) {
+ ip_unlock(port);
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ kr = ipc_port_dnrequest(port, name, ipr_spacem(space),
+ &request);
+ if (kr != KERN_SUCCESS) {
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+
+ kr = ipc_port_dngrow(port);
+ /* port is unlocked */
+ if (kr != KERN_SUCCESS)
+ return kr;
+
+ continue;
+ }
+
+ is_reference(space); /* for dnrequest */
+ entry->ie_object = (ipc_object_t) port;
+ entry->ie_request = request;
+ entry->ie_bits |= IE_BITS_COMPAT;
+ break;
+ }
+
+ /* space is write-locked and active, port is locked and active */
+
+ kr = ipc_right_copyout(space, name, entry,
+ msgt_name, TRUE, (ipc_object_t) port);
+ /* object is unlocked */
+ is_write_unlock(space);
+
+ assert(kr == KERN_SUCCESS);
+ return kr;
+}
+
+#endif MACH_IPC_COMPAT
+
+#include <mach_kdb.h>
+
+
+#if MACH_KDB
+#define printf kdbprintf
+
+/*
+ * Routine: ipc_object_print
+ * Purpose:
+ * Pretty-print an object for kdb.
+ */
+
+char *ikot_print_array[IKOT_MAX_TYPE] = {
+ "(NONE) ",
+ "(THREAD) ",
+ "(TASK) ",
+ "(HOST) ",
+ "(HOST_PRIV) ",
+ "(PROCESSOR) ",
+ "(PSET) ",
+ "(PSET_NAME) ",
+ "(PAGER) ",
+ "(PAGER_REQUEST) ",
+ "(DEVICE) ", /* 10 */
+ "(XMM_OBJECT) ",
+ "(XMM_PAGER) ",
+ "(XMM_KERNEL) ",
+ "(XMM_REPLY) ",
+ "(PAGER_TERMINATING)",
+ "(PAGING_NAME) ",
+ "(HOST_SECURITY) ",
+ "(LEDGER) ",
+ "(MASTER_DEVICE) ",
+ "(ACTIVATION) ", /* 20 */
+ "(SUBSYSTEM) ",
+ "(IO_DONE_QUEUE) ",
+ "(SEMAPHORE) ",
+ "(LOCK_SET) ",
+ "(CLOCK) ",
+ "(CLOCK_CTRL) ", /* 26 */
+ /* << new entries here */
+ "(UNKNOWN) " /* magic catchall */
+}; /* Please keep in sync with kern/ipc_kobject.h */
+
+void
+ipc_object_print(
+ ipc_object_t object)
+{
+ int kotype;
+
+ iprintf("%s", io_active(object) ? "active" : "dead");
+ printf(", refs=%d", object->io_references);
+ printf(", otype=%d", io_otype(object));
+ kotype = io_kotype(object);
+ if (kotype >= 0 && kotype < IKOT_MAX_TYPE)
+ printf(", kotype=%d %s\n", io_kotype(object),
+ ikot_print_array[kotype]);
+ else
+ printf(", kotype=0x%x %s\n", io_kotype(object),
+ ikot_print_array[IKOT_UNKNOWN]);
+}
+
+#endif /* MACH_KDB */