]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygwin/fhandler_mqueue.cc
Cygwin: POSIX msg queues: implement open/mq_open entirely in fhandler
[newlib-cygwin.git] / winsup / cygwin / fhandler_mqueue.cc
1 /* fhandler_mqueue.cc: fhandler for POSIX message queue
2
3 This file is part of Cygwin.
4
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
8
9 #include "winsup.h"
10 #include "shared_info.h"
11 #include "path.h"
12 #include "fhandler.h"
13 #include "dtable.h"
14 #include <mqueue.h>
15 #include <sys/param.h>
16
17 #define MSGSIZE(i) roundup((i), sizeof(long))
18
19 struct mq_attr defattr = { 0, 10, 8192, 0 }; /* Linux defaults. */
20
21 fhandler_mqueue::fhandler_mqueue () :
22 fhandler_disk_file ()
23 {
24 close_on_exec (true);
25 }
26
27 int
28 fhandler_mqueue::open (int flags, mode_t mode)
29 {
30 /* FIXME: reopen by handle semantics missing yet */
31 flags &= ~(O_NOCTTY | O_PATH | O_BINARY | O_TEXT);
32 return mq_open (flags, mode, NULL);
33 }
34
35 int
36 fhandler_mqueue::mq_open (int oflags, mode_t mode, struct mq_attr *attr)
37 {
38 NTSTATUS status;
39 IO_STATUS_BLOCK io;
40 PUNICODE_STRING mqstream;
41 OBJECT_ATTRIBUTES oa;
42 struct mq_info *mqinfo = NULL;
43 bool created = false;
44
45 if ((oflags & ~(O_ACCMODE | O_CLOEXEC | O_CREAT | O_EXCL | O_NONBLOCK))
46 || (oflags & O_ACCMODE) == O_ACCMODE)
47 {
48 set_errno (EINVAL);
49 return 0;
50 }
51
52 /* attach a stream suffix to the NT filename, thus creating a stream. */
53 mqstream = pc.get_nt_native_path (&ro_u_mq_suffix);
54 pc.get_object_attr (oa, sec_none_nih);
55
56 again:
57 if (oflags & O_CREAT)
58 {
59 /* Create and disallow sharing */
60 status = NtCreateFile (&get_handle (),
61 GENERIC_READ | GENERIC_WRITE | DELETE
62 | SYNCHRONIZE, &oa, &io, NULL,
63 FILE_ATTRIBUTE_NORMAL, FILE_SHARE_DELETE,
64 FILE_CREATE,
65 FILE_OPEN_FOR_BACKUP_INTENT
66 | FILE_SYNCHRONOUS_IO_NONALERT,
67 NULL, 0);
68 if (!NT_SUCCESS (status))
69 {
70 if (status == STATUS_OBJECT_NAME_COLLISION && (oflags & O_EXCL) == 0)
71 goto exists;
72 __seterrno_from_nt_status (status);
73 return 0;
74 }
75 if (pc.has_acls ())
76 set_created_file_access (get_handle (), pc, mode);
77 created = true;
78 goto out;
79 }
80 exists:
81 /* Open the file, and loop while detecting a sharing violation. */
82 while (true)
83 {
84 status = NtOpenFile (&get_handle (),
85 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
86 &oa, &io, FILE_SHARE_VALID_FLAGS,
87 FILE_OPEN_FOR_BACKUP_INTENT
88 | FILE_SYNCHRONOUS_IO_NONALERT);
89 if (NT_SUCCESS (status))
90 break;
91 if (status == STATUS_OBJECT_NAME_NOT_FOUND && (oflags & O_CREAT))
92 goto again;
93 if (status != STATUS_SHARING_VIOLATION)
94 {
95 __seterrno_from_nt_status (status);
96 return -1;
97 }
98 Sleep (100L);
99 }
100 out:
101 /* We need the filename without STREAM_SUFFIX later on */
102 mqstream->Length -= ro_u_mq_suffix.Length;
103 mqstream->Buffer[mqstream->Length / sizeof (WCHAR)] = L'\0';
104
105 if (created)
106 {
107 if (attr == NULL)
108 attr = &defattr;
109 /* Check minimum and maximum values. The max values are pretty much
110 arbitrary, taken from the linux mq_overview man page, up to Linux
111 3.4. These max values make sure that the internal mq_fattr
112 structure can use 32 bit types. */
113 if (attr->mq_maxmsg <= 0 || attr->mq_maxmsg > 32768
114 || attr->mq_msgsize <= 0 || attr->mq_msgsize > 1048576)
115 set_errno (EINVAL);
116 else
117 mqinfo = mqinfo_create (attr, mode, oflags & O_NONBLOCK);
118 }
119 else
120 mqinfo = mqinfo_open (oflags & O_NONBLOCK);
121 mq_open_finish (mqinfo != NULL, created);
122 return mqinfo ? 1 : 0;
123 }
124
125 struct mq_info *
126 fhandler_mqueue::_mqinfo (SIZE_T filesize, mode_t mode, int flags,
127 bool just_open)
128 {
129 WCHAR buf[NAME_MAX + sizeof ("mqueue/XXX")];
130 UNICODE_STRING uname;
131 OBJECT_ATTRIBUTES oa;
132 NTSTATUS status;
133 LARGE_INTEGER fsiz = { QuadPart: (LONGLONG) filesize };
134 PVOID mptr = NULL;
135
136 /* Set sectsize prior to using filesize in NtMapViewOfSection. It will
137 get pagesize aligned, which breaks the next NtMapViewOfSection in fork. */
138 mqinfo ()->mqi_sectsize = filesize;
139 mqinfo ()->mqi_mode = mode;
140 mqinfo ()->mqi_flags = flags;
141
142 __small_swprintf (buf, L"mqueue/mtx%s", get_name ());
143 RtlInitUnicodeString (&uname, buf);
144 InitializeObjectAttributes (&oa, &uname, OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
145 get_shared_parent_dir (),
146 everyone_sd (CYG_MUTANT_ACCESS));
147 status = NtCreateMutant (&mqinfo ()->mqi_lock, CYG_MUTANT_ACCESS, &oa,
148 FALSE);
149 if (!NT_SUCCESS (status))
150 goto err;
151
152 wcsncpy (buf + 7, L"snd", 3);
153 /* same length, no RtlInitUnicodeString required */
154 InitializeObjectAttributes (&oa, &uname, OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
155 get_shared_parent_dir (),
156 everyone_sd (CYG_EVENT_ACCESS));
157 status = NtCreateEvent (&mqinfo ()->mqi_waitsend, CYG_EVENT_ACCESS, &oa,
158 NotificationEvent, FALSE);
159 if (!NT_SUCCESS (status))
160 goto err;
161 wcsncpy (buf + 7, L"rcv", 3);
162 /* same length, same attributes, no more init required */
163 status = NtCreateEvent (&mqinfo ()->mqi_waitrecv, CYG_EVENT_ACCESS, &oa,
164 NotificationEvent, FALSE);
165 if (!NT_SUCCESS (status))
166 goto err;
167
168 InitializeObjectAttributes (&oa, NULL, 0, NULL, NULL);
169 status = NtCreateSection (&mqinfo ()->mqi_sect, SECTION_ALL_ACCESS, &oa,
170 &fsiz, PAGE_READWRITE, SEC_COMMIT, get_handle ());
171 if (!NT_SUCCESS (status))
172 goto err;
173
174 status = NtMapViewOfSection (mqinfo ()->mqi_sect, NtCurrentProcess (),
175 &mptr, 0, filesize, NULL, &filesize,
176 ViewShare, 0, PAGE_READWRITE);
177 if (!NT_SUCCESS (status))
178 goto err;
179
180 mqinfo ()->mqi_hdr = (struct mq_hdr *) mptr;
181
182 /* Special problem on Cygwin. /dev/mqueue is just a simple dir,
183 so there's a chance normal files are created in there. */
184 if (just_open && mqinfo ()->mqi_hdr->mqh_magic != MQI_MAGIC)
185 {
186 status = STATUS_ACCESS_DENIED;
187 goto err;
188 }
189
190 mqinfo ()->mqi_magic = MQI_MAGIC;
191 return mqinfo ();
192
193 err:
194 if (mqinfo ()->mqi_sect)
195 NtClose (mqinfo ()->mqi_sect);
196 if (mqinfo ()->mqi_waitrecv)
197 NtClose (mqinfo ()->mqi_waitrecv);
198 if (mqinfo ()->mqi_waitsend)
199 NtClose (mqinfo ()->mqi_waitsend);
200 if (mqinfo ()->mqi_lock)
201 NtClose (mqinfo ()->mqi_lock);
202 __seterrno_from_nt_status (status);
203 return NULL;
204 }
205
206 struct mq_info *
207 fhandler_mqueue::mqinfo_open (int flags)
208 {
209 FILE_STANDARD_INFORMATION fsi;
210 IO_STATUS_BLOCK io;
211 NTSTATUS status;
212 mode_t mode;
213
214 fsi.EndOfFile.QuadPart = 0;
215 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
216 FileStandardInformation);
217 if (!NT_SUCCESS (status))
218 {
219 __seterrno_from_nt_status (status);
220 return NULL;
221 }
222 if (get_file_attribute (get_handle (), pc, &mode, NULL, NULL))
223 mode = STD_RBITS | STD_WBITS;
224
225 return _mqinfo (fsi.EndOfFile.QuadPart, mode, flags, true);
226 }
227
228 struct mq_info *
229 fhandler_mqueue::mqinfo_create (struct mq_attr *attr, mode_t mode, int flags)
230 {
231 long msgsize;
232 off_t filesize = 0;
233 FILE_END_OF_FILE_INFORMATION feofi;
234 IO_STATUS_BLOCK io;
235 NTSTATUS status;
236 struct mq_info *mqinfo = NULL;
237
238 msgsize = MSGSIZE (attr->mq_msgsize);
239 filesize = sizeof (struct mq_hdr)
240 + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
241 feofi.EndOfFile.QuadPart = filesize;
242 status = NtSetInformationFile (get_handle (), &io, &feofi, sizeof feofi,
243 FileEndOfFileInformation);
244 if (!NT_SUCCESS (status))
245 {
246 __seterrno_from_nt_status (status);
247 return NULL;
248 }
249
250 mqinfo = _mqinfo (filesize, mode, flags, false);
251
252 if (mqinfo)
253 {
254 /* Initialize header at beginning of file */
255 /* Create free list with all messages on it */
256 int8_t *mptr;
257 struct mq_hdr *mqhdr;
258 struct msg_hdr *msghdr;
259
260 mptr = (int8_t *) mqinfo->mqi_hdr;
261 mqhdr = mqinfo->mqi_hdr;
262 mqhdr->mqh_attr.mq_flags = 0;
263 mqhdr->mqh_attr.mq_maxmsg = attr->mq_maxmsg;
264 mqhdr->mqh_attr.mq_msgsize = attr->mq_msgsize;
265 mqhdr->mqh_attr.mq_curmsgs = 0;
266 mqhdr->mqh_nwait = 0;
267 mqhdr->mqh_pid = 0;
268 mqhdr->mqh_head = 0;
269 mqhdr->mqh_magic = MQI_MAGIC;
270 long index = sizeof (struct mq_hdr);
271 mqhdr->mqh_free = index;
272 for (int i = 0; i < attr->mq_maxmsg - 1; i++)
273 {
274 msghdr = (struct msg_hdr *) &mptr[index];
275 index += sizeof (struct msg_hdr) + msgsize;
276 msghdr->msg_next = index;
277 }
278 msghdr = (struct msg_hdr *) &mptr[index];
279 msghdr->msg_next = 0; /* end of free list */
280 }
281
282 return mqinfo;
283 }
284
285 void
286 fhandler_mqueue::mq_open_finish (bool success, bool created)
287 {
288 NTSTATUS status;
289 HANDLE def_stream;
290 OBJECT_ATTRIBUTES oa;
291 IO_STATUS_BLOCK io;
292
293 if (get_handle ())
294 {
295 /* If we have an open queue stream handle, close it and set it to NULL */
296 HANDLE queue_stream = get_handle ();
297 set_handle (NULL);
298 if (success)
299 {
300 /* In case of success, open the default stream for reading. This
301 can be used to implement various IO functions without exposing
302 the actual message queue. */
303 pc.get_object_attr (oa, sec_none_nih);
304 status = NtOpenFile (&def_stream, GENERIC_READ | SYNCHRONIZE,
305 &oa, &io, FILE_SHARE_VALID_FLAGS,
306 FILE_OPEN_FOR_BACKUP_INTENT
307 | FILE_SYNCHRONOUS_IO_NONALERT);
308 if (NT_SUCCESS (status))
309 set_handle (def_stream);
310 else /* Note that we don't treat this as an error! */
311 {
312 debug_printf ("Opening default stream failed: status %y", status);
313 nohandle (true);
314 }
315 }
316 else if (created)
317 {
318 /* In case of error at creation time, delete the file */
319 FILE_DISPOSITION_INFORMATION disp = { TRUE };
320
321 NtSetInformationFile (queue_stream, &io, &disp, sizeof disp,
322 FileDispositionInformation);
323 /* We also have to set the delete disposition on the default stream,
324 otherwise only the queue stream will get deleted */
325 pc.get_object_attr (oa, sec_none_nih);
326 status = NtOpenFile (&def_stream, DELETE, &oa, &io,
327 FILE_SHARE_VALID_FLAGS,
328 FILE_OPEN_FOR_BACKUP_INTENT);
329 if (NT_SUCCESS (status))
330 {
331 NtSetInformationFile (def_stream, &io, &disp, sizeof disp,
332 FileDispositionInformation);
333 NtClose (def_stream);
334 }
335 }
336 NtClose (queue_stream);
337 }
338 }
339
340 char *
341 fhandler_mqueue::get_proc_fd_name (char *buf)
342 {
343 return strcpy (buf, strrchr (get_name (), '/'));
344 }
345
346 int __reg2
347 fhandler_mqueue::fstat (struct stat *buf)
348 {
349 int ret = fhandler_disk_file::fstat (buf);
350 if (!ret)
351 buf->st_dev = FH_MQUEUE;
352 return ret;
353 }
354
355 int
356 fhandler_mqueue::dup (fhandler_base *child, int flags)
357 {
358 /* FIXME */
359 set_errno (EBADF);
360 return -1;
361 }
362
363 void
364 fhandler_mqueue::fixup_after_fork (HANDLE parent)
365 {
366 __try
367 {
368 PVOID mptr = NULL;
369 SIZE_T filesize = mqinfo ()->mqi_sectsize;
370 NTSTATUS status;
371
372 if (!DuplicateHandle (parent, mqinfo ()->mqi_sect,
373 GetCurrentProcess (), &mqinfo ()->mqi_sect,
374 0, FALSE, DUPLICATE_SAME_ACCESS))
375 __leave;
376 status = NtMapViewOfSection (mqinfo ()->mqi_sect, NtCurrentProcess (),
377 &mptr, 0, filesize, NULL, &filesize,
378 ViewShare, 0, PAGE_READWRITE);
379 if (!NT_SUCCESS (status))
380 api_fatal ("Mapping message queue failed in fork, status 0x%x\n",
381 status);
382
383 mqinfo ()->mqi_hdr = (struct mq_hdr *) mptr;
384 if (!DuplicateHandle (parent, mqinfo ()->mqi_waitsend,
385 GetCurrentProcess (), &mqinfo ()->mqi_waitsend,
386 0, FALSE, DUPLICATE_SAME_ACCESS))
387 __leave;
388 if (!DuplicateHandle (parent, mqinfo ()->mqi_waitrecv,
389 GetCurrentProcess (), &mqinfo ()->mqi_waitrecv,
390 0, FALSE, DUPLICATE_SAME_ACCESS))
391 __leave;
392 if (!DuplicateHandle (parent, mqinfo ()->mqi_lock,
393 GetCurrentProcess (), &mqinfo ()->mqi_lock,
394 0, FALSE, DUPLICATE_SAME_ACCESS))
395 __leave;
396 return;
397 }
398 __except (EFAULT) {}
399 __endtry
400 api_fatal ("Creating IPC object failed in fork, %E");
401 }
402
403 int
404 fhandler_mqueue::close ()
405 {
406 int ret = -1;
407
408 __try
409 {
410 mqinfo ()->mqi_magic = 0; /* just in case */
411 NtUnmapViewOfSection (NtCurrentProcess (), mqinfo ()->mqi_hdr);
412 NtClose (mqinfo ()->mqi_sect);
413 NtClose (mqinfo ()->mqi_waitsend);
414 NtClose (mqinfo ()->mqi_waitrecv);
415 NtClose (mqinfo ()->mqi_lock);
416 }
417 __except (0) {}
418 __endtry
419 return 0;
420 }
This page took 0.054348 seconds and 5 git commands to generate.