1 /* fhandler_mqueue.cc: fhandler for POSIX message queue
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
10 #include "shared_info.h"
15 #include <sys/param.h>
17 #define MSGSIZE(i) roundup((i), sizeof(long))
19 struct mq_attr defattr
= { 0, 10, 8192, 0 }; /* Linux defaults. */
21 fhandler_mqueue::fhandler_mqueue () :
28 fhandler_mqueue::open (int flags
, mode_t mode
)
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
);
36 fhandler_mqueue::mq_open (int oflags
, mode_t mode
, struct mq_attr
*attr
)
40 PUNICODE_STRING mqstream
;
42 struct mq_info
*mqinfo
= NULL
;
45 if ((oflags
& ~(O_ACCMODE
| O_CLOEXEC
| O_CREAT
| O_EXCL
| O_NONBLOCK
))
46 || (oflags
& O_ACCMODE
) == O_ACCMODE
)
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
);
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
,
65 FILE_OPEN_FOR_BACKUP_INTENT
66 | FILE_SYNCHRONOUS_IO_NONALERT
,
68 if (!NT_SUCCESS (status
))
70 if (status
== STATUS_OBJECT_NAME_COLLISION
&& (oflags
& O_EXCL
) == 0)
72 __seterrno_from_nt_status (status
);
76 set_created_file_access (get_handle (), pc
, mode
);
81 /* Open the file, and loop while detecting a sharing violation. */
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
))
91 if (status
== STATUS_OBJECT_NAME_NOT_FOUND
&& (oflags
& O_CREAT
))
93 if (status
!= STATUS_SHARING_VIOLATION
)
95 __seterrno_from_nt_status (status
);
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';
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)
117 mqinfo
= mqinfo_create (attr
, mode
, oflags
& O_NONBLOCK
);
120 mqinfo
= mqinfo_open (oflags
& O_NONBLOCK
);
121 mq_open_finish (mqinfo
!= NULL
, created
);
122 return mqinfo
? 1 : 0;
126 fhandler_mqueue::_mqinfo (SIZE_T filesize
, mode_t mode
, int flags
,
129 WCHAR buf
[NAME_MAX
+ sizeof ("mqueue/XXX")];
130 UNICODE_STRING uname
;
131 OBJECT_ATTRIBUTES oa
;
133 LARGE_INTEGER fsiz
= { QuadPart
: (LONGLONG
) filesize
};
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
;
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
,
149 if (!NT_SUCCESS (status
))
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
))
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
))
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
))
174 status
= NtMapViewOfSection (mqinfo ()->mqi_sect
, NtCurrentProcess (),
175 &mptr
, 0, filesize
, NULL
, &filesize
,
176 ViewShare
, 0, PAGE_READWRITE
);
177 if (!NT_SUCCESS (status
))
180 mqinfo ()->mqi_hdr
= (struct mq_hdr
*) mptr
;
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
)
186 status
= STATUS_ACCESS_DENIED
;
190 mqinfo ()->mqi_magic
= MQI_MAGIC
;
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
);
207 fhandler_mqueue::mqinfo_open (int flags
)
209 FILE_STANDARD_INFORMATION fsi
;
214 fsi
.EndOfFile
.QuadPart
= 0;
215 status
= NtQueryInformationFile (get_handle (), &io
, &fsi
, sizeof fsi
,
216 FileStandardInformation
);
217 if (!NT_SUCCESS (status
))
219 __seterrno_from_nt_status (status
);
222 if (get_file_attribute (get_handle (), pc
, &mode
, NULL
, NULL
))
223 mode
= STD_RBITS
| STD_WBITS
;
225 return _mqinfo (fsi
.EndOfFile
.QuadPart
, mode
, flags
, true);
229 fhandler_mqueue::mqinfo_create (struct mq_attr
*attr
, mode_t mode
, int flags
)
233 FILE_END_OF_FILE_INFORMATION feofi
;
236 struct mq_info
*mqinfo
= NULL
;
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
))
246 __seterrno_from_nt_status (status
);
250 mqinfo
= _mqinfo (filesize
, mode
, flags
, false);
254 /* Initialize header at beginning of file */
255 /* Create free list with all messages on it */
257 struct mq_hdr
*mqhdr
;
258 struct msg_hdr
*msghdr
;
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;
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
++)
274 msghdr
= (struct msg_hdr
*) &mptr
[index
];
275 index
+= sizeof (struct msg_hdr
) + msgsize
;
276 msghdr
->msg_next
= index
;
278 msghdr
= (struct msg_hdr
*) &mptr
[index
];
279 msghdr
->msg_next
= 0; /* end of free list */
286 fhandler_mqueue::mq_open_finish (bool success
, bool created
)
290 OBJECT_ATTRIBUTES oa
;
295 /* If we have an open queue stream handle, close it and set it to NULL */
296 HANDLE queue_stream
= get_handle ();
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! */
312 debug_printf ("Opening default stream failed: status %y", status
);
318 /* In case of error at creation time, delete the file */
319 FILE_DISPOSITION_INFORMATION disp
= { TRUE
};
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
))
331 NtSetInformationFile (def_stream
, &io
, &disp
, sizeof disp
,
332 FileDispositionInformation
);
333 NtClose (def_stream
);
336 NtClose (queue_stream
);
341 fhandler_mqueue::get_proc_fd_name (char *buf
)
343 return strcpy (buf
, strrchr (get_name (), '/'));
347 fhandler_mqueue::fstat (struct stat
*buf
)
349 int ret
= fhandler_disk_file::fstat (buf
);
351 buf
->st_dev
= FH_MQUEUE
;
356 fhandler_mqueue::dup (fhandler_base
*child
, int flags
)
364 fhandler_mqueue::fixup_after_fork (HANDLE parent
)
369 SIZE_T filesize
= mqinfo ()->mqi_sectsize
;
372 if (!DuplicateHandle (parent
, mqinfo ()->mqi_sect
,
373 GetCurrentProcess (), &mqinfo ()->mqi_sect
,
374 0, FALSE
, DUPLICATE_SAME_ACCESS
))
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",
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
))
388 if (!DuplicateHandle (parent
, mqinfo ()->mqi_waitrecv
,
389 GetCurrentProcess (), &mqinfo ()->mqi_waitrecv
,
390 0, FALSE
, DUPLICATE_SAME_ACCESS
))
392 if (!DuplicateHandle (parent
, mqinfo ()->mqi_lock
,
393 GetCurrentProcess (), &mqinfo ()->mqi_lock
,
394 0, FALSE
, DUPLICATE_SAME_ACCESS
))
400 api_fatal ("Creating IPC object failed in fork, %E");
404 fhandler_mqueue::close ()
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
);