]>
Commit | Line | Data |
---|---|---|
a6df500f | 1 | /* shm.cc: XSI IPC interface for Cygwin. |
f449bfef | 2 | |
61522196 CV |
3 | Copyright 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2012, 2013 |
4 | Red Hat, Inc. | |
f449bfef | 5 | |
2402700d | 6 | This file is part of Cygwin. |
f449bfef | 7 | |
2402700d CF |
8 | This software is a copyrighted work licensed under the terms of the |
9 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
10 | details. */ | |
f449bfef RC |
11 | |
12 | #include "winsup.h" | |
a6df500f | 13 | #include <sys/queue.h> |
1c001dd2 CS |
14 | #include <unistd.h> |
15 | ||
a6df500f | 16 | #include "pinfo.h" |
1c001dd2 CS |
17 | #include "sigproc.h" |
18 | ||
f449bfef | 19 | #include "cygserver_shm.h" |
893ac8e0 | 20 | #include "cygtls.h" |
03abe23b | 21 | #include "sync.h" |
1e4e6ee8 | 22 | #include "ntdll.h" |
f449bfef | 23 | |
a6df500f CV |
24 | /* |
25 | * client_request_shm Constructors | |
1c001dd2 | 26 | */ |
1c001dd2 | 27 | |
a6df500f CV |
28 | client_request_shm::client_request_shm (int shmid, |
29 | const void *shmaddr, | |
30 | int shmflg) | |
31 | : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) | |
f449bfef | 32 | { |
a6df500f CV |
33 | _parameters.in.shmop = SHMOP_shmat; |
34 | ipc_set_proc_info (_parameters.in.ipcblk); | |
1c001dd2 | 35 | |
a6df500f CV |
36 | _parameters.in.atargs.shmid = shmid; |
37 | _parameters.in.atargs.shmaddr = shmaddr; | |
38 | _parameters.in.atargs.shmflg = shmflg; | |
1c001dd2 | 39 | |
a6df500f | 40 | msglen (sizeof (_parameters.in)); |
f449bfef RC |
41 | } |
42 | ||
a6df500f CV |
43 | client_request_shm::client_request_shm (int shmid, |
44 | int cmd, | |
45 | struct shmid_ds *buf) | |
46 | : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) | |
f449bfef | 47 | { |
a6df500f CV |
48 | _parameters.in.shmop = SHMOP_shmctl; |
49 | ipc_set_proc_info (_parameters.in.ipcblk); | |
1c001dd2 | 50 | |
a6df500f CV |
51 | _parameters.in.ctlargs.shmid = shmid; |
52 | _parameters.in.ctlargs.cmd = cmd; | |
53 | _parameters.in.ctlargs.buf = buf; | |
1c001dd2 | 54 | |
a6df500f | 55 | msglen (sizeof (_parameters.in)); |
f449bfef RC |
56 | } |
57 | ||
a6df500f CV |
58 | client_request_shm::client_request_shm (const void *shmaddr) |
59 | : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) | |
f449bfef | 60 | { |
a6df500f CV |
61 | _parameters.in.shmop = SHMOP_shmdt; |
62 | ipc_set_proc_info (_parameters.in.ipcblk); | |
f449bfef | 63 | |
a6df500f | 64 | _parameters.in.dtargs.shmaddr = shmaddr; |
f449bfef | 65 | |
a6df500f | 66 | msglen (sizeof (_parameters.in)); |
f449bfef RC |
67 | } |
68 | ||
a6df500f CV |
69 | client_request_shm::client_request_shm (key_t key, |
70 | size_t size, | |
71 | int shmflg) | |
72 | : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) | |
67be0adb | 73 | { |
a6df500f CV |
74 | _parameters.in.shmop = SHMOP_shmget; |
75 | ipc_set_proc_info (_parameters.in.ipcblk); | |
67be0adb | 76 | |
a6df500f CV |
77 | _parameters.in.getargs.key = key; |
78 | _parameters.in.getargs.size = size; | |
79 | _parameters.in.getargs.shmflg = shmflg; | |
1c001dd2 | 80 | |
a6df500f CV |
81 | msglen (sizeof (_parameters.in)); |
82 | } | |
1c001dd2 | 83 | |
a6df500f CV |
84 | client_request_shm::client_request_shm (proc *p1) |
85 | : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) | |
86 | { | |
87 | _parameters.in.shmop = SHMOP_shmfork; | |
88 | ipc_set_proc_info (_parameters.in.ipcblk); | |
1c001dd2 | 89 | |
a6df500f CV |
90 | _parameters.in.forkargs = *p1; |
91 | } | |
1c001dd2 | 92 | |
a6df500f CV |
93 | /* List of shmid's with file mapping HANDLE and size, returned by shmget. */ |
94 | struct shm_shmid_list { | |
7d9c458d | 95 | SLIST_ENTRY (shm_shmid_list) ssh_next; |
a6df500f CV |
96 | int shmid; |
97 | vm_object_t hdl; | |
98 | size_t size; | |
03abe23b | 99 | int ref_count; |
a6df500f | 100 | }; |
1c001dd2 | 101 | |
7d9c458d | 102 | static SLIST_HEAD (, shm_shmid_list) ssh_list; |
1c001dd2 | 103 | |
a6df500f CV |
104 | /* List of attached mappings, as returned by shmat. */ |
105 | struct shm_attached_list { | |
7d9c458d | 106 | SLIST_ENTRY (shm_attached_list) sph_next; |
a6df500f | 107 | vm_object_t ptr; |
03abe23b | 108 | shm_shmid_list *parent; |
1e4e6ee8 | 109 | ULONG access; |
a6df500f | 110 | }; |
67be0adb | 111 | |
7d9c458d | 112 | static SLIST_HEAD (, shm_attached_list) sph_list; |
1c001dd2 | 113 | |
03abe23b CV |
114 | static NO_COPY muto shm_guard; |
115 | #define SLIST_LOCK() (shm_guard.init ("shm_guard")->acquire ()) | |
116 | #define SLIST_UNLOCK() (shm_guard.release ()) | |
117 | ||
a6df500f CV |
118 | int __stdcall |
119 | fixup_shms_after_fork () | |
f449bfef | 120 | { |
a6df500f CV |
121 | if (!SLIST_FIRST (&sph_list)) |
122 | return 0; | |
123 | pinfo p (myself->ppid); | |
124 | proc parent = { myself->ppid, p->dwProcessId, p->uid, p->gid }; | |
1c001dd2 | 125 | |
a6df500f CV |
126 | client_request_shm request (&parent); |
127 | if (request.make_request () == -1 || request.retval () == -1) | |
f449bfef | 128 | { |
6b0d86c5 | 129 | syscall_printf ("-1 [%d] = fixup_shms_after_fork ()", request.error_code ()); |
1c001dd2 | 130 | set_errno (request.error_code ()); |
a6df500f CV |
131 | return 0; |
132 | } | |
133 | shm_attached_list *sph_entry; | |
1e4e6ee8 | 134 | /* Reconstruct map from list... */ |
a6df500f CV |
135 | SLIST_FOREACH (sph_entry, &sph_list, sph_next) |
136 | { | |
1e4e6ee8 CV |
137 | NTSTATUS status; |
138 | vm_object_t ptr = sph_entry->ptr; | |
61522196 | 139 | SIZE_T viewsize = sph_entry->parent->size; |
f16706de | 140 | status = NtMapViewOfSection (sph_entry->parent->hdl, NtCurrentProcess (), |
1e4e6ee8 CV |
141 | &ptr, 0, sph_entry->parent->size, NULL, |
142 | &viewsize, ViewShare, 0, sph_entry->access); | |
143 | if (!NT_SUCCESS (status) || ptr != sph_entry->ptr) | |
61522196 | 144 | api_fatal ("fixup_shms_after_fork: NtMapViewOfSection (%p), status %y. Terminating.", |
1e4e6ee8 | 145 | sph_entry->ptr, status); |
f449bfef RC |
146 | } |
147 | return 0; | |
148 | } | |
149 | ||
a6df500f CV |
150 | /* |
151 | * XSI shmaphore API. These are exported by the DLL. | |
152 | */ | |
f449bfef | 153 | |
a6df500f CV |
154 | extern "C" void * |
155 | shmat (int shmid, const void *shmaddr, int shmflg) | |
1c001dd2 | 156 | { |
61522196 | 157 | syscall_printf ("shmat (shmid = %d, shmaddr = %p, shmflg = %y)", |
a6df500f | 158 | shmid, shmaddr, shmflg); |
f449bfef | 159 | |
03abe23b | 160 | SLIST_LOCK (); |
a6df500f CV |
161 | shm_shmid_list *ssh_entry; |
162 | SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next) | |
f449bfef | 163 | { |
a6df500f CV |
164 | if (ssh_entry->shmid == shmid) |
165 | break; | |
f449bfef | 166 | } |
a6df500f | 167 | if (!ssh_entry) |
f449bfef | 168 | { |
5c7b73ed | 169 | /* The shmid is unknown to this process so far. Try to get it from |
e3778517 | 170 | the server if it exists. Use special internal call to shmget, |
5c7b73ed CV |
171 | which interprets the key as a shmid and only returns a valid |
172 | shmid if one exists. Since shmctl inserts a new entry for this | |
173 | shmid into ssh_list automatically, we just have to go through | |
174 | that list again. If that still fails, well, bad luck. */ | |
175 | if (shmid && shmget ((key_t) shmid, 0, IPC_KEY_IS_SHMID) != -1) | |
e3778517 | 176 | { |
5c7b73ed CV |
177 | SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next) |
178 | { | |
179 | if (ssh_entry->shmid == shmid) | |
180 | break; | |
181 | } | |
182 | } | |
183 | if (!ssh_entry) | |
e3778517 | 184 | { |
5c7b73ed CV |
185 | /* Invalid shmid */ |
186 | set_errno (EINVAL); | |
03abe23b | 187 | SLIST_UNLOCK (); |
5c7b73ed CV |
188 | return (void *) -1; |
189 | } | |
f449bfef | 190 | } |
03abe23b CV |
191 | /* Early increment ref counter. This allows further actions to run with |
192 | unlocked lists, because shmdt or shmctl(IPC_RMID) won't delete this | |
193 | ssh_entry. */ | |
194 | ++ssh_entry->ref_count; | |
195 | SLIST_UNLOCK (); | |
196 | ||
a6df500f CV |
197 | vm_object_t attach_va = NULL; |
198 | if (shmaddr) | |
f449bfef | 199 | { |
a6df500f | 200 | if (shmflg & SHM_RND) |
e3778517 | 201 | attach_va = (vm_object_t)((vm_offset_t)shmaddr & ~(SHMLBA-1)); |
a6df500f CV |
202 | else |
203 | attach_va = (vm_object_t)shmaddr; | |
204 | /* Don't even bother to call anything if shmaddr is NULL or | |
e3778517 | 205 | not aligned. */ |
a6df500f CV |
206 | if (!attach_va || (vm_offset_t)attach_va % SHMLBA) |
207 | { | |
208 | set_errno (EINVAL); | |
03abe23b | 209 | --ssh_entry->ref_count; |
83216c72 | 210 | return (void *) -1; |
a6df500f | 211 | } |
f449bfef | 212 | } |
a6df500f CV |
213 | /* Try allocating memory before calling cygserver. */ |
214 | shm_attached_list *sph_entry = new (shm_attached_list); | |
215 | if (!sph_entry) | |
1c001dd2 | 216 | { |
a6df500f | 217 | set_errno (ENOMEM); |
03abe23b | 218 | --ssh_entry->ref_count; |
83216c72 | 219 | return (void *) -1; |
1c001dd2 | 220 | } |
1e4e6ee8 CV |
221 | NTSTATUS status; |
222 | vm_object_t ptr = NULL; | |
61522196 | 223 | SIZE_T viewsize = ssh_entry->size; |
1e4e6ee8 | 224 | ULONG access = (shmflg & SHM_RDONLY) ? PAGE_READONLY : PAGE_READWRITE; |
f16706de | 225 | status = NtMapViewOfSection (ssh_entry->hdl, NtCurrentProcess (), &ptr, 0, |
1e4e6ee8 CV |
226 | ssh_entry->size, NULL, &viewsize, ViewShare, |
227 | MEM_TOP_DOWN, access); | |
228 | if (!NT_SUCCESS (status)) | |
038c71f1 | 229 | { |
1e4e6ee8 | 230 | __seterrno_from_nt_status (status); |
a6df500f | 231 | delete sph_entry; |
03abe23b | 232 | --ssh_entry->ref_count; |
83216c72 | 233 | return (void *) -1; |
038c71f1 | 234 | } |
a6df500f CV |
235 | /* Use returned ptr address as is, so it's stored using the exact value |
236 | in cygserver. */ | |
237 | client_request_shm request (shmid, ptr, shmflg & ~SHM_RND); | |
238 | if (request.make_request () == -1 || request.ptrval () == NULL) | |
038c71f1 | 239 | { |
6b0d86c5 | 240 | syscall_printf ("-1 [%d] = shmat ()", request.error_code ()); |
a6df500f CV |
241 | UnmapViewOfFile (ptr); |
242 | delete sph_entry; | |
243 | set_errno (request.error_code ()); | |
03abe23b | 244 | --ssh_entry->ref_count; |
bd0e3521 | 245 | if (request.error_code () == ENOSYS) |
e3778517 | 246 | raise (SIGSYS); |
83216c72 | 247 | return (void *) -1; |
038c71f1 | 248 | } |
a6df500f | 249 | sph_entry->ptr = ptr; |
03abe23b | 250 | sph_entry->parent = ssh_entry; |
a6df500f | 251 | sph_entry->access = access; |
03abe23b | 252 | SLIST_LOCK (); |
a6df500f | 253 | SLIST_INSERT_HEAD (&sph_list, sph_entry, sph_next); |
03abe23b | 254 | SLIST_UNLOCK (); |
a6df500f | 255 | return ptr; |
e217832c CV |
256 | } |
257 | ||
e217832c | 258 | extern "C" int |
a6df500f | 259 | shmctl (int shmid, int cmd, struct shmid_ds *buf) |
e217832c | 260 | { |
61522196 | 261 | syscall_printf ("shmctl (shmid = %d, cmd = %d, buf = %p)", |
a6df500f | 262 | shmid, cmd, buf); |
893ac8e0 CF |
263 | myfault efault; |
264 | if (efault.faulted (EFAULT)) | |
265 | return -1; | |
a6df500f CV |
266 | client_request_shm request (shmid, cmd, buf); |
267 | if (request.make_request () == -1 || request.retval () == -1) | |
268 | { | |
269 | syscall_printf ("-1 [%d] = shmctl ()", request.error_code ()); | |
270 | set_errno (request.error_code ()); | |
bd0e3521 | 271 | if (request.error_code () == ENOSYS) |
e3778517 | 272 | raise (SIGSYS); |
a6df500f CV |
273 | return -1; |
274 | } | |
275 | if (cmd == IPC_RMID) | |
276 | { | |
03abe23b | 277 | /* Cleanup */ |
a6df500f | 278 | shm_shmid_list *ssh_entry, *ssh_next_entry; |
03abe23b | 279 | SLIST_LOCK (); |
a6df500f | 280 | SLIST_FOREACH_SAFE (ssh_entry, &ssh_list, ssh_next, ssh_next_entry) |
e3778517 | 281 | { |
a6df500f CV |
282 | if (ssh_entry->shmid == shmid) |
283 | { | |
03abe23b CV |
284 | /* Remove this entry from the list and close the handle |
285 | only if it's not in use anymore. */ | |
286 | if (ssh_entry->ref_count <= 0) | |
92beb463 | 287 | { |
03abe23b CV |
288 | SLIST_REMOVE (&ssh_list, ssh_entry, shm_shmid_list, ssh_next); |
289 | CloseHandle (ssh_entry->hdl); | |
290 | delete ssh_entry; | |
92beb463 | 291 | } |
a6df500f CV |
292 | break; |
293 | } | |
294 | } | |
03abe23b | 295 | SLIST_UNLOCK (); |
a6df500f CV |
296 | } |
297 | return request.retval (); | |
e217832c CV |
298 | } |
299 | ||
e217832c | 300 | extern "C" int |
a6df500f | 301 | shmdt (const void *shmaddr) |
e217832c | 302 | { |
6b0d86c5 | 303 | syscall_printf ("shmdt (shmaddr = %p)", shmaddr); |
a6df500f CV |
304 | client_request_shm request (shmaddr); |
305 | if (request.make_request () == -1 || request.retval () == -1) | |
306 | { | |
6b0d86c5 | 307 | syscall_printf ("-1 [%d] = shmdt ()", request.error_code ()); |
a6df500f | 308 | set_errno (request.error_code ()); |
bd0e3521 | 309 | if (request.error_code () == ENOSYS) |
e3778517 | 310 | raise (SIGSYS); |
a6df500f CV |
311 | return -1; |
312 | } | |
313 | shm_attached_list *sph_entry, *sph_next_entry; | |
314 | /* Remove map from list... */ | |
03abe23b | 315 | SLIST_LOCK (); |
a6df500f CV |
316 | SLIST_FOREACH_SAFE (sph_entry, &sph_list, sph_next, sph_next_entry) |
317 | { | |
318 | if (sph_entry->ptr == shmaddr) | |
e3778517 | 319 | { |
a6df500f | 320 | SLIST_REMOVE (&sph_list, sph_entry, shm_attached_list, sph_next); |
03abe23b | 321 | /* ...unmap view... */ |
a6df500f | 322 | UnmapViewOfFile (sph_entry->ptr); |
03abe23b CV |
323 | /* ...and, if this was the last reference to this shared section... */ |
324 | shm_shmid_list *ssh_entry = sph_entry->parent; | |
325 | if (--ssh_entry->ref_count <= 0) | |
326 | { | |
327 | /* ...delete parent entry and close handle. */ | |
328 | SLIST_REMOVE (&ssh_list, ssh_entry, shm_shmid_list, ssh_next); | |
329 | CloseHandle (ssh_entry->hdl); | |
330 | delete ssh_entry; | |
331 | } | |
a6df500f CV |
332 | delete sph_entry; |
333 | break; | |
334 | } | |
335 | } | |
03abe23b | 336 | SLIST_UNLOCK (); |
a6df500f | 337 | return request.retval (); |
e217832c CV |
338 | } |
339 | ||
e217832c | 340 | extern "C" int |
a6df500f | 341 | shmget (key_t key, size_t size, int shmflg) |
e217832c | 342 | { |
61522196 | 343 | syscall_printf ("shmget (key = %U, size = %d, shmflg = %y)", |
a6df500f CV |
344 | key, size, shmflg); |
345 | /* Try allocating memory before calling cygserver. */ | |
346 | shm_shmid_list *ssh_new_entry = new (shm_shmid_list); | |
347 | if (!ssh_new_entry) | |
348 | { | |
349 | set_errno (ENOMEM); | |
350 | return -1; | |
351 | } | |
352 | client_request_shm request (key, size, shmflg); | |
353 | if (request.make_request () == -1 || request.retval () == -1) | |
354 | { | |
6b0d86c5 | 355 | syscall_printf ("-1 [%d] = shmget ()", request.error_code ()); |
a6df500f CV |
356 | delete ssh_new_entry; |
357 | set_errno (request.error_code ()); | |
bd0e3521 | 358 | if (request.error_code () == ENOSYS) |
e3778517 | 359 | raise (SIGSYS); |
a6df500f CV |
360 | return -1; |
361 | } | |
362 | int shmid = request.retval (); /* Shared mem ID */ | |
363 | vm_object_t hdl = request.objval (); /* HANDLE associated with it. */ | |
364 | shm_shmid_list *ssh_entry; | |
03abe23b | 365 | SLIST_LOCK (); |
a6df500f CV |
366 | SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next) |
367 | { | |
368 | if (ssh_entry->shmid == shmid) | |
e3778517 | 369 | { |
a6df500f CV |
370 | /* We already maintain an entry for this shmid. That means, |
371 | the hdl returned by cygserver is a superfluous duplicate | |
372 | of the original hdl maintained by cygserver. We can safely | |
373 | delete it. */ | |
374 | CloseHandle (hdl); | |
375 | delete ssh_new_entry; | |
03abe23b | 376 | SLIST_UNLOCK (); |
a6df500f CV |
377 | return shmid; |
378 | } | |
379 | } | |
380 | /* We arrive here only if shmid is a new one for this process. Add the | |
381 | shmid and hdl value to the list. */ | |
382 | ssh_new_entry->shmid = shmid; | |
383 | ssh_new_entry->hdl = hdl; | |
384 | ssh_new_entry->size = size; | |
03abe23b | 385 | ssh_new_entry->ref_count = 0; |
a6df500f | 386 | SLIST_INSERT_HEAD (&ssh_list, ssh_new_entry, ssh_next); |
03abe23b | 387 | SLIST_UNLOCK (); |
a6df500f | 388 | return shmid; |
e217832c | 389 | } |