+
+/*
+ * Routine: ipc_right_copyin_two
+ * Purpose:
+ * Like ipc_right_copyin with two dispositions,
+ * each of which results in a send or send-once right,
+ * and deadok = FALSE.
+ * Conditions:
+ * The space is write-locked and active.
+ * The object is returned with two refs/rights.
+ * Msgt_one refers to the dest_type
+ * Returns:
+ * KERN_SUCCESS Acquired an object.
+ * KERN_INVALID_RIGHT Name doesn't denote correct right(s).
+ * KERN_INVALID_CAPABILITY Name doesn't denote correct right for msgt_two.
+ */
+kern_return_t
+ipc_right_copyin_two(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry,
+ mach_msg_type_name_t msgt_one,
+ mach_msg_type_name_t msgt_two,
+ ipc_object_t *objectp,
+ ipc_port_t *sorightp,
+ ipc_port_t *releasep)
+{
+ kern_return_t kr;
+ int assertcnt = 0;
+
+ assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_one));
+ assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_two));
+
+ /*
+ * This is a little tedious to make atomic, because
+ * there are 25 combinations of valid dispositions.
+ * However, most are easy.
+ */
+
+ /*
+ * If either is move-sonce, then there must be an error.
+ */
+ if (msgt_one == MACH_MSG_TYPE_MOVE_SEND_ONCE ||
+ msgt_two == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
+ return KERN_INVALID_RIGHT;
+ }
+
+ if ((msgt_one == MACH_MSG_TYPE_MAKE_SEND) ||
+ (msgt_one == MACH_MSG_TYPE_MAKE_SEND_ONCE) ||
+ (msgt_two == MACH_MSG_TYPE_MAKE_SEND) ||
+ (msgt_two == MACH_MSG_TYPE_MAKE_SEND_ONCE)) {
+ /*
+ * One of the dispositions needs a receive right.
+ *
+ * If the copyin below succeeds, we know the receive
+ * right is there (because the pre-validation of
+ * the second disposition already succeeded in our
+ * caller).
+ *
+ * Hence the port is not in danger of dying.
+ */
+ ipc_object_t object_two;
+
+ kr = ipc_right_copyin(space, name, entry,
+ msgt_one, IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND,
+ objectp, sorightp, releasep,
+ &assertcnt, 0, NULL);
+ assert(assertcnt == 0);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ assert(IO_VALID(*objectp));
+ assert(*sorightp == IP_NULL);
+ assert(*releasep == IP_NULL);
+
+ /*
+ * Now copyin the second (previously validated)
+ * disposition. The result can't be a dead port,
+ * as no valid disposition can make us lose our
+ * receive right.
+ */
+ kr = ipc_right_copyin(space, name, entry,
+ msgt_two, IPC_RIGHT_COPYIN_FLAGS_NONE,
+ &object_two, sorightp, releasep,
+ &assertcnt, 0, NULL);
+ assert(assertcnt == 0);
+ assert(kr == KERN_SUCCESS);
+ assert(*sorightp == IP_NULL);
+ assert(*releasep == IP_NULL);
+ assert(object_two == *objectp);
+ assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE);
+ } else if ((msgt_one == MACH_MSG_TYPE_MOVE_SEND) &&
+ (msgt_two == MACH_MSG_TYPE_MOVE_SEND)) {
+ /*
+ * This is an easy case. Just use our
+ * handy-dandy special-purpose copyin call
+ * to get two send rights for the price of one.
+ */
+ kr = ipc_right_copyin_two_move_sends(space, name, entry,
+ objectp, sorightp,
+ releasep);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+ } else {
+ mach_msg_type_name_t msgt_name;
+
+ /*
+ * Must be either a single move-send and a
+ * copy-send, or two copy-send dispositions.
+ * Use the disposition with the greatest side
+ * effects for the actual copyin - then just
+ * duplicate the send right you get back.
+ */
+ if (msgt_one == MACH_MSG_TYPE_MOVE_SEND ||
+ msgt_two == MACH_MSG_TYPE_MOVE_SEND) {
+ msgt_name = MACH_MSG_TYPE_MOVE_SEND;
+ } else {
+ msgt_name = MACH_MSG_TYPE_COPY_SEND;
+ }
+
+ kr = ipc_right_copyin(space, name, entry,
+ msgt_name, IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND,
+ objectp, sorightp, releasep,
+ &assertcnt, 0, NULL);
+ assert(assertcnt == 0);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ /*
+ * Copy the right we got back. If it is dead now,
+ * that's OK. Neither right will be usable to send
+ * a message anyway.
+ */
+ (void)ipc_port_copy_send(ip_object_to_port(*objectp));
+ }
+
+ return KERN_SUCCESS;
+}
+
+