+ guard_failure:
+ return KERN_INVALID_RIGHT;
+}
+
+/*
+ * Routine: ipc_right_destruct
+ * Purpose:
+ * Deallocates the receive right and modifies the
+ * user-reference count for the send rights as requested.
+ * Conditions:
+ * The space is write-locked, and is unlocked upon return.
+ * The space must be active.
+ * Returns:
+ * KERN_SUCCESS Count was modified.
+ * KERN_INVALID_RIGHT Entry has wrong type.
+ * KERN_INVALID_VALUE Bad delta for the right.
+ */
+
+kern_return_t
+ipc_right_destruct(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry,
+ mach_port_delta_t srdelta,
+ uint64_t guard)
+{
+ ipc_port_t port = IP_NULL;
+ ipc_entry_bits_t bits;
+
+ mach_port_urefs_t urefs;
+ ipc_port_t request = IP_NULL;
+ ipc_port_t nsrequest = IP_NULL;
+ mach_port_mscount_t mscount = 0;
+
+ bits = entry->ie_bits;
+
+ assert(is_active(space));
+
+ if (((bits & MACH_PORT_TYPE_RECEIVE) == 0) ||
+ (srdelta && ((bits & MACH_PORT_TYPE_SEND) == 0))) {
+ is_write_unlock(space);
+ return KERN_INVALID_RIGHT;
+ }
+
+ if (srdelta > 0)
+ goto invalid_value;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+
+ /* Mach Port Guard Checking */
+ if(port->ip_guarded && (guard != port->ip_context)) {
+ uint64_t portguard = port->ip_context;
+ ip_unlock(port);
+ is_write_unlock(space);
+ mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_DESTROY);
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ /*
+ * First reduce the send rights as requested and
+ * adjust the entry->ie_bits accordingly. The
+ * ipc_entry_modified() call is made once the receive
+ * right is destroyed too.
+ */
+
+ if (srdelta) {
+
+ assert(port->ip_srights > 0);
+
+ urefs = IE_BITS_UREFS(bits);
+
+ /*
+ * Since we made sure that srdelta is negative,
+ * the check for urefs overflow is not required.
+ */
+ if (MACH_PORT_UREFS_UNDERFLOW(urefs, srdelta)) {
+ ip_unlock(port);
+ goto invalid_value;
+ }
+
+ if (urefs == MACH_PORT_UREFS_MAX) {
+ /*
+ * urefs are pegged due to an overflow
+ * only a delta removing all refs at once can change it
+ */
+ if (srdelta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX)))
+ srdelta = 0;
+ }
+
+ if ((urefs + srdelta) == 0) {
+ if (--port->ip_srights == 0) {
+ nsrequest = port->ip_nsrequest;
+ if (nsrequest != IP_NULL) {
+ port->ip_nsrequest = IP_NULL;
+ mscount = port->ip_mscount;
+ }
+ }
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_RECEIVE);
+ entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|
+ MACH_PORT_TYPE_SEND);
+ } else {
+ entry->ie_bits = bits + srdelta;
+ }
+ }
+
+ /*
+ * Now destroy the receive right. Update space and
+ * entry accordingly.
+ */
+
+ bits = entry->ie_bits;
+ if (bits & MACH_PORT_TYPE_SEND) {
+ assert(IE_BITS_UREFS(bits) > 0);
+ assert(IE_BITS_UREFS(bits) <= MACH_PORT_UREFS_MAX);
+
+ if (port->ip_pdrequest != NULL) {
+ /*
+ * Since another task has requested a
+ * destroy notification for this port, it
+ * isn't actually being destroyed - the receive
+ * right is just being moved to another task.
+ * Since we still have one or more send rights,
+ * we need to record the loss of the receive
+ * right and enter the remaining send right
+ * into the hash table.
+ */
+ ipc_entry_modified(space, name, entry);
+ entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE;
+ ipc_hash_insert(space, (ipc_object_t) port,
+ name, entry);
+ ip_reference(port);
+ } else {
+ /*
+ * The remaining send right turns into a
+ * dead name. Notice we don't decrement
+ * ip_srights, generate a no-senders notif,
+ * or use ipc_right_dncancel, because the
+ * port is destroyed "first".
+ */
+ bits &= ~IE_BITS_TYPE_MASK;
+ bits |= MACH_PORT_TYPE_DEAD_NAME;
+ if (entry->ie_request) {
+ entry->ie_request = IE_REQ_NONE;
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ bits++; /* increment urefs */
+ }
+ entry->ie_bits = bits;
+ entry->ie_object = IO_NULL;
+ ipc_entry_modified(space, name, entry);
+ }
+ } else {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
+ assert(IE_BITS_UREFS(bits) == 0);
+ request = ipc_right_request_cancel_macro(space, port,
+ name, entry);
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc(space, name, entry);
+ }
+
+ /* Unlock space */