Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/fhandler/fifo.cc')
-rw-r--r--winsup/cygwin/fhandler/fifo.cc1862
1 files changed, 1862 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler/fifo.cc b/winsup/cygwin/fhandler/fifo.cc
new file mode 100644
index 000000000..1d3e42908
--- /dev/null
+++ b/winsup/cygwin/fhandler/fifo.cc
@@ -0,0 +1,1862 @@
+/* fhandler_fifo.cc - See fhandler.h for a description of the fhandler classes.
+
+ This file is part of Cygwin.
+
+ This software is a copyrighted work licensed under the terms of the
+ Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+ details. */
+
+#include "winsup.h"
+#include <w32api/winioctl.h>
+#include "miscfuncs.h"
+
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "sigproc.h"
+#include "cygtls.h"
+#include "shared_info.h"
+#include "ntdll.h"
+#include "cygwait.h"
+#include <sys/param.h>
+
+/*
+ Overview:
+
+ FIFOs are implemented via Windows named pipes. The server end of
+ the pipe corresponds to an fhandler_fifo open for reading (a.k.a,
+ a "reader"), and the client end corresponds to an fhandler_fifo
+ open for writing (a.k.a, a "writer").
+
+ The server can have multiple instances. The reader (assuming for
+ the moment that there is only one) creates a pipe instance for
+ each writer that opens. The reader maintains a list of
+ "fifo_client_handler" structures, one for each writer. A
+ fifo_client_handler contains the handle for the pipe server
+ instance and information about the state of the connection with
+ the writer. Access to the list is controlled by a
+ "fifo_client_lock".
+
+ The reader runs a "fifo_reader_thread" that creates new pipe
+ instances as needed and listens for client connections.
+
+ The connection state of a fifo_client_handler has one of the
+ following values, in which order is important:
+
+ fc_unknown
+ fc_error
+ fc_disconnected
+ fc_closing
+ fc_listening
+ fc_connected
+ fc_input_avail
+
+ It can be changed in the following places:
+
+ - It is set to fc_listening when the pipe instance is created.
+
+ - It is set to fc_connected when the fifo_reader_thread detects
+ a connection.
+
+ - It is set to a value reported by the O/S when
+ query_and_set_state is called. This can happen in
+ select.cc:peek_fifo and a couple other places.
+
+ - It is set to fc_disconnected by raw_read when an attempt to
+ read yields STATUS_PIPE_BROKEN.
+
+ - It is set to fc_error in various places when unexpected
+ things happen.
+
+ State changes are always guarded by fifo_client_lock.
+
+ If there are multiple readers open, only one of them, called the
+ "owner", maintains the fifo_client_handler list. The owner is
+ therefore the only reader that can read at any given time. If a
+ different reader wants to read, it has to take ownership and
+ duplicate the fifo_client_handler list.
+
+ A reader that is not an owner also runs a fifo_reader_thread,
+ which is mostly idle. The thread wakes up if that reader might
+ need to take ownership.
+
+ There is a block of named shared memory, accessible to all
+ fhandlers for a given FIFO. It keeps track of the number of open
+ readers and writers; it contains information needed for the owner
+ change process; and it contains some locks to prevent races and
+ deadlocks between the various threads.
+
+ The shared memory is created by the first reader to open the
+ FIFO. It is opened by subsequent readers and by all writers. It
+ is destroyed by Windows when the last handle to it is closed.
+
+ If a handle to it somehow remains open after all processes
+ holding file descriptors to the FIFO have closed, the shared
+ memory can persist and be reused with stale data by the next
+ process that opens the FIFO. So far I've seen this happen only
+ as a result of a bug in the code, but there are some debug_printf
+ statements in fhandler_fifo::open to help detect this if it
+ happens again.
+
+ At this writing, I know of only one application (Midnight
+ Commander when running under tcsh) that *explicitly* opens two
+ readers of a FIFO. But many applications will have multiple
+ readers open via dup/fork/exec.
+*/
+
+
+/* This is only to be used for writers. When reading,
+STATUS_PIPE_EMPTY simply means there's no data to be read. */
+#define STATUS_PIPE_IS_CLOSED(status) \
+ ({ NTSTATUS _s = (status); \
+ _s == STATUS_PIPE_CLOSING \
+ || _s == STATUS_PIPE_BROKEN \
+ || _s == STATUS_PIPE_EMPTY; })
+
+#define STATUS_PIPE_NO_INSTANCE_AVAILABLE(status) \
+ ({ NTSTATUS _s = (status); \
+ _s == STATUS_INSTANCE_NOT_AVAILABLE \
+ || _s == STATUS_PIPE_NOT_AVAILABLE \
+ || _s == STATUS_PIPE_BUSY; })
+
+/* Number of pages reserved for shared_fc_handler. */
+#define SH_FC_HANDLER_PAGES 100
+
+static NO_COPY fifo_reader_id_t null_fr_id = { .winpid = 0, .fh = NULL };
+
+fhandler_fifo::fhandler_fifo ():
+ fhandler_pipe_fifo (),
+ read_ready (NULL), write_ready (NULL), writer_opening (NULL),
+ owner_needed_evt (NULL), owner_found_evt (NULL), update_needed_evt (NULL),
+ cancel_evt (NULL), thr_sync_evt (NULL), pipe_name_buf (NULL),
+ fc_handler (NULL), shandlers (0), nhandlers (0),
+ reader (false), writer (false), duplexer (false),
+ me (null_fr_id), shmem_handle (NULL), shmem (NULL),
+ shared_fc_hdl (NULL), shared_fc_handler (NULL)
+{
+ need_fork_fixup (true);
+}
+
+PUNICODE_STRING
+fhandler_fifo::get_pipe_name ()
+{
+ if (!pipe_name_buf)
+ {
+ pipe_name.Length = CYGWIN_FIFO_PIPE_NAME_LEN * sizeof (WCHAR);
+ pipe_name.MaximumLength = pipe_name.Length + sizeof (WCHAR);
+ pipe_name_buf = (PWCHAR) cmalloc_abort (HEAP_STR,
+ pipe_name.MaximumLength);
+ pipe_name.Buffer = pipe_name_buf;
+ __small_swprintf (pipe_name_buf, L"%S-fifo.%08x.%016X",
+ &cygheap->installation_key, get_dev (), get_ino ());
+ }
+ return &pipe_name;
+}
+
+inline PSECURITY_ATTRIBUTES
+sec_user_cloexec (bool cloexec, PSECURITY_ATTRIBUTES sa, PSID sid)
+{
+ return cloexec ? sec_user_nih (sa, sid) : sec_user (sa, sid);
+}
+
+static HANDLE
+create_event ()
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES attr;
+ HANDLE evt = NULL;
+
+ InitializeObjectAttributes (&attr, NULL, 0, NULL, NULL);
+ status = NtCreateEvent (&evt, EVENT_ALL_ACCESS, &attr,
+ NotificationEvent, FALSE);
+ if (!NT_SUCCESS (status))
+ __seterrno_from_nt_status (status);
+ return evt;
+}
+
+
+static void
+set_pipe_non_blocking (HANDLE ph, bool nonblocking)
+{
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
+ FILE_PIPE_INFORMATION fpi;
+
+ fpi.ReadMode = FILE_PIPE_MESSAGE_MODE;
+ fpi.CompletionMode = nonblocking ? FILE_PIPE_COMPLETE_OPERATION
+ : FILE_PIPE_QUEUE_OPERATION;
+ status = NtSetInformationFile (ph, &io, &fpi, sizeof fpi,
+ FilePipeInformation);
+ if (!NT_SUCCESS (status))
+ debug_printf ("NtSetInformationFile(FilePipeInformation): %y", status);
+}
+
+/* Called when a FIFO is first opened for reading and again each time
+ a new client handler is needed. Each pipe instance is created in
+ blocking mode so that we can easily wait for a connection. After
+ it is connected, it is put in nonblocking mode. */
+HANDLE
+fhandler_fifo::create_pipe_instance ()
+{
+ NTSTATUS status;
+ HANDLE npfsh;
+ HANDLE ph = NULL;
+ ACCESS_MASK access;
+ OBJECT_ATTRIBUTES attr;
+ IO_STATUS_BLOCK io;
+ ULONG hattr;
+ ULONG sharing;
+ ULONG nonblocking = FILE_PIPE_QUEUE_OPERATION;
+ ULONG max_instances = -1;
+ LARGE_INTEGER timeout;
+
+ status = npfs_handle (npfsh);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ return NULL;
+ }
+ access = GENERIC_READ | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES
+ | SYNCHRONIZE;
+ sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ hattr = (openflags & O_CLOEXEC ? 0 : OBJ_INHERIT) | OBJ_CASE_INSENSITIVE;
+ InitializeObjectAttributes (&attr, get_pipe_name (),
+ hattr, npfsh, NULL);
+ timeout.QuadPart = -500000;
+ status = NtCreateNamedPipeFile (&ph, access, &attr, &io, sharing,
+ FILE_OPEN_IF, 0,
+ FILE_PIPE_MESSAGE_TYPE
+ | FILE_PIPE_REJECT_REMOTE_CLIENTS,
+ FILE_PIPE_MESSAGE_MODE,
+ nonblocking, max_instances,
+ DEFAULT_PIPEBUFSIZE, DEFAULT_PIPEBUFSIZE,
+ &timeout);
+ if (!NT_SUCCESS (status))
+ __seterrno_from_nt_status (status);
+ return ph;
+}
+
+/* Connect to a pipe instance. */
+NTSTATUS
+fhandler_fifo::open_pipe (HANDLE& ph)
+{
+ NTSTATUS status;
+ HANDLE npfsh;
+ ACCESS_MASK access;
+ OBJECT_ATTRIBUTES attr;
+ IO_STATUS_BLOCK io;
+ ULONG sharing;
+
+ status = npfs_handle (npfsh);
+ if (!NT_SUCCESS (status))
+ return status;
+ access = GENERIC_WRITE | FILE_READ_ATTRIBUTES | SYNCHRONIZE;
+ InitializeObjectAttributes (&attr, get_pipe_name (),
+ openflags & O_CLOEXEC ? 0 : OBJ_INHERIT,
+ npfsh, NULL);
+ sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ return NtOpenFile (&ph, access, &attr, &io, sharing, 0);
+}
+
+/* Wait up to 100ms for a pipe instance to be available, then connect. */
+NTSTATUS
+fhandler_fifo::wait_open_pipe (HANDLE& ph)
+{
+ HANDLE npfsh;
+ HANDLE evt;
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
+ ULONG pwbuf_size;
+ PFILE_PIPE_WAIT_FOR_BUFFER pwbuf;
+ LONGLONG stamp;
+ LONGLONG orig_timeout = -100 * NS100PERSEC / MSPERSEC; /* 100ms */
+
+ status = npfs_handle (npfsh);
+ if (!NT_SUCCESS (status))
+ return status;
+ if (!(evt = create_event ()))
+ api_fatal ("Can't create event, %E");
+ pwbuf_size
+ = offsetof (FILE_PIPE_WAIT_FOR_BUFFER, Name) + get_pipe_name ()->Length;
+ pwbuf = (PFILE_PIPE_WAIT_FOR_BUFFER) alloca (pwbuf_size);
+ pwbuf->Timeout.QuadPart = orig_timeout;
+ pwbuf->NameLength = get_pipe_name ()->Length;
+ pwbuf->TimeoutSpecified = TRUE;
+ memcpy (pwbuf->Name, get_pipe_name ()->Buffer, get_pipe_name ()->Length);
+ stamp = get_clock (CLOCK_MONOTONIC)->n100secs ();
+ bool retry;
+ do
+ {
+ retry = false;
+ status = NtFsControlFile (npfsh, evt, NULL, NULL, &io, FSCTL_PIPE_WAIT,
+ pwbuf, pwbuf_size, NULL, 0);
+ if (status == STATUS_PENDING)
+ {
+ if (WaitForSingleObject (evt, INFINITE) == WAIT_OBJECT_0)
+ status = io.Status;
+ else
+ api_fatal ("WFSO failed, %E");
+ }
+ if (NT_SUCCESS (status))
+ status = open_pipe (ph);
+ if (STATUS_PIPE_NO_INSTANCE_AVAILABLE (status))
+ {
+ /* Another writer has grabbed the pipe instance. Adjust
+ the timeout and keep waiting if there's time left. */
+ pwbuf->Timeout.QuadPart = orig_timeout
+ + get_clock (CLOCK_MONOTONIC)->n100secs () - stamp;
+ if (pwbuf->Timeout.QuadPart < 0)
+ retry = true;
+ else
+ status = STATUS_IO_TIMEOUT;
+ }
+ }
+ while (retry);
+ NtClose (evt);
+ return status;
+}
+
+/* Always called with fifo_client_lock in place. */
+int
+fhandler_fifo::add_client_handler (bool new_pipe_instance)
+{
+ fifo_client_handler fc;
+
+ if (nhandlers >= shandlers)
+ {
+ void *temp = realloc (fc_handler,
+ (shandlers += 64) * sizeof (fc_handler[0]));
+ if (!temp)
+ {
+ shandlers -= 64;
+ set_errno (ENOMEM);
+ return -1;
+ }
+ fc_handler = (fifo_client_handler *) temp;
+ }
+ if (new_pipe_instance)
+ {
+ HANDLE ph = create_pipe_instance ();
+ if (!ph)
+ return -1;
+ fc.h = ph;
+ fc.set_state (fc_listening);
+ }
+ fc_handler[nhandlers++] = fc;
+ return 0;
+}
+
+/* Always called with fifo_client_lock in place. Delete a
+ client_handler by swapping it with the last one in the list. */
+void
+fhandler_fifo::delete_client_handler (int i)
+{
+ fc_handler[i].close ();
+ if (i < --nhandlers)
+ fc_handler[i] = fc_handler[nhandlers];
+}
+
+/* Delete handlers that we will never read from. Always called with
+ fifo_client_lock in place. */
+void
+fhandler_fifo::cleanup_handlers ()
+{
+ /* Work from the top down to try to avoid copying. */
+ for (int i = nhandlers - 1; i >= 0; --i)
+ if (fc_handler[i].get_state () < fc_connected)
+ delete_client_handler (i);
+}
+
+/* Always called with fifo_client_lock in place. */
+void
+fhandler_fifo::record_connection (fifo_client_handler& fc, bool set,
+ fifo_client_connect_state s)
+{
+ if (set)
+ fc.set_state (s);
+ set_pipe_non_blocking (fc.h, true);
+}
+
+/* Called from fifo_reader_thread_func with owner_lock in place. */
+int
+fhandler_fifo::update_my_handlers ()
+{
+ int ret = 0;
+
+ close_all_handlers ();
+ fifo_reader_id_t prev = get_prev_owner ();
+ if (!prev)
+ {
+ debug_printf ("No previous owner to copy handles from");
+ return 0;
+ }
+ HANDLE prev_proc;
+ if (prev.winpid == me.winpid)
+ prev_proc = GetCurrentProcess ();
+ else
+ prev_proc = OpenProcess (PROCESS_DUP_HANDLE, false, prev.winpid);
+ if (!prev_proc)
+ api_fatal ("Can't open process of previous owner, %E");
+
+ fifo_client_lock ();
+ for (int i = 0; i < get_shared_nhandlers (); i++)
+ {
+ if (add_client_handler (false) < 0)
+ api_fatal ("Can't add client handler, %E");
+ fifo_client_handler &fc = fc_handler[nhandlers - 1];
+ if (!DuplicateHandle (prev_proc, shared_fc_handler[i].h,
+ GetCurrentProcess (), &fc.h, 0,
+ !close_on_exec (), DUPLICATE_SAME_ACCESS))
+ {
+ debug_printf ("Can't duplicate handle of previous owner, %E");
+ __seterrno ();
+ fc.set_state (fc_error);
+ fc.last_read = false;
+ ret = -1;
+ }
+ else
+ {
+ fc.set_state (shared_fc_handler[i].get_state ());
+ fc.last_read = shared_fc_handler[i].last_read;
+ }
+ }
+ fifo_client_unlock ();
+ NtClose (prev_proc);
+ set_prev_owner (null_fr_id);
+ return ret;
+}
+
+/* Always called with fifo_client_lock and owner_lock in place. */
+int
+fhandler_fifo::update_shared_handlers ()
+{
+ cleanup_handlers ();
+ if (nhandlers > get_shared_shandlers ())
+ {
+ if (remap_shared_fc_handler (nhandlers * sizeof (fc_handler[0])) < 0)
+ return -1;
+ }
+ set_shared_nhandlers (nhandlers);
+ memcpy (shared_fc_handler, fc_handler, nhandlers * sizeof (fc_handler[0]));
+ shared_fc_handler_updated (true);
+ set_prev_owner (me);
+ return 0;
+}
+
+static DWORD
+fifo_reader_thread (LPVOID param)
+{
+ fhandler_fifo *fh = (fhandler_fifo *) param;
+ return fh->fifo_reader_thread_func ();
+}
+
+DWORD
+fhandler_fifo::fifo_reader_thread_func ()
+{
+ HANDLE conn_evt;
+
+ if (!(conn_evt = CreateEvent (NULL, false, false, NULL)))
+ api_fatal ("Can't create connection event, %E");
+
+ while (1)
+ {
+ fifo_reader_id_t cur_owner, pending_owner;
+ bool idle = false, take_ownership = false;
+
+ owner_lock ();
+ cur_owner = get_owner ();
+ pending_owner = get_pending_owner ();
+
+ if (pending_owner)
+ {
+ if (pending_owner == me)
+ take_ownership = true;
+ else if (cur_owner != me)
+ idle = true;
+ else
+ {
+ /* I'm the owner but someone else wants to be. Have I
+ already seen and reacted to update_needed_evt? */
+ if (WaitForSingleObject (update_needed_evt, 0) == WAIT_OBJECT_0)
+ {
+ /* No, I haven't. */
+ fifo_client_lock ();
+ if (update_shared_handlers () < 0)
+ api_fatal ("Can't update shared handlers, %E");
+ fifo_client_unlock ();
+ }
+ owner_unlock ();
+ /* Yield to pending owner. */
+ Sleep (1);
+ continue;
+ }
+ }
+ else if (!cur_owner)
+ take_ownership = true;
+ else if (cur_owner != me)
+ idle = true;
+ else
+ /* I'm the owner and there's no pending owner. */
+ goto owner_listen;
+ if (idle)
+ {
+ owner_unlock ();
+ HANDLE w[2] = { owner_needed_evt, cancel_evt };
+ switch (WaitForMultipleObjects (2, w, false, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ continue;
+ case WAIT_OBJECT_0 + 1:
+ goto canceled;
+ default:
+ api_fatal ("WFMO failed, %E");
+ }
+ }
+ else if (take_ownership)
+ {
+ if (!shared_fc_handler_updated ())
+ {
+ owner_unlock ();
+ if (IsEventSignalled (cancel_evt))
+ goto canceled;
+ continue;
+ }
+ else
+ {
+ set_owner (me);
+ set_pending_owner (null_fr_id);
+ if (update_my_handlers () < 0)
+ debug_printf ("error updating my handlers, %E");
+ owner_found ();
+ /* Fall through to owner_listen. */
+ }
+ }
+
+owner_listen:
+ fifo_client_lock ();
+ cleanup_handlers ();
+ if (add_client_handler () < 0)
+ api_fatal ("Can't add a client handler, %E");
+
+ /* Listen for a writer to connect to the new client handler. */
+ fifo_client_handler& fc = fc_handler[nhandlers - 1];
+ fifo_client_unlock ();
+ shared_fc_handler_updated (false);
+ owner_unlock ();
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
+ bool cancel = false;
+ bool update = false;
+
+ status = NtFsControlFile (fc.h, conn_evt, NULL, NULL, &io,
+ FSCTL_PIPE_LISTEN, NULL, 0, NULL, 0);
+ if (status == STATUS_PENDING)
+ {
+ HANDLE w[3] = { conn_evt, update_needed_evt, cancel_evt };
+ switch (WaitForMultipleObjects (3, w, false, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ status = io.Status;
+ debug_printf ("NtFsControlFile STATUS_PENDING, then %y",
+ status);
+ break;
+ case WAIT_OBJECT_0 + 1:
+ status = STATUS_WAIT_1;
+ update = true;
+ break;
+ case WAIT_OBJECT_0 + 2:
+ status = STATUS_THREAD_IS_TERMINATING;
+ cancel = true;
+ update = true;
+ break;
+ default:
+ api_fatal ("WFMO failed, %E");
+ }
+ }
+ else
+ debug_printf ("NtFsControlFile status %y, no STATUS_PENDING",
+ status);
+ HANDLE ph = NULL;
+ NTSTATUS status1;
+
+ fifo_client_lock ();
+ if (fc.get_state () != fc_listening)
+ /* select.cc:peek_fifo has already recorded a connection. */
+ ;
+ else
+ {
+ switch (status)
+ {
+ case STATUS_SUCCESS:
+ case STATUS_PIPE_CONNECTED:
+ record_connection (fc);
+ break;
+ case STATUS_PIPE_CLOSING:
+ debug_printf ("NtFsControlFile got STATUS_PIPE_CLOSING...");
+ /* Maybe a writer already connected, wrote, and closed.
+ Just query the O/S. */
+ fc.query_and_set_state ();
+ debug_printf ("...O/S reports state %d", fc.get_state ());
+ record_connection (fc, false);
+ break;
+ case STATUS_THREAD_IS_TERMINATING:
+ case STATUS_WAIT_1:
+ /* Try to connect a bogus client. Otherwise fc is still
+ listening, and the next connection might not get recorded. */
+ status1 = open_pipe (ph);
+ WaitForSingleObject (conn_evt, INFINITE);
+ if (NT_SUCCESS (status1))
+ /* Bogus cilent connected. */
+ delete_client_handler (nhandlers - 1);
+ else
+ /* Did a real client connect? */
+ switch (io.Status)
+ {
+ case STATUS_SUCCESS:
+ case STATUS_PIPE_CONNECTED:
+ record_connection (fc);
+ break;
+ case STATUS_PIPE_CLOSING:
+ debug_printf ("got STATUS_PIPE_CLOSING when trying to connect bogus client...");
+ fc.query_and_set_state ();
+ debug_printf ("...O/S reports state %d", fc.get_state ());
+ record_connection (fc, false);
+ break;
+ default:
+ debug_printf ("NtFsControlFile status %y after failing to connect bogus client or real client", io.Status);
+ fc.set_state (fc_error);
+ break;
+ }
+ break;
+ default:
+ debug_printf ("NtFsControlFile got unexpected status %y", status);
+ fc.set_state (fc_error);
+ break;
+ }
+ }
+ if (ph)
+ NtClose (ph);
+ if (update)
+ {
+ owner_lock ();
+ if (get_owner () == me && update_shared_handlers () < 0)
+ api_fatal ("Can't update shared handlers, %E");
+ owner_unlock ();
+ }
+ fifo_client_unlock ();
+ if (cancel)
+ goto canceled;
+ }
+canceled:
+ if (conn_evt)
+ NtClose (conn_evt);
+ /* automatically return the cygthread to the cygthread pool */
+ _my_tls._ctinfo->auto_release ();
+ return 0;
+}
+
+/* Return -1 on error and 0 or 1 on success. If ONLY_OPEN is true, we
+ expect the shared memory to exist, and we only try to open it. In
+ this case, we return 0 on success.
+
+ Otherwise, we create the shared memory if it doesn't exist, and we
+ return 1 if it already existed and we successfully open it. */
+int
+fhandler_fifo::create_shmem (bool only_open)
+{
+ HANDLE sect;
+ OBJECT_ATTRIBUTES attr;
+ NTSTATUS status;
+ LARGE_INTEGER size = { .QuadPart = sizeof (fifo_shmem_t) };
+ SIZE_T viewsize = sizeof (fifo_shmem_t);
+ PVOID addr = NULL;
+ UNICODE_STRING uname;
+ WCHAR shmem_name[MAX_PATH];
+ bool already_exists = false;
+
+ __small_swprintf (shmem_name, L"fifo-shmem.%08x.%016X", get_dev (),
+ get_ino ());
+ RtlInitUnicodeString (&uname, shmem_name);
+ InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT,
+ get_shared_parent_dir (), NULL);
+ if (!only_open)
+ {
+ status = NtCreateSection (&sect, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY
+ | SECTION_MAP_READ | SECTION_MAP_WRITE,
+ &attr, &size, PAGE_READWRITE, SEC_COMMIT, NULL);
+ if (status == STATUS_OBJECT_NAME_COLLISION)
+ already_exists = true;
+ }
+ if (only_open || already_exists)
+ status = NtOpenSection (&sect, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY
+ | SECTION_MAP_READ | SECTION_MAP_WRITE, &attr);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ return -1;
+ }
+ status = NtMapViewOfSection (sect, NtCurrentProcess (), &addr, 0, viewsize,
+ NULL, &viewsize, ViewShare, 0, PAGE_READWRITE);
+ if (!NT_SUCCESS (status))
+ {
+ NtClose (sect);
+ __seterrno_from_nt_status (status);
+ return -1;
+ }
+ shmem_handle = sect;
+ shmem = (fifo_shmem_t *) addr;
+ return already_exists ? 1 : 0;
+}
+
+/* shmem_handle must be valid when this is called. */
+int
+fhandler_fifo::reopen_shmem ()
+{
+ NTSTATUS status;
+ SIZE_T viewsize = sizeof (fifo_shmem_t);
+ PVOID addr = NULL;
+
+ status = NtMapViewOfSection (shmem_handle, NtCurrentProcess (), &addr,
+ 0, viewsize, NULL, &viewsize, ViewShare,
+ 0, PAGE_READWRITE);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ return -1;
+ }
+ shmem = (fifo_shmem_t *) addr;
+ return 0;
+}
+
+/* On first creation, map and commit one page of memory. */
+int
+fhandler_fifo::create_shared_fc_handler ()
+{
+ HANDLE sect;
+ OBJECT_ATTRIBUTES attr;
+ NTSTATUS status;
+ LARGE_INTEGER size
+ = { .QuadPart = (LONGLONG) (SH_FC_HANDLER_PAGES * wincap.page_size ()) };
+ SIZE_T viewsize = get_shared_fc_handler_committed () ?: wincap.page_size ();
+ PVOID addr = NULL;
+ UNICODE_STRING uname;
+ WCHAR shared_fc_name[MAX_PATH];
+
+ __small_swprintf (shared_fc_name, L"fifo-shared-fc.%08x.%016X", get_dev (),
+ get_ino ());
+ RtlInitUnicodeString (&uname, shared_fc_name);
+ InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT,
+ get_shared_parent_dir (), NULL);
+ status = NtCreateSection (&sect, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY
+ | SECTION_MAP_READ | SECTION_MAP_WRITE, &attr,
+ &size, PAGE_READWRITE, SEC_RESERVE, NULL);
+ if (status == STATUS_OBJECT_NAME_COLLISION)
+ status = NtOpenSection (&sect, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY
+ | SECTION_MAP_READ | SECTION_MAP_WRITE, &attr);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ return -1;
+ }
+ status = NtMapViewOfSection (sect, NtCurrentProcess (), &addr, 0, viewsize,
+ NULL, &viewsize, ViewShare, 0, PAGE_READWRITE);
+ if (!NT_SUCCESS (status))
+ {
+ NtClose (sect);
+ __seterrno_from_nt_status (status);
+ return -1;
+ }
+ shared_fc_hdl = sect;
+ shared_fc_handler = (fifo_client_handler *) addr;
+ if (!get_shared_fc_handler_committed ())
+ set_shared_fc_handler_committed (viewsize);
+ set_shared_shandlers (viewsize / sizeof (fifo_client_handler));
+ return 0;
+}
+
+/* shared_fc_hdl must be valid when this is called. */
+int
+fhandler_fifo::reopen_shared_fc_handler ()
+{
+ NTSTATUS status;
+ SIZE_T viewsize = get_shared_fc_handler_committed ();
+ PVOID addr = NULL;
+
+ status = NtMapViewOfSection (shared_fc_hdl, NtCurrentProcess (),
+ &addr, 0, viewsize, NULL, &viewsize,
+ ViewShare, 0, PAGE_READWRITE);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ return -1;
+ }
+ shared_fc_handler = (fifo_client_handler *) addr;
+ return 0;
+}
+
+int
+fhandler_fifo::remap_shared_fc_handler (size_t nbytes)
+{
+ NTSTATUS status;
+ SIZE_T viewsize = roundup2 (nbytes, wincap.page_size ());
+ PVOID addr = NULL;
+
+ if (viewsize > SH_FC_HANDLER_PAGES * wincap.page_size ())
+ {
+ set_errno (ENOMEM);
+ return -1;
+ }
+
+ NtUnmapViewOfSection (NtCurrentProcess (), shared_fc_handler);
+ status = NtMapViewOfSection (shared_fc_hdl, NtCurrentProcess (),
+ &addr, 0, viewsize, NULL, &viewsize,
+ ViewShare, 0, PAGE_READWRITE);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ return -1;
+ }
+ shared_fc_handler = (fifo_client_handler *) addr;
+ set_shared_fc_handler_committed (viewsize);
+ set_shared_shandlers (viewsize / sizeof (fc_handler[0]));
+ return 0;
+}
+
+int
+fhandler_fifo::open (int flags, mode_t)
+{
+ int saved_errno = 0, shmem_res = 0;
+
+ if (flags & O_PATH)
+ return open_fs (flags);
+
+ /* Determine what we're doing with this fhandler: reading, writing, both */
+ switch (flags & O_ACCMODE)
+ {
+ case O_RDONLY:
+ reader = true;
+ break;
+ case O_WRONLY:
+ writer = true;
+ break;
+ case O_RDWR:
+ reader = writer = duplexer = true;
+ break;
+ default:
+ set_errno (EINVAL);
+ goto err;
+ }
+
+ debug_only_printf ("reader %d, writer %d, duplexer %d", reader, writer, duplexer);
+ set_flags (flags);
+ if (reader && !duplexer)
+ nohandle (true);
+
+ /* Create control events for this named pipe */
+ char char_sa_buf[1024];
+ LPSECURITY_ATTRIBUTES sa_buf;
+ sa_buf = sec_user_cloexec (flags & O_CLOEXEC, (PSECURITY_ATTRIBUTES) char_sa_buf,
+ cygheap->user.sid());
+
+ char npbuf[MAX_PATH];
+ __small_sprintf (npbuf, "r-event.%08x.%016X", get_dev (), get_ino ());
+ if (!(read_ready = CreateEvent (sa_buf, true, false, npbuf)))
+ {
+ debug_printf ("CreateEvent for %s failed, %E", npbuf);
+ __seterrno ();
+ goto err;
+ }
+ npbuf[0] = 'w';
+ if (!(write_ready = CreateEvent (sa_buf, true, false, npbuf)))
+ {
+ debug_printf ("CreateEvent for %s failed, %E", npbuf);
+ __seterrno ();
+ goto err_close_read_ready;
+ }
+ npbuf[0] = 'o';
+ if (!(writer_opening = CreateEvent (sa_buf, true, false, npbuf)))
+ {
+ debug_printf ("CreateEvent for %s failed, %E", npbuf);
+ __seterrno ();
+ goto err_close_write_ready;
+ }
+
+ /* If we're reading, create the shared memory and the shared
+ fc_handler memory, create some events, start the
+ fifo_reader_thread, signal read_ready, and wait for a writer. */
+ if (reader)
+ {
+ /* Create/open shared memory. */
+ if ((shmem_res = create_shmem ()) < 0)
+ goto err_close_writer_opening;
+ else if (shmem_res == 0)
+ debug_printf ("shmem created");
+ else
+ debug_printf ("shmem existed; ok if we're not the first reader");
+ if (create_shared_fc_handler () < 0)
+ goto err_close_shmem;
+ npbuf[0] = 'n';
+ if (!(owner_needed_evt = CreateEvent (sa_buf, true, false, npbuf)))
+ {
+ debug_printf ("CreateEvent for %s failed, %E", npbuf);
+ __seterrno ();
+ goto err_close_shared_fc_handler;
+ }
+ npbuf[0] = 'f';
+ if (!(owner_found_evt = CreateEvent (sa_buf, true, false, npbuf)))
+ {
+ debug_printf ("CreateEvent for %s failed, %E", npbuf);
+ __seterrno ();
+ goto err_close_owner_needed_evt;
+ }
+ npbuf[0] = 'u';
+ if (!(update_needed_evt = CreateEvent (sa_buf, false, false, npbuf)))
+ {
+ debug_printf ("CreateEvent for %s failed, %E", npbuf);
+ __seterrno ();
+ goto err_close_owner_found_evt;
+ }
+ if (!(cancel_evt = create_event ()))
+ goto err_close_update_needed_evt;
+ if (!(thr_sync_evt = create_event ()))
+ goto err_close_cancel_evt;
+
+ me.winpid = GetCurrentProcessId ();
+ me.fh = this;
+ nreaders_lock ();
+ if (inc_nreaders () == 1)
+ {
+ /* Reinitialize _sh_fc_handler_updated, which starts as 0. */
+ shared_fc_handler_updated (true);
+ set_owner (me);
+ }
+ new cygthread (fifo_reader_thread, this, "fifo_reader", thr_sync_evt);
+ SetEvent (read_ready);
+ nreaders_unlock ();
+
+ /* If we're a duplexer, we need a handle for writing. */
+ if (duplexer)
+ {
+ HANDLE ph = NULL;
+ NTSTATUS status;
+
+ nwriters_lock ();
+ inc_nwriters ();
+ SetEvent (write_ready);
+ nwriters_unlock ();
+
+ while (1)
+ {
+ status = open_pipe (ph);
+ if (NT_SUCCESS (status))
+ {
+ set_handle (ph);
+ set_pipe_non_blocking (ph, flags & O_NONBLOCK);
+ break;
+ }
+ else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
+ {
+ /* The pipe hasn't been created yet. */
+ yield ();
+ continue;
+ }
+ else
+ {
+ __seterrno_from_nt_status (status);
+ nohandle (true);
+ goto err_close_reader;
+ }
+ }
+ }
+ /* Not a duplexer; wait for a writer to connect if we're blocking. */
+ else if (!wait (write_ready))
+ goto err_close_reader;
+ goto success;
+ }
+
+ /* If we're writing, wait for read_ready, connect to the pipe, open
+ the shared memory, and signal write_ready. */
+ if (writer)
+ {
+ NTSTATUS status;
+
+ /* Don't let a reader see EOF at this point. */
+ SetEvent (writer_opening);
+ while (1)
+ {
+ if (!wait (read_ready))
+ {
+ ResetEvent (writer_opening);
+ goto err_close_writer_opening;
+ }
+ status = open_pipe (get_handle ());
+ if (NT_SUCCESS (status))
+ goto writer_shmem;
+ else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
+ {
+ /* The pipe hasn't been created yet or there's no longer
+ a reader open. */
+ yield ();
+ continue;
+ }
+ else if (STATUS_PIPE_NO_INSTANCE_AVAILABLE (status))
+ break;
+ else
+ {
+ debug_printf ("create of writer failed");
+ __seterrno_from_nt_status (status);
+ ResetEvent (writer_opening);
+ goto err_close_writer_opening;
+ }
+ }
+
+ /* We should get here only if the system is heavily loaded
+ and/or many writers are trying to connect simultaneously */
+ while (1)
+ {
+ if (!wait (read_ready))
+ {
+ ResetEvent (writer_opening);
+ goto err_close_writer_opening;
+ }
+ status = wait_open_pipe (get_handle ());
+ if (NT_SUCCESS (status))
+ goto writer_shmem;
+ else if (status == STATUS_IO_TIMEOUT)
+ continue;
+ else
+ {
+ debug_printf ("create of writer failed");
+ __seterrno_from_nt_status (status);
+ ResetEvent (writer_opening);
+ goto err_close_writer_opening;
+ }
+ }
+ }
+writer_shmem:
+ if (create_shmem (true) < 0)
+ goto err_close_writer_opening;
+/* writer_success: */
+ set_pipe_non_blocking (get_handle (), flags & O_NONBLOCK);
+ nwriters_lock ();
+ inc_nwriters ();
+ SetEvent (write_ready);
+ ResetEvent (writer_opening);
+ nwriters_unlock ();
+success:
+ if (!select_sem)
+ {
+ __small_sprintf (npbuf, "semaphore.%08x.%016X", get_dev (), get_ino ());
+ select_sem = CreateSemaphore (sa_buf, 0, INT32_MAX, npbuf);
+ }
+ return 1;
+err_close_reader:
+ saved_errno = get_errno ();
+ close ();
+ set_errno (saved_errno);
+ return 0;
+/* err_close_thr_sync_evt: */
+/* NtClose (thr_sync_evt); */
+err_close_cancel_evt:
+ NtClose (cancel_evt);
+err_close_update_needed_evt:
+ NtClose (update_needed_evt);
+err_close_owner_found_evt:
+ NtClose (owner_found_evt);
+err_close_owner_needed_evt:
+ NtClose (owner_needed_evt);
+err_close_shared_fc_handler:
+ NtUnmapViewOfSection (NtCurrentProcess (), shared_fc_handler);
+ NtClose (shared_fc_hdl);
+err_close_shmem:
+ NtUnmapViewOfSection (NtCurrentProcess (), shmem);
+ NtClose (shmem_handle);
+err_close_writer_opening:
+ NtClose (writer_opening);
+err_close_write_ready:
+ NtClose (write_ready);
+err_close_read_ready:
+ NtClose (read_ready);
+err:
+ if (get_handle ())
+ NtClose (get_handle ());
+ return 0;
+}
+
+off_t
+fhandler_fifo::lseek (off_t offset, int whence)
+{
+ debug_printf ("(%D, %d)", offset, whence);
+ set_errno (ESPIPE);
+ return -1;
+}
+
+bool
+fhandler_fifo::wait (HANDLE h)
+{
+#ifdef DEBUGGING
+ const char *what;
+ if (h == read_ready)
+ what = "reader";
+ else
+ what = "writer";
+#endif
+ /* Set the wait to zero for non-blocking I/O-related events. */
+ DWORD wait = ((h == read_ready || h == write_ready)
+ && get_flags () & O_NONBLOCK) ? 0 : INFINITE;
+
+ debug_only_printf ("waiting for %s", what);
+ /* Wait for the event. Set errno, as appropriate if something goes wrong. */
+ switch (cygwait (h, wait))
+ {
+ case WAIT_OBJECT_0:
+ debug_only_printf ("successfully waited for %s", what);
+ return true;
+ case WAIT_SIGNALED:
+ debug_only_printf ("interrupted by signal while waiting for %s", what);
+ set_errno (EINTR);
+ return false;
+ case WAIT_CANCELED:
+ debug_only_printf ("cancellable interruption while waiting for %s", what);
+ pthread::static_cancel_self (); /* never returns */
+ break;
+ case WAIT_TIMEOUT:
+ if (h == write_ready)
+ {
+ debug_only_printf ("wait timed out waiting for write but will still open reader since non-blocking mode");
+ return true;
+ }
+ else
+ {
+ set_errno (ENXIO);
+ return false;
+ }
+ break;
+ default:
+ debug_only_printf ("unknown error while waiting for %s", what);
+ __seterrno ();
+ return false;
+ }
+}
+
+/* Called from raw_read and select.cc:peek_fifo. */
+int
+fhandler_fifo::take_ownership (DWORD timeout)
+{
+ int ret = 0;
+
+ owner_lock ();
+ if (get_owner () == me)
+ {
+ owner_unlock ();
+ return 0;
+ }
+ set_pending_owner (me);
+ /* Wake up my fifo_reader_thread. */
+ owner_needed ();
+ if (get_owner ())
+ /* Wake up the owner and request an update of the shared fc_handlers. */
+ SetEvent (update_needed_evt);
+ owner_unlock ();
+ /* The reader threads should now do the transfer. */
+ switch (WaitForSingleObject (owner_found_evt, timeout))
+ {
+ case WAIT_OBJECT_0:
+ owner_lock ();
+ if (get_owner () != me)
+ {
+ debug_printf ("owner_found_evt signaled, but I'm not the owner");
+ ret = -1;
+ }
+ owner_unlock ();
+ break;
+ case WAIT_TIMEOUT:
+ debug_printf ("timed out");
+ ret = -1;
+ break;
+ default:
+ debug_printf ("WFSO failed, %E");
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+void
+fhandler_fifo::release_select_sem (const char *from)
+{
+ LONG n_release;
+ if (reader) /* Number of select() call. */
+ n_release = get_obj_handle_count (select_sem)
+ - get_obj_handle_count (read_ready);
+ else /* Number of select() and reader */
+ n_release = get_obj_handle_count (select_sem)
+ - get_obj_handle_count (get_handle ());
+ debug_printf("%s(%s) release %d", from,
+ reader ? "reader" : "writer", n_release);
+ if (n_release)
+ ReleaseSemaphore (select_sem, n_release, NULL);
+}
+
+/* Read from a non-blocking pipe and wait for completion. */
+static NTSTATUS
+nt_read (HANDLE h, HANDLE evt, PIO_STATUS_BLOCK pio, void *in_ptr, size_t& len)
+{
+ NTSTATUS status;
+
+ ResetEvent (evt);
+ status = NtReadFile (h, evt, NULL, NULL, pio, in_ptr, len, NULL, NULL);
+ if (status == STATUS_PENDING)
+ {
+ /* Very short-lived */
+ status = NtWaitForSingleObject (evt, FALSE, NULL);
+ if (NT_SUCCESS (status))
+ status = pio->Status;
+ }
+ return status;
+}
+
+void
+fhandler_fifo::raw_read (void *in_ptr, size_t& len)
+{
+ HANDLE evt;
+
+ if (!len)
+ return;
+
+ if (!(evt = CreateEvent (NULL, false, false, NULL)))
+ {
+ __seterrno ();
+ len = (size_t) -1;
+ return;
+ }
+
+ while (1)
+ {
+ int nconnected = 0;
+
+ /* No one else can take ownership while we hold the reading_lock. */
+ reading_lock ();
+ if (take_ownership (10) < 0)
+ goto maybe_retry;
+
+ fifo_client_lock ();
+ /* Poll the connected clients for input. Make three passes.
+
+ On the first pass, just try to read from the client from
+ which we last read successfully. This should minimize
+ interleaving of writes from different clients.
+
+ On the second pass, just try to read from the clients in the
+ state fc_input_avail. This should be more efficient if
+ select has been called and detected input available.
+
+ On the third pass, try to read from all connected clients. */
+
+ /* First pass. */
+ int j;
+ for (j = 0; j < nhandlers; j++)
+ if (fc_handler[j].last_read)
+ break;
+ if (j < nhandlers && fc_handler[j].get_state () < fc_connected)
+ {
+ fc_handler[j].last_read = false;
+ j = nhandlers;
+ }
+ if (j < nhandlers)
+ {
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
+
+ status = nt_read (fc_handler[j].h, evt, &io, in_ptr, len);
+ switch (status)
+ {
+ case STATUS_SUCCESS:
+ case STATUS_BUFFER_OVERFLOW:
+ if (io.Information > 0)
+ {
+ len = io.Information;
+ goto unlock_out;
+ }
+ break;
+ case STATUS_PIPE_EMPTY:
+ /* Update state in case it's fc_input_avail. */
+ fc_handler[j].set_state (fc_connected);
+ break;
+ case STATUS_PIPE_BROKEN:
+ fc_handler[j].set_state (fc_disconnected);
+ break;
+ default:
+ debug_printf ("nt_read status %y", status);
+ fc_handler[j].set_state (fc_error);
+ break;
+ }
+ }
+
+ /* Second pass. */
+ for (int i = 0; i < nhandlers; i++)
+ if (fc_handler[i].get_state () == fc_input_avail)
+ {
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
+
+ status = nt_read (fc_handler[i].h, evt, &io, in_ptr, len);
+ switch (status)
+ {
+ case STATUS_SUCCESS:
+ case STATUS_BUFFER_OVERFLOW:
+ if (io.Information > 0)
+ {
+ len = io.Information;
+ if (j < nhandlers)
+ fc_handler[j].last_read = false;
+ fc_handler[i].last_read = true;
+ goto unlock_out;
+ }
+ break;
+ case STATUS_PIPE_EMPTY:
+ /* No input available after all. */
+ fc_handler[i].set_state (fc_connected);
+ break;
+ case STATUS_PIPE_BROKEN:
+ fc_handler[i].set_state (fc_disconnected);
+ break;
+ default:
+ debug_printf ("nt_read status %y", status);
+ fc_handler[i].set_state (fc_error);
+ break;
+ }
+ }
+
+ /* Third pass. */
+ for (int i = 0; i < nhandlers; i++)
+ if (fc_handler[i].get_state () >= fc_connected)
+ {
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
+
+ nconnected++;
+ status = nt_read (fc_handler[i].h, evt, &io, in_ptr, len);
+ switch (status)
+ {
+ case STATUS_SUCCESS:
+ case STATUS_BUFFER_OVERFLOW:
+ if (io.Information > 0)
+ {
+ len = io.Information;
+ if (j < nhandlers)
+ fc_handler[j].last_read = false;
+ fc_handler[i].last_read = true;
+ goto unlock_out;
+ }
+ break;
+ case STATUS_PIPE_EMPTY:
+ break;
+ case STATUS_PIPE_BROKEN:
+ fc_handler[i].set_state (fc_disconnected);
+ nconnected--;
+ break;
+ default:
+ debug_printf ("nt_read status %y", status);
+ fc_handler[i].set_state (fc_error);
+ nconnected--;
+ break;
+ }
+ }
+ if (!nconnected && hit_eof ())
+ {
+ len = 0;
+ goto unlock_out;
+ }
+ fifo_client_unlock ();
+maybe_retry:
+ reading_unlock ();
+ if (is_nonblocking ())
+ {
+ set_errno (EAGAIN);
+ len = (size_t) -1;
+ goto out;
+ }
+ else
+ {
+ /* Allow interruption and don't hog the CPU. */
+ DWORD waitret = cygwait (select_sem, 1, cw_cancel | cw_sig_eintr);
+ if (waitret == WAIT_CANCELED)
+ pthread::static_cancel_self ();
+ else if (waitret == WAIT_SIGNALED)
+ {
+ if (_my_tls.call_signal_handler ())
+ continue;
+ else
+ {
+ set_errno (EINTR);
+ len = (size_t) -1;
+ goto out;
+ }
+ }
+ }
+ /* We might have been closed by a signal handler or another thread. */
+ if (isclosed ())
+ {
+ set_errno (EBADF);
+ len = (size_t) -1;
+ goto out;
+ }
+ }
+unlock_out:
+ fifo_client_unlock ();
+ reading_unlock ();
+out:
+ if (select_sem)
+ release_select_sem ("raw_read");
+ CloseHandle (evt);
+}
+
+int
+fhandler_fifo::fstat (struct stat *buf)
+{
+ if (reader || writer || duplexer)
+ {
+ /* fhandler_fifo::open has been called, and O_PATH is not set.
+ We don't want to call fhandler_base::fstat. In the writer
+ and duplexer cases we have a handle, but it's a pipe handle
+ rather than a file handle, so it's not suitable for stat. In
+ the reader case we don't have a handle, but
+ fhandler_base::fstat would call fhandler_base::open, which
+ would modify the flags and status_flags. */
+ fhandler_disk_file fh (pc);
+ fh.get_device () = FH_FS;
+ int res = fh.fstat (buf);
+ buf->st_dev = buf->st_rdev = dev ();
+ buf->st_mode = dev ().mode ();
+ buf->st_size = 0;
+ return res;
+ }
+ return fhandler_base::fstat (buf);
+}
+
+int
+fhandler_fifo::fstatvfs (struct statvfs *sfs)
+{
+ if (get_flags () & O_PATH)
+ /* We already have a handle. */
+ {
+ HANDLE h = get_handle ();
+ if (h)
+ return fstatvfs_by_handle (h, sfs);
+ }
+
+ fhandler_disk_file fh (pc);
+ fh.get_device () = FH_FS;
+ return fh.fstatvfs (sfs);
+}
+
+void
+fhandler_fifo::close_all_handlers ()
+{
+ fifo_client_lock ();
+ for (int i = 0; i < nhandlers; i++)
+ fc_handler[i].close ();
+ nhandlers = 0;
+ fifo_client_unlock ();
+}
+
+/* Return previous state. */
+fifo_client_connect_state
+fifo_client_handler::query_and_set_state ()
+{
+ IO_STATUS_BLOCK io;
+ FILE_PIPE_LOCAL_INFORMATION fpli;
+ NTSTATUS status;
+ fifo_client_connect_state prev_state = get_state ();
+
+ if (!h)
+ {
+ set_state (fc_unknown);
+ goto out;
+ }
+
+ status = NtQueryInformationFile (h, &io, &fpli,
+ sizeof (fpli), FilePipeLocalInformation);
+ if (!NT_SUCCESS (status))
+ {
+ debug_printf ("NtQueryInformationFile status %y", status);
+ set_state (fc_error);
+ }
+ else if (fpli.ReadDataAvailable > 0)
+ set_state (fc_input_avail);
+ else
+ switch (fpli.NamedPipeState)
+ {
+ case FILE_PIPE_DISCONNECTED_STATE:
+ set_state (fc_disconnected);
+ break;
+ case FILE_PIPE_LISTENING_STATE:
+ set_state (fc_listening);
+ break;
+ case FILE_PIPE_CONNECTED_STATE:
+ set_state (fc_connected);
+ break;
+ case FILE_PIPE_CLOSING_STATE:
+ set_state (fc_closing);
+ break;
+ default:
+ set_state (fc_error);
+ break;
+ }
+out:
+ return prev_state;
+}
+
+void
+fhandler_fifo::cancel_reader_thread ()
+{
+ if (cancel_evt)
+ SetEvent (cancel_evt);
+ if (thr_sync_evt)
+ WaitForSingleObject (thr_sync_evt, INFINITE);
+}
+
+int
+fhandler_fifo::close ()
+{
+ if (select_sem)
+ {
+ release_select_sem ("close");
+ NtClose (select_sem);
+ }
+ if (writer)
+ {
+ nwriters_lock ();
+ if (dec_nwriters () == 0)
+ ResetEvent (write_ready);
+ nwriters_unlock ();
+ }
+ if (reader)
+ {
+ /* If we're the owner, we can't close our fc_handlers if a new
+ owner might need to duplicate them. */
+ bool close_fc_ok = false;
+
+ cancel_reader_thread ();
+ nreaders_lock ();
+ if (dec_nreaders () == 0)
+ {
+ close_fc_ok = true;
+ ResetEvent (read_ready);
+ ResetEvent (owner_needed_evt);
+ ResetEvent (owner_found_evt);
+ set_owner (null_fr_id);
+ set_prev_owner (null_fr_id);
+ set_pending_owner (null_fr_id);
+ set_shared_nhandlers (0);
+ }
+ else
+ {
+ owner_lock ();
+ if (get_owner () != me)
+ close_fc_ok = true;
+ else
+ {
+ set_owner (null_fr_id);
+ set_prev_owner (me);
+ if (!get_pending_owner ())
+ owner_needed ();
+ }
+ owner_unlock ();
+ }
+ nreaders_unlock ();
+ while (!close_fc_ok)
+ {
+ if (WaitForSingleObject (owner_found_evt, 1) == WAIT_OBJECT_0)
+ close_fc_ok = true;
+ else
+ {
+ nreaders_lock ();
+ if (!nreaders ())
+ {
+ close_fc_ok = true;
+ nreaders_unlock ();
+ }
+ else
+ {
+ nreaders_unlock ();
+ owner_lock ();
+ if (get_owner () || get_prev_owner () != me)
+ close_fc_ok = true;
+ owner_unlock ();
+ }
+ }
+ }
+ close_all_handlers ();
+ if (fc_handler)
+ free (fc_handler);
+ if (owner_needed_evt)
+ NtClose (owner_needed_evt);
+ if (owner_found_evt)
+ NtClose (owner_found_evt);
+ if (update_needed_evt)
+ NtClose (update_needed_evt);
+ if (cancel_evt)
+ NtClose (cancel_evt);
+ if (thr_sync_evt)
+ NtClose (thr_sync_evt);
+ if (shared_fc_handler)
+ NtUnmapViewOfSection (NtCurrentProcess (), shared_fc_handler);
+ if (shared_fc_hdl)
+ NtClose (shared_fc_hdl);
+ }
+ if (shmem)
+ NtUnmapViewOfSection (NtCurrentProcess (), shmem);
+ if (shmem_handle)
+ NtClose (shmem_handle);
+ if (read_ready)
+ NtClose (read_ready);
+ if (write_ready)
+ NtClose (write_ready);
+ if (writer_opening)
+ NtClose (writer_opening);
+ if (nohandle ())
+ return 0;
+ else
+ return fhandler_base::close ();
+}
+
+/* If we have a write handle (i.e., we're a duplexer or a writer),
+ keep the nonblocking state of the windows pipe in sync with our
+ nonblocking state. */
+int
+fhandler_fifo::fcntl (int cmd, intptr_t arg)
+{
+ if (cmd != F_SETFL || nohandle () || (get_flags () & O_PATH))
+ return fhandler_base::fcntl (cmd, arg);
+
+ const bool was_nonblocking = is_nonblocking ();
+ int res = fhandler_base::fcntl (cmd, arg);
+ const bool now_nonblocking = is_nonblocking ();
+ if (now_nonblocking != was_nonblocking)
+ set_pipe_non_blocking (get_handle (), now_nonblocking);
+ return res;
+}
+
+int
+fhandler_fifo::dup (fhandler_base *child, int flags)
+{
+ fhandler_fifo *fhf = NULL;
+
+ if (get_flags () & O_PATH)
+ return fhandler_base::dup (child, flags);
+
+ if (fhandler_base::dup (child, flags))
+ goto err;
+
+ fhf = (fhandler_fifo *) child;
+ if (!DuplicateHandle (GetCurrentProcess (), read_ready,
+ GetCurrentProcess (), &fhf->read_ready,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ goto err;
+ }
+ if (!DuplicateHandle (GetCurrentProcess (), write_ready,
+ GetCurrentProcess (), &fhf->write_ready,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ goto err_close_read_ready;
+ }
+ if (!DuplicateHandle (GetCurrentProcess (), writer_opening,
+ GetCurrentProcess (), &fhf->writer_opening,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ goto err_close_write_ready;
+ }
+ if (!DuplicateHandle (GetCurrentProcess (), shmem_handle,
+ GetCurrentProcess (), &fhf->shmem_handle,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ goto err_close_writer_opening;
+ }
+ if (fhf->reopen_shmem () < 0)
+ goto err_close_shmem_handle;
+ if (select_sem &&
+ !DuplicateHandle (GetCurrentProcess (), select_sem,
+ GetCurrentProcess (), &fhf->select_sem,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ goto err_close_shmem;
+ }
+ if (reader)
+ {
+ /* Make sure the child starts unlocked. */
+ fhf->fifo_client_unlock ();
+
+ /* Clear fc_handler list; the child never starts as owner. */
+ fhf->nhandlers = fhf->shandlers = 0;
+ fhf->fc_handler = NULL;
+
+ if (!DuplicateHandle (GetCurrentProcess (), shared_fc_hdl,
+ GetCurrentProcess (), &fhf->shared_fc_hdl,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ goto err_close_select_sem;
+ }
+ if (fhf->reopen_shared_fc_handler () < 0)
+ goto err_close_shared_fc_hdl;
+ if (!DuplicateHandle (GetCurrentProcess (), owner_needed_evt,
+ GetCurrentProcess (), &fhf->owner_needed_evt,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ goto err_close_shared_fc_handler;
+ }
+ if (!DuplicateHandle (GetCurrentProcess (), owner_found_evt,
+ GetCurrentProcess (), &fhf->owner_found_evt,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ goto err_close_owner_needed_evt;
+ }
+ if (!DuplicateHandle (GetCurrentProcess (), update_needed_evt,
+ GetCurrentProcess (), &fhf->update_needed_evt,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ goto err_close_owner_found_evt;
+ }
+ if (!(fhf->cancel_evt = create_event ()))
+ goto err_close_update_needed_evt;
+ if (!(fhf->thr_sync_evt = create_event ()))
+ goto err_close_cancel_evt;
+ inc_nreaders ();
+ fhf->me.fh = fhf;
+ new cygthread (fifo_reader_thread, fhf, "fifo_reader", fhf->thr_sync_evt);
+ }
+ if (writer)
+ inc_nwriters ();
+ return 0;
+err_close_cancel_evt:
+ NtClose (fhf->cancel_evt);
+err_close_update_needed_evt:
+ NtClose (fhf->update_needed_evt);
+err_close_owner_found_evt:
+ NtClose (fhf->owner_found_evt);
+err_close_owner_needed_evt:
+ NtClose (fhf->owner_needed_evt);
+err_close_shared_fc_handler:
+ NtUnmapViewOfSection (GetCurrentProcess (), fhf->shared_fc_handler);
+err_close_shared_fc_hdl:
+ NtClose (fhf->shared_fc_hdl);
+err_close_select_sem:
+ NtClose (fhf->select_sem);
+err_close_shmem:
+ NtUnmapViewOfSection (GetCurrentProcess (), fhf->shmem);
+err_close_shmem_handle:
+ NtClose (fhf->shmem_handle);
+err_close_writer_opening:
+ NtClose (fhf->writer_opening);
+err_close_write_ready:
+ NtClose (fhf->write_ready);
+err_close_read_ready:
+ NtClose (fhf->read_ready);
+err:
+ return -1;
+}
+
+void
+fhandler_fifo::fixup_after_fork (HANDLE parent)
+{
+ fhandler_base::fixup_after_fork (parent);
+ fork_fixup (parent, read_ready, "read_ready");
+ fork_fixup (parent, write_ready, "write_ready");
+ fork_fixup (parent, writer_opening, "writer_opening");
+ fork_fixup (parent, shmem_handle, "shmem_handle");
+ if (reopen_shmem () < 0)
+ api_fatal ("Can't reopen shared memory during fork, %E");
+ if (select_sem)
+ fork_fixup (parent, select_sem, "select_sem");
+ if (reader)
+ {
+ /* Make sure the child starts unlocked. */
+ fifo_client_unlock ();
+
+ fork_fixup (parent, shared_fc_hdl, "shared_fc_hdl");
+ if (reopen_shared_fc_handler () < 0)
+ api_fatal ("Can't reopen shared fc_handler memory during fork, %E");
+ fork_fixup (parent, owner_needed_evt, "owner_needed_evt");
+ fork_fixup (parent, owner_found_evt, "owner_found_evt");
+ fork_fixup (parent, update_needed_evt, "update_needed_evt");
+ if (close_on_exec ())
+ /* Prevent a later attempt to close the non-inherited
+ pipe-instance handles copied from the parent. */
+ nhandlers = 0;
+ if (!(cancel_evt = create_event ()))
+ api_fatal ("Can't create reader thread cancel event during fork, %E");
+ if (!(thr_sync_evt = create_event ()))
+ api_fatal ("Can't create reader thread sync event during fork, %E");
+ inc_nreaders ();
+ me.winpid = GetCurrentProcessId ();
+ new cygthread (fifo_reader_thread, this, "fifo_reader", thr_sync_evt);
+ }
+ if (writer)
+ inc_nwriters ();
+}
+
+void
+fhandler_fifo::fixup_after_exec ()
+{
+ fhandler_base::fixup_after_exec ();
+ if (close_on_exec ())
+ return;
+ if (reopen_shmem () < 0)
+ api_fatal ("Can't reopen shared memory during exec, %E");
+ if (reader)
+ {
+ /* Make sure the child starts unlocked. */
+ fifo_client_unlock ();
+
+ if (reopen_shared_fc_handler () < 0)
+ api_fatal ("Can't reopen shared fc_handler memory during exec, %E");
+ fc_handler = NULL;
+ nhandlers = shandlers = 0;
+ if (!(cancel_evt = create_event ()))
+ api_fatal ("Can't create reader thread cancel event during exec, %E");
+ if (!(thr_sync_evt = create_event ()))
+ api_fatal ("Can't create reader thread sync event during exec, %E");
+ /* At this moment we're a new reader. The count will be
+ decremented when the parent closes. */
+ inc_nreaders ();
+ me.winpid = GetCurrentProcessId ();
+ new cygthread (fifo_reader_thread, this, "fifo_reader", thr_sync_evt);
+ }
+ if (writer)
+ inc_nwriters ();
+}
+
+void
+fhandler_fifo::set_close_on_exec (bool val)
+{
+ fhandler_base::set_close_on_exec (val);
+ set_no_inheritance (read_ready, val);
+ set_no_inheritance (write_ready, val);
+ set_no_inheritance (writer_opening, val);
+ if (reader)
+ {
+ set_no_inheritance (owner_needed_evt, val);
+ set_no_inheritance (owner_found_evt, val);
+ set_no_inheritance (update_needed_evt, val);
+ fifo_client_lock ();
+ for (int i = 0; i < nhandlers; i++)
+ set_no_inheritance (fc_handler[i].h, val);
+ fifo_client_unlock ();
+ }
+ if (select_sem)
+ set_no_inheritance (select_sem, val);
+}