/* posix_ipc.cc: POSIX IPC API for Cygwin.
- Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
-
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
#include "cygheap.h"
#include "sigproc.h"
#include "ntdll.h"
+#include "tls_pbuf.h"
+#include <io.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <stdlib.h>
#include <mqueue.h>
#include <semaphore.h>
-/* The prefix_len is the length of the path prefix ncluding trailing "/"
+/* The prefix_len is the length of the path prefix including trailing "/"
(or "/sem." for semaphores) as well as the trailing NUL. */
static struct
{
set_errno (EINVAL);
return false;
}
- /* Name must not be empty, or just be a single slash, or start with more
- than one slash. Same for backslash.
- Apart from handling backslash like slash, the naming rules are identical
+ /* Apart from handling backslash like slash, the naming rules are identical
to Linux, including the names and requirements for subdirectories, if
the name contains further slashes. */
- if (!name || (strchr ("/\\", name[0])
- && (!name[1] || strchr ("/\\", name[1]))))
+ /* Name must not be empty and has to start with a slash (or backslash) */
+ if (!name || !strchr ("/\\", name[0]))
{
debug_printf ("Invalid %s name '%s'", ipc_names[type].description, name);
set_errno (EINVAL);
return false;
}
- /* Skip leading (back-)slash. */
- if (strchr ("/\\", name[0]))
- ++name;
- if (len > PATH_MAX - ipc_names[type].prefix_len)
+ /* Name must not consist of just a single slash (or backslash) */
+ if (!name[1])
+ {
+ debug_printf ("Invalid %s name '%s'", ipc_names[type].description, name);
+ set_errno (ENOENT);
+ return false;
+ }
+ /* Name must not contain slashes after the leading one */
+ if (strpbrk (name + 1, "/\\"))
+ {
+ debug_printf ("Invalid %s name '%s'", ipc_names[type].description, name);
+ set_errno (EACCES);
+ return false;
+ }
+ /* Length must be less than or equal to NAME_MAX, or NAME_MAX - 4 in
+ case of semaphores, due to the leading "sem." prefix */
+ if (len > NAME_MAX - (type == semaphore ? strlen ("sem.") : 0))
{
debug_printf ("%s name '%s' too long", ipc_names[type].description, name);
set_errno (ENAMETOOLONG);
}
__small_sprintf (res_name, "%s/%s%s", ipc_names[type].prefix,
type == semaphore ? "sem." : "",
- name);
+ name + 1);
return true;
}
-static int
-ipc_mutex_init (HANDLE *pmtx, const char *name)
-{
- WCHAR buf[MAX_PATH];
- UNICODE_STRING uname;
- OBJECT_ATTRIBUTES attr;
- NTSTATUS status;
-
- __small_swprintf (buf, L"mqueue/mtx_%s", name);
- RtlInitUnicodeString (&uname, buf);
- InitializeObjectAttributes (&attr, &uname,
- OBJ_INHERIT | OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
- get_shared_parent_dir (),
- everyone_sd (CYG_MUTANT_ACCESS));
- status = NtCreateMutant (pmtx, CYG_MUTANT_ACCESS, &attr, FALSE);
- if (!NT_SUCCESS (status))
- {
- debug_printf ("NtCreateMutant: %y", status);
- return geterrno_from_win_error (RtlNtStatusToDosError (status));
- }
- return 0;
-}
-
static int
ipc_mutex_lock (HANDLE mtx, bool eintr)
{
return ReleaseMutex (mtx) ? 0 : geterrno_from_win_error ();
}
-static inline int
-ipc_mutex_close (HANDLE mtx)
-{
- return CloseHandle (mtx) ? 0 : geterrno_from_win_error ();
-}
-
-static int
-ipc_cond_init (HANDLE *pevt, const char *name, char sr)
-{
- WCHAR buf[MAX_PATH];
- UNICODE_STRING uname;
- OBJECT_ATTRIBUTES attr;
- NTSTATUS status;
-
- __small_swprintf (buf, L"mqueue/evt_%s%c", name, sr);
- RtlInitUnicodeString (&uname, buf);
- InitializeObjectAttributes (&attr, &uname,
- OBJ_INHERIT | OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
- get_shared_parent_dir (),
- everyone_sd (CYG_EVENT_ACCESS));
- status = NtCreateEvent (pevt, CYG_EVENT_ACCESS, &attr,
- NotificationEvent, FALSE);
- if (!NT_SUCCESS (status))
- {
- debug_printf ("NtCreateEvent: %y", status);
- return geterrno_from_win_error (RtlNtStatusToDosError (status));
- }
- return 0;
-}
-
static int
ipc_cond_timedwait (HANDLE evt, HANDLE mtx, const struct timespec *abstime)
{
DWORD timer_idx = 0;
int ret = 0;
- set_signal_arrived here (w4[1]);
+ wait_signal_arrived here (w4[1]);
if ((w4[cnt] = pthread::get_cancel_event ()) != NULL)
++cnt;
if (abstime)
{
- if (abstime->tv_sec < 0
- || abstime->tv_nsec < 0
- || abstime->tv_nsec > 999999999)
+ if (!valid_timespec (*abstime))
return EINVAL;
/* If a timeout is set, we create a waitable timer to wait for.
case WAIT_OBJECT_0 + 2:
if (timer_idx != 2)
pthread::static_cancel_self ();
- /*FALLTHRU*/
+ fallthrough;
case WAIT_OBJECT_0 + 3:
ret = ETIMEDOUT;
break;
case WAIT_OBJECT_0 + 2:
if (timer_idx != 2)
pthread_testcancel ();
- /*FALLTHRU*/
+ fallthrough;
case WAIT_OBJECT_0 + 3:
ret = ETIMEDOUT;
break;
SetEvent (evt);
}
-static inline void
-ipc_cond_close (HANDLE evt)
-{
- CloseHandle (evt);
-}
-
class ipc_flock
{
struct flock fl;
files are created under /dev/mqueue. mq_timedsend and mq_timedreceive
are implemented additionally. */
-/* The mq_attr structure is defined using long datatypes per POSIX.
- For interoperability reasons between 32 and 64 bit processes, we have
- to make sure to use a unified structure layout in the message queue file.
- That's what the mq_fattr is, the in-file representation of the mq_attr
- struct. */
-#pragma pack (push, 4)
-struct mq_fattr
-{
- uint32_t mq_flags;
- uint32_t mq_maxmsg;
- uint32_t mq_msgsize;
- uint32_t mq_curmsgs;
-};
-
-struct mq_hdr
-{
- struct mq_fattr mqh_attr; /* the queue's attributes */
- int32_t mqh_head; /* index of first message */
- int32_t mqh_free; /* index of first free message */
- int32_t mqh_nwait; /* #threads blocked in mq_receive() */
- pid_t mqh_pid; /* nonzero PID if mqh_event set */
- char mqh_uname[36]; /* unique name used to identify synchronization
- objects connected to this queue */
- union {
- struct sigevent mqh_event; /* for mq_notify() */
- /* Make sure sigevent takes the same space on 32 and 64 bit systems.
- Other than that, it doesn't need to be compatible since only
- one process can be notified at a time. */
- uint64_t mqh_placeholder[8];
- };
- uint32_t mqh_magic; /* Expect MQI_MAGIC here, otherwise it's
- an old-style message queue. */
-};
-
-struct msg_hdr
-{
- int32_t msg_next; /* index of next on linked list */
- int32_t msg_len; /* actual length */
- unsigned int msg_prio; /* priority */
-};
-#pragma pack (pop)
-
-struct mq_info
-{
- struct mq_hdr *mqi_hdr; /* start of mmap'ed region */
- uint32_t mqi_magic; /* magic number if open */
- int mqi_flags; /* flags for this process */
- HANDLE mqi_lock; /* mutex lock */
- HANDLE mqi_waitsend; /* and condition variable for full queue */
- HANDLE mqi_waitrecv; /* and condition variable for empty queue */
-};
-
-#define MQI_MAGIC 0x98765432UL
-
-#define MSGSIZE(i) roundup((i), sizeof(long))
-
-#define MAX_TRIES 10 /* for waiting for initialization */
-
-struct mq_attr defattr = { 0, 10, 8192, 0 }; /* Linux defaults. */
-
-extern "C" off_t lseek64 (int, off_t, int);
-extern "C" void *mmap64 (void *, size_t, int, int, int, off_t);
-
extern "C" mqd_t
mq_open (const char *name, int oflag, ...)
{
- int i, fd = -1, nonblock, created = 0;
- long msgsize, index;
- off_t filesize = 0;
va_list ap;
- mode_t mode;
- int8_t *mptr = (int8_t *) MAP_FAILED;
- struct stat statbuff;
- struct mq_hdr *mqhdr;
- struct msg_hdr *msghdr;
- struct mq_attr *attr;
- struct mq_info *mqinfo = NULL;
- LUID luid;
+ mode_t mode = 0;
+ fhandler_mqueue *fh = NULL;
+ struct mq_attr *attr = NULL;
size_t len = strlen (name);
char mqname[ipc_names[mqueue].prefix_len + len];
__try
{
- oflag &= (O_CREAT | O_EXCL | O_NONBLOCK);
- nonblock = oflag & O_NONBLOCK;
- oflag &= ~O_NONBLOCK;
-
- again:
if (oflag & O_CREAT)
{
va_start (ap, oflag); /* init ap to final named argument */
mode = va_arg (ap, mode_t) & ~S_IXUSR;
attr = va_arg (ap, struct mq_attr *);
va_end (ap);
-
- /* Open and specify O_EXCL and user-execute */
- fd = open (mqname, oflag | O_EXCL | O_RDWR | O_CLOEXEC,
- mode | S_IXUSR);
- if (fd < 0)
- {
- if (errno == EEXIST && (oflag & O_EXCL) == 0)
- goto exists; /* already exists, OK */
- return (mqd_t) -1;
- }
- created = 1;
- /* First one to create the file initializes it */
- 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. However,
- these max values make sure that the internal mq_fattr structure
- can use 32 bit types. */
- else if (attr->mq_maxmsg <= 0 || attr->mq_maxmsg > 32768
- || attr->mq_msgsize <= 0 || attr->mq_msgsize > 1048576)
- {
- set_errno (EINVAL);
- __leave;
- }
- /* Calculate and set the file size */
- msgsize = MSGSIZE (attr->mq_msgsize);
- filesize = sizeof (struct mq_hdr)
- + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
- if (lseek64 (fd, filesize - 1, SEEK_SET) == -1)
- __leave;
- if (write (fd, "", 1) == -1)
- __leave;
-
- /* Memory map the file */
- mptr = (int8_t *) mmap64 (NULL, (size_t) filesize,
- PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0);
- if (mptr == (int8_t *) MAP_FAILED)
- __leave;
-
- /* Allocate one mq_info{} for the queue */
- if (!(mqinfo = (struct mq_info *)
- calloc (1, sizeof (struct mq_info))))
- __leave;
- mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
- mqinfo->mqi_magic = MQI_MAGIC;
- mqinfo->mqi_flags = nonblock;
-
- /* Initialize header at beginning of file */
- /* Create free list with all messages on it */
- 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;
- NtAllocateLocallyUniqueId (&luid);
- __small_sprintf (mqhdr->mqh_uname, "%016X%08x%08x",
- hash_path_name (0,mqname),
- luid.HighPart, luid.LowPart);
- mqhdr->mqh_head = 0;
- mqhdr->mqh_magic = MQI_MAGIC;
- index = sizeof (struct mq_hdr);
- mqhdr->mqh_free = index;
- for (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 */
-
- /* Initialize mutex & condition variables */
- i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
- if (i != 0)
- {
- set_errno (i);
- __leave;
- }
- i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S');
- if (i != 0)
- {
- set_errno (i);
- __leave;
- }
- i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R');
- if (i != 0)
- {
- set_errno (i);
- __leave;
- }
- /* Initialization complete, turn off user-execute bit */
- if (fchmod (fd, mode) == -1)
- __leave;
- close (fd);
- return ((mqd_t) mqinfo);
}
- exists:
- /* Open the file then memory map */
- if ((fd = open (mqname, O_RDWR | O_CLOEXEC)) < 0)
- {
- if (errno == ENOENT && (oflag & O_CREAT))
- goto again;
- __leave;
- }
- /* Make certain initialization is complete */
- for (i = 0; i < MAX_TRIES; i++)
- {
- if (stat64 (mqname, &statbuff) == -1)
- {
- if (errno == ENOENT && (oflag & O_CREAT))
- {
- close (fd);
- fd = -1;
- goto again;
- }
- __leave;
- }
- if ((statbuff.st_mode & S_IXUSR) == 0)
- break;
- sleep (1);
- }
- if (i == MAX_TRIES)
- {
- set_errno (ETIMEDOUT);
- __leave;
- }
+ /* Create file descriptor for mqueue */
+ cygheap_fdnew fd;
- filesize = statbuff.st_size;
- mptr = (int8_t *) mmap64 (NULL, (size_t) filesize, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0);
- if (mptr == (int8_t *) MAP_FAILED)
+ if (fd < 0)
__leave;
- close (fd);
- fd = -1;
-
- /* Allocate one mq_info{} for each open */
- if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info))))
+ fh = (fhandler_mqueue *) build_fh_name (mqname,
+ PC_OPEN | PC_POSIX
+ | PC_SYM_NOFOLLOW | PC_NULLEMPTY,
+ NULL);
+ if (!fh)
__leave;
- mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
- if (mqhdr->mqh_magic != MQI_MAGIC)
- {
- system_printf (
- "Old message queue \"%s\" detected!\n"
- "This file is not usable as message queue anymore due to changes in the "
- "internal file layout. Please remove the file and try again.", mqname);
- set_errno (EACCES);
- __leave;
- }
- mqinfo->mqi_magic = MQI_MAGIC;
- mqinfo->mqi_flags = nonblock;
- /* Initialize mutex & condition variable */
- i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
- if (i != 0)
- {
- set_errno (i);
- __leave;
- }
- i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S');
- if (i != 0)
+ if (fh->mq_open (oflag, mode, attr))
{
- set_errno (i);
- __leave;
- }
- i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R');
- if (i != 0)
- {
- set_errno (i);
- __leave;
+ fd = fh;
+ return (mqd_t) fd;
}
- return (mqd_t) mqinfo;
}
__except (EFAULT) {}
__endtry
- /* Don't let following function calls change errno */
- save_errno save;
- if (created)
- unlink (mqname);
- if (mptr != (int8_t *) MAP_FAILED)
- munmap((void *) mptr, (size_t) filesize);
- if (mqinfo)
+ if (fh)
+ delete fh;
+ return (mqd_t) -1;
+}
+
+static struct mq_info *
+get_mqinfo (cygheap_fdget &fd)
+{
+ if (fd >= 0)
{
- if (mqinfo->mqi_lock)
- ipc_mutex_close (mqinfo->mqi_lock);
- if (mqinfo->mqi_waitsend)
- ipc_cond_close (mqinfo->mqi_waitsend);
- if (mqinfo->mqi_waitrecv)
- ipc_cond_close (mqinfo->mqi_waitrecv);
- free (mqinfo);
+ fhandler_mqueue *fh = fd->is_mqueue ();
+ if (fh)
+ return fh->mqinfo ();
+ set_errno (EINVAL);
}
- if (fd >= 0)
- close (fd);
- return (mqd_t) -1;
+ return NULL;
}
extern "C" int
__try
{
- mqinfo = (struct mq_info *) mqd;
+ cygheap_fdget fd ((int) mqd, true);
+ mqinfo = get_mqinfo (fd);
if (mqinfo->mqi_magic != MQI_MAGIC)
{
set_errno (EBADF);
__try
{
- mqinfo = (struct mq_info *) mqd;
+ cygheap_fdget fd ((int) mqd, true);
+ mqinfo = get_mqinfo (fd);
if (mqinfo->mqi_magic != MQI_MAGIC)
{
set_errno (EBADF);
__try
{
- mqinfo = (struct mq_info *) mqd;
+ cygheap_fdget fd ((int) mqd, true);
+ mqinfo = get_mqinfo (fd);
if (mqinfo->mqi_magic != MQI_MAGIC)
{
set_errno (EBADF);
__try
{
- mqinfo = (struct mq_info *) mqd;
+ cygheap_fdget fd ((int) mqd);
+ mqinfo = get_mqinfo (fd);
if (mqinfo->mqi_magic != MQI_MAGIC)
{
set_errno (EBADF);
__leave;
}
- if (prio > MQ_PRIO_MAX)
+ if (prio >= MQ_PRIO_MAX)
{
set_errno (EINVAL);
__leave;
ipc_cond_signal (mqinfo->mqi_waitrecv);
attr->mq_curmsgs++;
- ipc_mutex_unlock (mqinfo->mqi_lock);
ret = 0;
}
__except (EBADF) {}
struct mq_hdr *mqhdr;
struct mq_fattr *attr;
struct msg_hdr *msghdr;
- struct mq_info *mqinfo = (struct mq_info *) mqd;
+ struct mq_info *mqinfo;
bool ipc_mutex_locked = false;
pthread_testcancel ();
__try
{
+ cygheap_fdget fd ((int) mqd);
+ mqinfo = get_mqinfo (fd);
if (mqinfo->mqi_magic != MQI_MAGIC)
{
set_errno (EBADF);
if (attr->mq_curmsgs == attr->mq_maxmsg)
ipc_cond_signal (mqinfo->mqi_waitsend);
attr->mq_curmsgs--;
-
- ipc_mutex_unlock (mqinfo->mqi_lock);
}
__except (EBADF) {}
__endtry
extern "C" int
mq_close (mqd_t mqd)
{
- long msgsize, filesize;
- struct mq_hdr *mqhdr;
- struct mq_fattr *attr;
- struct mq_info *mqinfo;
-
__try
{
- mqinfo = (struct mq_info *) mqd;
- if (mqinfo->mqi_magic != MQI_MAGIC)
+ cygheap_fdget fd ((int) mqd, true);
+ if (!fd->is_mqueue ())
{
set_errno (EBADF);
__leave;
}
- mqhdr = mqinfo->mqi_hdr;
- attr = &mqhdr->mqh_attr;
if (mq_notify (mqd, NULL)) /* unregister calling process */
__leave;
- msgsize = MSGSIZE (attr->mq_msgsize);
- filesize = sizeof (struct mq_hdr)
- + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
- if (munmap (mqinfo->mqi_hdr, filesize) == -1)
- __leave;
-
- mqinfo->mqi_magic = 0; /* just in case */
- ipc_cond_close (mqinfo->mqi_waitsend);
- ipc_cond_close (mqinfo->mqi_waitrecv);
- ipc_mutex_close (mqinfo->mqi_lock);
- free (mqinfo);
+ fd->isclosed (true);
+ fd->close ();
+ fd.release ();
return 0;
}
__except (EBADF) {}
the already existing semaphore class in thread.cc. Using a file backed
solution allows to implement kernel persistent named semaphores. */
+#define MAX_TRIES 10 /* for waiting for initialization */
+
struct sem_finfo
{
unsigned int value;
return SEM_FAILED;
}
+extern "C" off_t lseek64 (int, off_t, int);
+
int
_sem_close (sem_t *sem, bool do_close)
{