1/*
2 * Copyright (c) 2008, 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
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
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <mach/mach_types.h>
29#include <mach/notify.h>
30#include <ipc/ipc_port.h>
31#include <kern/ipc_kobject.h>
32#include <kern/ipc_misc.h>
33
34#include <mach/mach_port.h>
35#include <mach/vm_map.h>
36#include <vm/vm_map.h>
37#include <vm/vm_kern.h>
38
39extern void fileport_releasefg(struct fileglob *);
40
41/*
42 * fileport_alloc
43 *
44 * Description: Obtain a send right for the given fileglob, which must be
45 * referenced.
46 *
47 * Parameters: fg A fileglob.
48 *
49 * Returns: Port of type IKOT_FILEPORT with fileglob set as its kobject.
50 * Port is returned with a send right.
51 */
52ipc_port_t
53fileport_alloc(struct fileglob *fg)
54{
55 return ipc_kobject_alloc_port(kobject: (ipc_kobject_t)fg, type: IKOT_FILEPORT,
56 options: IPC_KOBJECT_ALLOC_MAKE_SEND | IPC_KOBJECT_ALLOC_NSREQUEST);
57}
58
59
60/*
61 * fileport_get_fileglob
62 *
63 * Description: Obtain the fileglob associated with a given port.
64 *
65 * Parameters: port A Mach port of type IKOT_FILEPORT.
66 *
67 * Returns: NULL The given Mach port did not reference a
68 * fileglob.
69 * !NULL The fileglob that is associated with the
70 * Mach port.
71 *
72 * Notes: The caller must have a reference on the fileport.
73 */
74struct fileglob *
75fileport_port_to_fileglob(ipc_port_t port)
76{
77 if (IP_VALID(port)) {
78 return ipc_kobject_get_stable(port, type: IKOT_FILEPORT);
79 }
80 return NULL;
81}
82
83
84/*
85 * fileport_no_senders
86 *
87 * Description: Handle a no-senders notification for a fileport. Unless
88 * the message is spoofed, destroys the port and releases
89 * its reference on the fileglob.
90 *
91 * Parameters: msg A Mach no-senders notification message.
92 */
93static void
94fileport_no_senders(ipc_port_t port, mach_port_mscount_t mscount)
95{
96 struct fileglob *fg;
97
98 fg = ipc_kobject_dealloc_port(port, mscount, type: IKOT_FILEPORT);
99
100 fileport_releasefg(fg);
101}
102
103IPC_KOBJECT_DEFINE(IKOT_FILEPORT,
104 .iko_op_stable = true,
105 .iko_op_no_senders = fileport_no_senders);
106
107/*
108 * fileport_invoke
109 *
110 * Description: Invoke a function with the fileglob underlying the fileport.
111 * Returns the error code related to the fileglob lookup.
112 *
113 * Parameters: task The target task
114 * action The function to invoke with the fileglob
115 * arg Anonymous pointer to caller state
116 * rval The value returned from calling 'action'
117 */
118kern_return_t
119fileport_invoke(task_t task, mach_port_name_t name,
120 int (*action)(mach_port_name_t, struct fileglob *, void *),
121 void *arg, int *rval)
122{
123 kern_return_t kr;
124 ipc_port_t fileport;
125 struct fileglob *fg;
126
127 kr = ipc_object_copyin(space: task->itk_space, name,
128 MACH_MSG_TYPE_COPY_SEND, objectp: (ipc_object_t *)&fileport, context: 0, NULL,
129 copyin_flags: IPC_OBJECT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND);
130 if (kr != KERN_SUCCESS) {
131 return kr;
132 }
133
134 if ((fg = fileport_port_to_fileglob(port: fileport)) != NULL) {
135 *rval = (*action)(name, fg, arg);
136 } else {
137 kr = KERN_FAILURE;
138 }
139 ipc_port_release_send(port: fileport);
140 return kr;
141}
142
143/*
144 * fileport_walk
145 *
146 * Description: Invoke the action function on every fileport in the task.
147 *
148 * Parameters: task The target task
149 * countp Returns how many ports were found
150 * action The function to invoke on each fileport
151 */
152kern_return_t
153fileport_walk(task_t task, size_t *countp,
154 bool (^cb)(size_t i, mach_port_name_t, struct fileglob *))
155{
156 const uint32_t BATCH_SIZE = 4 << 10;
157 ipc_space_t space = task->itk_space;
158 ipc_entry_table_t table;
159 ipc_entry_num_t index;
160 ipc_entry_t entry;
161 size_t count = 0;
162
163 is_read_lock(space);
164 if (!is_active(space)) {
165 is_read_unlock(space);
166 return KERN_INVALID_TASK;
167 }
168
169 table = is_active_table(space);
170 entry = ipc_entry_table_base(array: table);
171
172 /* skip the first element which is not a real entry */
173 index = 1;
174 entry = ipc_entry_table_next_elem(array: table, e: entry);
175
176 for (;;) {
177 ipc_entry_bits_t bits = entry->ie_bits;
178 ipc_object_t io = entry->ie_object;
179 mach_port_name_t name;
180 struct fileglob *fg;
181
182 if (IE_BITS_TYPE(bits) & MACH_PORT_TYPE_SEND) {
183 name = MACH_PORT_MAKE(index, IE_BITS_GEN(bits));
184 fg = fileport_port_to_fileglob(ip_object_to_port(io));
185
186 if (fg) {
187 if (cb && !cb(count, name, fg)) {
188 cb = NULL;
189 if (countp == NULL) {
190 break;
191 }
192 }
193 count++;
194 }
195 }
196
197 index++;
198 entry = ipc_entry_table_next_elem(array: table, e: entry);
199 if (!entry) {
200 break;
201 }
202 if (index % BATCH_SIZE == 0) {
203 /*
204 * Give the system some breathing room,
205 * validate that the space is still valid,
206 * and reload the pointer and length.
207 */
208 is_read_unlock(space);
209 is_read_lock(space);
210 if (!is_active(space)) {
211 is_read_unlock(space);
212 return KERN_INVALID_TASK;
213 }
214
215 table = is_active_table(space);
216 entry = ipc_entry_table_get_nocheck(array: table, i: index);
217 }
218 }
219
220 is_read_unlock(space);
221
222 if (countp) {
223 *countp = count;
224 }
225
226 return KERN_SUCCESS;
227}
228