OSDN Git Service

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