]> sourceware.org Git - newlib-cygwin.git/blobdiff - winsup/cygwin/fhandler_mqueue.cc
Cygwin: POSIX msg queues: implement open/mq_open entirely in fhandler
[newlib-cygwin.git] / winsup / cygwin / fhandler_mqueue.cc
index dc10a36717ce113d198a3848342e8d1cbdbf2491..6b0d98d75431c817bb2354278146a8a94935e97f 100644 (file)
@@ -11,60 +11,190 @@ details. */
 #include "path.h"
 #include "fhandler.h"
 #include "dtable.h"
+#include <mqueue.h>
+#include <sys/param.h>
+
+#define MSGSIZE(i)      roundup((i), sizeof(long))
+
+struct mq_attr defattr = { 0, 10, 8192, 0 };   /* Linux defaults. */
 
 fhandler_mqueue::fhandler_mqueue () :
-  fhandler_base ()
+  fhandler_disk_file ()
 {
-  nohandle (true);
   close_on_exec (true);
 }
 
+int
+fhandler_mqueue::open (int flags, mode_t mode)
+{
+  /* FIXME: reopen by handle semantics missing yet */
+  flags &= ~(O_NOCTTY | O_PATH | O_BINARY | O_TEXT);
+  return mq_open (flags, mode, NULL);
+}
+
+int
+fhandler_mqueue::mq_open (int oflags, mode_t mode, struct mq_attr *attr)
+{
+  NTSTATUS status;
+  IO_STATUS_BLOCK io;
+  PUNICODE_STRING mqstream;
+  OBJECT_ATTRIBUTES oa;
+  struct mq_info *mqinfo = NULL;
+  bool created = false;
+
+  if ((oflags & ~(O_ACCMODE | O_CLOEXEC | O_CREAT | O_EXCL | O_NONBLOCK))
+      || (oflags & O_ACCMODE) == O_ACCMODE)
+    {
+      set_errno (EINVAL);
+      return 0;
+    }
+
+  /* attach a stream suffix to the NT filename, thus creating a stream. */
+  mqstream = pc.get_nt_native_path (&ro_u_mq_suffix);
+  pc.get_object_attr (oa, sec_none_nih);
+
+again:
+  if (oflags & O_CREAT)
+    {
+      /* Create and disallow sharing */
+      status = NtCreateFile (&get_handle (),
+                            GENERIC_READ | GENERIC_WRITE | DELETE
+                            | SYNCHRONIZE, &oa, &io, NULL,
+                            FILE_ATTRIBUTE_NORMAL, FILE_SHARE_DELETE,
+                            FILE_CREATE,
+                            FILE_OPEN_FOR_BACKUP_INTENT
+                            | FILE_SYNCHRONOUS_IO_NONALERT,
+                            NULL, 0);
+      if (!NT_SUCCESS (status))
+       {
+         if (status == STATUS_OBJECT_NAME_COLLISION && (oflags & O_EXCL) == 0)
+           goto exists;
+         __seterrno_from_nt_status (status);
+         return 0;
+       }
+      if (pc.has_acls ())
+       set_created_file_access (get_handle (), pc, mode);
+      created = true;
+      goto out;
+    }
+exists:
+  /* Open the file, and loop while detecting a sharing violation. */
+  while (true)
+    {
+      status = NtOpenFile (&get_handle (),
+                          GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+                          &oa, &io, FILE_SHARE_VALID_FLAGS,
+                          FILE_OPEN_FOR_BACKUP_INTENT
+                          | FILE_SYNCHRONOUS_IO_NONALERT);
+      if (NT_SUCCESS (status))
+       break;
+      if (status == STATUS_OBJECT_NAME_NOT_FOUND && (oflags & O_CREAT))
+       goto again;
+      if (status != STATUS_SHARING_VIOLATION)
+       {
+         __seterrno_from_nt_status (status);
+         return -1;
+       }
+      Sleep (100L);
+    }
+out:
+  /* We need the filename without STREAM_SUFFIX later on */
+  mqstream->Length -= ro_u_mq_suffix.Length;
+  mqstream->Buffer[mqstream->Length / sizeof (WCHAR)] = L'\0';
+
+  if (created)
+    {
+      if (attr == NULL)
+       attr = &defattr;
+      /* Check minimum and maximum values.  The max values are pretty much
+        arbitrary, taken from the linux mq_overview man page, up to Linux
+        3.4.  These max values make sure that the internal mq_fattr
+        structure can use 32 bit types. */
+      if (attr->mq_maxmsg <= 0 || attr->mq_maxmsg > 32768
+              || attr->mq_msgsize <= 0 || attr->mq_msgsize > 1048576)
+       set_errno (EINVAL);
+      else
+       mqinfo = mqinfo_create (attr, mode, oflags & O_NONBLOCK);
+    }
+  else
+    mqinfo = mqinfo_open (oflags & O_NONBLOCK);
+  mq_open_finish (mqinfo != NULL, created);
+  return mqinfo ? 1 : 0;
+}
+
 struct mq_info *
-fhandler_mqueue::mqinfo (const char *name, int8_t *mptr, HANDLE sect,
-                        size_t size, mode_t mode, int flags)
+fhandler_mqueue::_mqinfo (SIZE_T filesize, mode_t mode, int flags,
+                         bool just_open)
 {
-  WCHAR buf[MAX_PATH];
+  WCHAR buf[NAME_MAX + sizeof ("mqueue/XXX")];
   UNICODE_STRING uname;
-  OBJECT_ATTRIBUTES attr;
+  OBJECT_ATTRIBUTES oa;
   NTSTATUS status;
+  LARGE_INTEGER fsiz = { QuadPart: (LONGLONG) filesize };
+  PVOID mptr = NULL;
 
-  set_name (name);
-  mqinfo ()->mqi_hdr = (struct mq_hdr *) mptr;
-  mqinfo ()->mqi_sect = sect;
-  mqinfo ()->mqi_sectsize = size;
+  /* Set sectsize prior to using filesize in NtMapViewOfSection.  It will
+     get pagesize aligned, which breaks the next NtMapViewOfSection in fork. */
+  mqinfo ()->mqi_sectsize = filesize;
   mqinfo ()->mqi_mode = mode;
   mqinfo ()->mqi_flags = flags;
 
-  __small_swprintf (buf, L"mqueue/mtx%s", name);
+  __small_swprintf (buf, L"mqueue/mtx%s", get_name ());
   RtlInitUnicodeString (&uname, buf);
-  InitializeObjectAttributes (&attr, &uname, OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
+  InitializeObjectAttributes (&oa, &uname, OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
                               get_shared_parent_dir (),
                               everyone_sd (CYG_MUTANT_ACCESS));
-  status = NtCreateMutant (&mqinfo ()->mqi_lock, CYG_MUTANT_ACCESS, &attr,
+  status = NtCreateMutant (&mqinfo ()->mqi_lock, CYG_MUTANT_ACCESS, &oa,
                           FALSE);
   if (!NT_SUCCESS (status))
     goto err;
 
   wcsncpy (buf + 7, L"snd", 3);
   /* same length, no RtlInitUnicodeString required */
-  InitializeObjectAttributes (&attr, &uname, OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
+  InitializeObjectAttributes (&oa, &uname, OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
                               get_shared_parent_dir (),
                               everyone_sd (CYG_EVENT_ACCESS));
-  status = NtCreateEvent (&mqinfo ()->mqi_waitsend, CYG_EVENT_ACCESS, &attr,
+  status = NtCreateEvent (&mqinfo ()->mqi_waitsend, CYG_EVENT_ACCESS, &oa,
                          NotificationEvent, FALSE);
   if (!NT_SUCCESS (status))
     goto err;
   wcsncpy (buf + 7, L"rcv", 3);
   /* same length, same attributes, no more init required */
-  status = NtCreateEvent (&mqinfo ()->mqi_waitrecv, CYG_EVENT_ACCESS, &attr,
+  status = NtCreateEvent (&mqinfo ()->mqi_waitrecv, CYG_EVENT_ACCESS, &oa,
                          NotificationEvent, FALSE);
   if (!NT_SUCCESS (status))
     goto err;
 
+  InitializeObjectAttributes (&oa, NULL, 0, NULL, NULL);
+  status = NtCreateSection (&mqinfo ()->mqi_sect, SECTION_ALL_ACCESS, &oa,
+                           &fsiz, PAGE_READWRITE, SEC_COMMIT, get_handle ());
+  if (!NT_SUCCESS (status))
+    goto err;
+
+  status = NtMapViewOfSection (mqinfo ()->mqi_sect, NtCurrentProcess (),
+                              &mptr, 0, filesize, NULL, &filesize,
+                              ViewShare, 0, PAGE_READWRITE);
+  if (!NT_SUCCESS (status))
+    goto err;
+
+  mqinfo ()->mqi_hdr = (struct mq_hdr *) mptr;
+
+  /* Special problem on Cygwin.  /dev/mqueue is just a simple dir,
+     so there's a chance normal files are created in there. */
+  if (just_open && mqinfo ()->mqi_hdr->mqh_magic != MQI_MAGIC)
+    {
+      status = STATUS_ACCESS_DENIED;
+      goto err;
+    }
+
   mqinfo ()->mqi_magic = MQI_MAGIC;
   return mqinfo ();
 
 err:
+  if (mqinfo ()->mqi_sect)
+    NtClose (mqinfo ()->mqi_sect);
+  if (mqinfo ()->mqi_waitrecv)
+    NtClose (mqinfo ()->mqi_waitrecv);
   if (mqinfo ()->mqi_waitsend)
     NtClose (mqinfo ()->mqi_waitsend);
   if (mqinfo ()->mqi_lock)
@@ -73,22 +203,152 @@ err:
   return NULL;
 }
 
+struct mq_info *
+fhandler_mqueue::mqinfo_open (int flags)
+{
+  FILE_STANDARD_INFORMATION fsi;
+  IO_STATUS_BLOCK io;
+  NTSTATUS status;
+  mode_t mode;
+
+  fsi.EndOfFile.QuadPart = 0;
+  status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
+                                  FileStandardInformation);
+  if (!NT_SUCCESS (status))
+    {
+      __seterrno_from_nt_status (status);
+      return NULL;
+    }
+  if (get_file_attribute (get_handle (), pc, &mode, NULL, NULL))
+    mode = STD_RBITS | STD_WBITS;
+
+  return _mqinfo (fsi.EndOfFile.QuadPart, mode, flags, true);
+}
+
+struct mq_info *
+fhandler_mqueue::mqinfo_create (struct mq_attr *attr, mode_t mode, int flags)
+{
+  long msgsize;
+  off_t filesize = 0;
+  FILE_END_OF_FILE_INFORMATION feofi;
+  IO_STATUS_BLOCK io;
+  NTSTATUS status;
+  struct mq_info *mqinfo = NULL;
+
+  msgsize = MSGSIZE (attr->mq_msgsize);
+  filesize = sizeof (struct mq_hdr)
+            + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
+  feofi.EndOfFile.QuadPart = filesize;
+  status = NtSetInformationFile (get_handle (), &io, &feofi, sizeof feofi,
+                                FileEndOfFileInformation);
+  if (!NT_SUCCESS (status))
+    {
+      __seterrno_from_nt_status (status);
+      return NULL;
+    }
+
+  mqinfo = _mqinfo (filesize, mode, flags, false);
+
+  if (mqinfo)
+    {
+      /* Initialize header at beginning of file */
+      /* Create free list with all messages on it */
+      int8_t *mptr;
+      struct mq_hdr *mqhdr;
+      struct msg_hdr *msghdr;
+
+      mptr = (int8_t *) mqinfo->mqi_hdr;
+      mqhdr = mqinfo->mqi_hdr;
+      mqhdr->mqh_attr.mq_flags = 0;
+      mqhdr->mqh_attr.mq_maxmsg = attr->mq_maxmsg;
+      mqhdr->mqh_attr.mq_msgsize = attr->mq_msgsize;
+      mqhdr->mqh_attr.mq_curmsgs = 0;
+      mqhdr->mqh_nwait = 0;
+      mqhdr->mqh_pid = 0;
+      mqhdr->mqh_head = 0;
+      mqhdr->mqh_magic = MQI_MAGIC;
+      long index = sizeof (struct mq_hdr);
+      mqhdr->mqh_free = index;
+      for (int i = 0; i < attr->mq_maxmsg - 1; i++)
+       {
+         msghdr = (struct msg_hdr *) &mptr[index];
+         index += sizeof (struct msg_hdr) + msgsize;
+         msghdr->msg_next = index;
+       }
+      msghdr = (struct msg_hdr *) &mptr[index];
+      msghdr->msg_next = 0;         /* end of free list */
+    }
+
+  return mqinfo;
+}
+
+void
+fhandler_mqueue::mq_open_finish (bool success, bool created)
+{
+  NTSTATUS status;
+  HANDLE def_stream;
+  OBJECT_ATTRIBUTES oa;
+  IO_STATUS_BLOCK io;
+
+  if (get_handle ())
+    {
+      /* If we have an open queue stream handle, close it and set it to NULL */
+      HANDLE queue_stream = get_handle ();
+      set_handle (NULL);
+      if (success)
+       {
+         /* In case of success, open the default stream for reading.  This
+            can be used to implement various IO functions without exposing
+            the actual message queue. */
+         pc.get_object_attr (oa, sec_none_nih);
+         status = NtOpenFile (&def_stream, GENERIC_READ | SYNCHRONIZE,
+                              &oa, &io, FILE_SHARE_VALID_FLAGS,
+                              FILE_OPEN_FOR_BACKUP_INTENT
+                              | FILE_SYNCHRONOUS_IO_NONALERT);
+         if (NT_SUCCESS (status))
+           set_handle (def_stream);
+         else /* Note that we don't treat this as an error! */
+           {
+             debug_printf ("Opening default stream failed: status %y", status);
+             nohandle (true);
+           }
+       }
+      else if (created)
+       {
+         /* In case of error at creation time, delete the file */
+         FILE_DISPOSITION_INFORMATION disp = { TRUE };
+
+         NtSetInformationFile (queue_stream, &io, &disp, sizeof disp,
+                               FileDispositionInformation);
+         /* We also have to set the delete disposition on the default stream,
+            otherwise only the queue stream will get deleted */
+         pc.get_object_attr (oa, sec_none_nih);
+         status = NtOpenFile (&def_stream, DELETE, &oa, &io,
+                              FILE_SHARE_VALID_FLAGS,
+                              FILE_OPEN_FOR_BACKUP_INTENT);
+         if (NT_SUCCESS (status))
+           {
+             NtSetInformationFile (def_stream, &io, &disp, sizeof disp,
+                                   FileDispositionInformation);
+             NtClose (def_stream);
+           }
+       }
+      NtClose (queue_stream);
+    }
+}
+
 char *
 fhandler_mqueue::get_proc_fd_name (char *buf)
 {
-  return strcpy (buf, get_name ());
+  return strcpy (buf, strrchr (get_name (), '/'));
 }
 
 int __reg2
 fhandler_mqueue::fstat (struct stat *buf)
 {
-  int ret = fhandler_base::fstat (buf);
+  int ret = fhandler_disk_file::fstat (buf);
   if (!ret)
-    {
-      buf->st_mode = S_IFREG | mqinfo ()->mqi_mode;
-      buf->st_dev = FH_MQUEUE;
-      buf->st_ino = hash_path_name (0, get_name ());
-    }
+    buf->st_dev = FH_MQUEUE;
   return ret;
 }
 
@@ -109,28 +369,35 @@ fhandler_mqueue::fixup_after_fork (HANDLE parent)
       SIZE_T filesize = mqinfo ()->mqi_sectsize;
       NTSTATUS status;
 
-      DuplicateHandle (parent, mqinfo ()->mqi_sect,
-                      GetCurrentProcess (), &mqinfo ()->mqi_sect,
-                      0, FALSE, DUPLICATE_SAME_ACCESS);
+      if (!DuplicateHandle (parent, mqinfo ()->mqi_sect,
+                           GetCurrentProcess (), &mqinfo ()->mqi_sect,
+                           0, FALSE, DUPLICATE_SAME_ACCESS))
+       __leave;
       status = NtMapViewOfSection (mqinfo ()->mqi_sect, NtCurrentProcess (),
                                   &mptr, 0, filesize, NULL, &filesize,
                                   ViewShare, 0, PAGE_READWRITE);
       if (!NT_SUCCESS (status))
-       api_fatal ("Mapping message queue failed in fork\n");
-      else
-       mqinfo ()->mqi_hdr = (struct mq_hdr *) mptr;
-      DuplicateHandle (parent, mqinfo ()->mqi_waitsend,
-                      GetCurrentProcess (), &mqinfo ()->mqi_waitsend,
-                      0, FALSE, DUPLICATE_SAME_ACCESS);
-      DuplicateHandle (parent, mqinfo ()->mqi_waitrecv,
-                      GetCurrentProcess (), &mqinfo ()->mqi_waitrecv,
-                      0, FALSE, DUPLICATE_SAME_ACCESS);
-      DuplicateHandle (parent, mqinfo ()->mqi_lock,
-                      GetCurrentProcess (), &mqinfo ()->mqi_lock,
-                      0, FALSE, DUPLICATE_SAME_ACCESS);
+       api_fatal ("Mapping message queue failed in fork, status 0x%x\n",
+                  status);
+
+      mqinfo ()->mqi_hdr = (struct mq_hdr *) mptr;
+      if (!DuplicateHandle (parent, mqinfo ()->mqi_waitsend,
+                           GetCurrentProcess (), &mqinfo ()->mqi_waitsend,
+                           0, FALSE, DUPLICATE_SAME_ACCESS))
+       __leave;
+      if (!DuplicateHandle (parent, mqinfo ()->mqi_waitrecv,
+                           GetCurrentProcess (), &mqinfo ()->mqi_waitrecv,
+                           0, FALSE, DUPLICATE_SAME_ACCESS))
+       __leave;
+      if (!DuplicateHandle (parent, mqinfo ()->mqi_lock,
+                           GetCurrentProcess (), &mqinfo ()->mqi_lock,
+                           0, FALSE, DUPLICATE_SAME_ACCESS))
+       __leave;
+      return;
     }
   __except (EFAULT) {}
   __endtry
+  api_fatal ("Creating IPC object failed in fork, %E");
 }
 
 int
@@ -146,9 +413,8 @@ fhandler_mqueue::close ()
       NtClose (mqinfo ()->mqi_waitsend);
       NtClose (mqinfo ()->mqi_waitrecv);
       NtClose (mqinfo ()->mqi_lock);
-      ret = 0;
     }
-  __except (EFAULT) {}
+  __except (0) {}
   __endtry
-  return ret;
+  return 0;
 }
This page took 0.03495 seconds and 5 git commands to generate.